From b1853d9129e884c032de68a282df5345bb939580 Mon Sep 17 00:00:00 2001 From: Pablo Lagos Date: Mon, 15 Nov 2021 12:34:57 -0300 Subject: [PATCH 1/4] Version 1.2.1 --- capi.go | 4 +- gen_helper.go | 7 +- internal/libwebp-1.2.1/.gitattributes | 6 + internal/libwebp-1.2.1/.gitignore | 54 + internal/libwebp-1.2.1/.mailmap | 14 + internal/libwebp-1.2.1/.pylintrc | 441 ++ internal/libwebp-1.2.1/.style.yapf | 2 + internal/libwebp-1.2.1/AUTHORS | 51 + internal/libwebp-1.2.1/Android.mk | 281 + internal/libwebp-1.2.1/CMakeLists.txt | 732 +++ internal/libwebp-1.2.1/CONTRIBUTING.md | 29 + internal/libwebp-1.2.1/COPYING | 30 + internal/libwebp-1.2.1/ChangeLog | 4231 +++++++++++++ internal/libwebp-1.2.1/Makefile.am | 9 + internal/libwebp-1.2.1/Makefile.vc | 523 ++ internal/libwebp-1.2.1/NEWS | 266 + internal/libwebp-1.2.1/PATENTS | 23 + internal/libwebp-1.2.1/PRESUBMIT.py | 183 + internal/libwebp-1.2.1/README | 795 +++ internal/libwebp-1.2.1/README.mux | 258 + internal/libwebp-1.2.1/README.webp_js | 75 + internal/libwebp-1.2.1/autogen.sh | 2 + internal/libwebp-1.2.1/build.gradle | 435 ++ .../libwebp-1.2.1/cmake/WebPConfig.cmake.in | 11 + internal/libwebp-1.2.1/cmake/config.h.in | 149 + internal/libwebp-1.2.1/cmake/cpu.cmake | 162 + internal/libwebp-1.2.1/cmake/deps.cmake | 180 + internal/libwebp-1.2.1/codereview.settings | 4 + internal/libwebp-1.2.1/configure.ac | 798 +++ internal/libwebp-1.2.1/doc/README | 29 + internal/libwebp-1.2.1/doc/TODO | 13 + internal/libwebp-1.2.1/doc/template.html | 94 + .../libwebp-1.2.1/doc/webp-container-spec.txt | 819 +++ .../doc/webp-lossless-bitstream-spec.txt | 1090 ++++ internal/libwebp-1.2.1/examples/Android.mk | 93 + internal/libwebp-1.2.1/examples/Makefile.am | 119 + internal/libwebp-1.2.1/examples/anim_diff.c | 317 + internal/libwebp-1.2.1/examples/anim_dump.c | 121 + internal/libwebp-1.2.1/examples/anim_util.c | 779 +++ internal/libwebp-1.2.1/examples/anim_util.h | 73 + internal/libwebp-1.2.1/examples/cwebp.c | 1239 ++++ internal/libwebp-1.2.1/examples/dwebp.c | 421 ++ .../libwebp-1.2.1/examples/example_util.c | 135 + .../libwebp-1.2.1/examples/example_util.h | 70 + internal/libwebp-1.2.1/examples/gif2webp.c | 606 ++ internal/libwebp-1.2.1/examples/gifdec.c | 416 ++ internal/libwebp-1.2.1/examples/gifdec.h | 116 + internal/libwebp-1.2.1/examples/img2webp.c | 324 + internal/libwebp-1.2.1/examples/stopwatch.h | 63 + internal/libwebp-1.2.1/examples/test.webp | Bin 0 -> 4880 bytes internal/libwebp-1.2.1/examples/test_ref.ppm | 4 + internal/libwebp-1.2.1/examples/unicode.h | 102 + internal/libwebp-1.2.1/examples/unicode_gif.h | 75 + internal/libwebp-1.2.1/examples/vwebp.c | 641 ++ internal/libwebp-1.2.1/examples/webpinfo.c | 1180 ++++ internal/libwebp-1.2.1/examples/webpmux.c | 1233 ++++ internal/libwebp-1.2.1/extras/Makefile.am | 44 + internal/libwebp-1.2.1/extras/extras.c | 162 + internal/libwebp-1.2.1/extras/extras.h | 77 + internal/libwebp-1.2.1/extras/get_disto.c | 355 ++ .../libwebp-1.2.1/extras/quality_estimate.c | 129 + internal/libwebp-1.2.1/extras/vwebp_sdl.c | 101 + internal/libwebp-1.2.1/extras/webp_quality.c | 54 + internal/libwebp-1.2.1/extras/webp_to_sdl.c | 110 + internal/libwebp-1.2.1/extras/webp_to_sdl.h | 22 + internal/libwebp-1.2.1/gradle.properties | 14 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + internal/libwebp-1.2.1/gradlew | 183 + internal/libwebp-1.2.1/gradlew.bat | 100 + internal/libwebp-1.2.1/imageio/Android.mk | 54 + internal/libwebp-1.2.1/imageio/Makefile.am | 32 + internal/libwebp-1.2.1/imageio/image_dec.c | 66 + internal/libwebp-1.2.1/imageio/image_dec.h | 67 + internal/libwebp-1.2.1/imageio/image_enc.c | 604 ++ internal/libwebp-1.2.1/imageio/image_enc.h | 96 + internal/libwebp-1.2.1/imageio/imageio_util.c | 162 + internal/libwebp-1.2.1/imageio/imageio_util.h | 64 + internal/libwebp-1.2.1/imageio/jpegdec.c | 360 ++ internal/libwebp-1.2.1/imageio/jpegdec.h | 37 + internal/libwebp-1.2.1/imageio/metadata.c | 49 + internal/libwebp-1.2.1/imageio/metadata.h | 47 + internal/libwebp-1.2.1/imageio/pngdec.c | 374 ++ internal/libwebp-1.2.1/imageio/pngdec.h | 37 + internal/libwebp-1.2.1/imageio/pnmdec.c | 296 + internal/libwebp-1.2.1/imageio/pnmdec.h | 37 + internal/libwebp-1.2.1/imageio/tiffdec.c | 291 + internal/libwebp-1.2.1/imageio/tiffdec.h | 37 + internal/libwebp-1.2.1/imageio/webpdec.c | 244 + internal/libwebp-1.2.1/imageio/webpdec.h | 67 + internal/libwebp-1.2.1/imageio/wicdec.c | 413 ++ internal/libwebp-1.2.1/imageio/wicdec.h | 34 + internal/libwebp-1.2.1/infra/common.sh | 106 + internal/libwebp-1.2.1/infra/compile.sh | 401 ++ .../libwebp-1.2.1/infra/compile_android.sh | 224 + internal/libwebp-1.2.1/infra/compile_js.sh | 75 + .../infra/run_static_analysis.sh | 98 + internal/libwebp-1.2.1/iosbuild.sh | 170 + internal/libwebp-1.2.1/m4/.gitignore | 2 + internal/libwebp-1.2.1/m4/ax_pthread.m4 | 332 + internal/libwebp-1.2.1/makefile.unix | 501 ++ internal/libwebp-1.2.1/man/Makefile.am | 17 + internal/libwebp-1.2.1/man/cwebp.1 | 326 + internal/libwebp-1.2.1/man/dwebp.1 | 150 + internal/libwebp-1.2.1/man/gif2webp.1 | 164 + internal/libwebp-1.2.1/man/img2webp.1 | 105 + internal/libwebp-1.2.1/man/vwebp.1 | 101 + internal/libwebp-1.2.1/man/webpinfo.1 | 80 + internal/libwebp-1.2.1/man/webpmux.1 | 271 + internal/libwebp-1.2.1/src/Makefile.am | 55 + internal/libwebp-1.2.1/src/dec/Makefile.am | 29 + internal/libwebp-1.2.1/src/dec/alpha_dec.c | 232 + internal/libwebp-1.2.1/src/dec/alphai_dec.h | 54 + internal/libwebp-1.2.1/src/dec/buffer_dec.c | 310 + internal/libwebp-1.2.1/src/dec/common_dec.h | 54 + internal/libwebp-1.2.1/src/dec/frame_dec.c | 803 +++ internal/libwebp-1.2.1/src/dec/idec_dec.c | 908 +++ internal/libwebp-1.2.1/src/dec/io_dec.c | 662 ++ internal/libwebp-1.2.1/src/dec/quant_dec.c | 115 + internal/libwebp-1.2.1/src/dec/tree_dec.c | 537 ++ internal/libwebp-1.2.1/src/dec/vp8_dec.c | 722 +++ internal/libwebp-1.2.1/src/dec/vp8_dec.h | 185 + internal/libwebp-1.2.1/src/dec/vp8i_dec.h | 319 + internal/libwebp-1.2.1/src/dec/vp8l_dec.c | 1742 +++++ internal/libwebp-1.2.1/src/dec/vp8li_dec.h | 135 + internal/libwebp-1.2.1/src/dec/webp_dec.c | 852 +++ internal/libwebp-1.2.1/src/dec/webpi_dec.h | 137 + internal/libwebp-1.2.1/src/demux/Makefile.am | 18 + .../libwebp-1.2.1/src/demux/anim_decode.c | 465 ++ internal/libwebp-1.2.1/src/demux/demux.c | 978 +++ .../src/demux/libwebpdemux.pc.in | 11 + .../libwebp-1.2.1/src/demux/libwebpdemux.rc | 41 + internal/libwebp-1.2.1/src/dsp/Makefile.am | 186 + .../libwebp-1.2.1/src/dsp/alpha_processing.c | 495 ++ .../src/dsp/alpha_processing_mips_dsp_r2.c | 228 + .../src/dsp/alpha_processing_neon.c | 192 + .../src/dsp/alpha_processing_sse2.c | 367 ++ .../src/dsp/alpha_processing_sse41.c | 92 + internal/libwebp-1.2.1/src/dsp/common_sse2.h | 194 + internal/libwebp-1.2.1/src/dsp/common_sse41.h | 132 + internal/libwebp-1.2.1/src/dsp/cost.c | 411 ++ internal/libwebp-1.2.1/src/dsp/cost_mips32.c | 154 + .../libwebp-1.2.1/src/dsp/cost_mips_dsp_r2.c | 107 + internal/libwebp-1.2.1/src/dsp/cost_neon.c | 122 + internal/libwebp-1.2.1/src/dsp/cost_sse2.c | 119 + internal/libwebp-1.2.1/src/dsp/cpu.c | 253 + internal/libwebp-1.2.1/src/dsp/dec.c | 887 +++ .../libwebp-1.2.1/src/dsp/dec_clip_tables.c | 369 ++ internal/libwebp-1.2.1/src/dsp/dec_mips32.c | 587 ++ .../libwebp-1.2.1/src/dsp/dec_mips_dsp_r2.c | 994 +++ internal/libwebp-1.2.1/src/dsp/dec_msa.c | 1020 +++ internal/libwebp-1.2.1/src/dsp/dec_neon.c | 1663 +++++ internal/libwebp-1.2.1/src/dsp/dec_sse2.c | 1227 ++++ internal/libwebp-1.2.1/src/dsp/dec_sse41.c | 46 + internal/libwebp-1.2.1/src/dsp/dsp.h | 731 +++ internal/libwebp-1.2.1/src/dsp/enc.c | 830 +++ internal/libwebp-1.2.1/src/dsp/enc_mips32.c | 677 ++ .../libwebp-1.2.1/src/dsp/enc_mips_dsp_r2.c | 1517 +++++ internal/libwebp-1.2.1/src/dsp/enc_msa.c | 896 +++ internal/libwebp-1.2.1/src/dsp/enc_neon.c | 938 +++ internal/libwebp-1.2.1/src/dsp/enc_sse2.c | 1381 ++++ internal/libwebp-1.2.1/src/dsp/enc_sse41.c | 339 + internal/libwebp-1.2.1/src/dsp/filters.c | 287 + .../src/dsp/filters_mips_dsp_r2.c | 402 ++ internal/libwebp-1.2.1/src/dsp/filters_msa.c | 202 + internal/libwebp-1.2.1/src/dsp/filters_neon.c | 329 + internal/libwebp-1.2.1/src/dsp/filters_sse2.c | 340 + internal/libwebp-1.2.1/src/dsp/lossless.c | 680 ++ internal/libwebp-1.2.1/src/dsp/lossless.h | 259 + .../libwebp-1.2.1/src/dsp/lossless_common.h | 191 + internal/libwebp-1.2.1/src/dsp/lossless_enc.c | 948 +++ .../src/dsp/lossless_enc_mips32.c | 397 ++ .../src/dsp/lossless_enc_mips_dsp_r2.c | 281 + .../libwebp-1.2.1/src/dsp/lossless_enc_msa.c | 148 + .../libwebp-1.2.1/src/dsp/lossless_enc_neon.c | 144 + .../libwebp-1.2.1/src/dsp/lossless_enc_sse2.c | 669 ++ .../src/dsp/lossless_enc_sse41.c | 155 + .../src/dsp/lossless_mips_dsp_r2.c | 701 ++ internal/libwebp-1.2.1/src/dsp/lossless_msa.c | 356 ++ .../libwebp-1.2.1/src/dsp/lossless_neon.c | 645 ++ .../libwebp-1.2.1/src/dsp/lossless_sse2.c | 716 +++ .../libwebp-1.2.1/src/dsp/lossless_sse41.c | 132 + internal/libwebp-1.2.1/src/dsp/mips_macro.h | 200 + internal/libwebp-1.2.1/src/dsp/msa_macro.h | 1392 ++++ internal/libwebp-1.2.1/src/dsp/neon.h | 101 + internal/libwebp-1.2.1/src/dsp/quant.h | 85 + internal/libwebp-1.2.1/src/dsp/rescaler.c | 251 + .../libwebp-1.2.1/src/dsp/rescaler_mips32.c | 295 + .../src/dsp/rescaler_mips_dsp_r2.c | 314 + internal/libwebp-1.2.1/src/dsp/rescaler_msa.c | 443 ++ .../libwebp-1.2.1/src/dsp/rescaler_neon.c | 192 + .../libwebp-1.2.1/src/dsp/rescaler_sse2.c | 366 ++ internal/libwebp-1.2.1/src/dsp/ssim.c | 159 + internal/libwebp-1.2.1/src/dsp/ssim_sse2.c | 165 + internal/libwebp-1.2.1/src/dsp/upsampling.c | 327 + .../src/dsp/upsampling_mips_dsp_r2.c | 291 + .../libwebp-1.2.1/src/dsp/upsampling_msa.c | 688 ++ .../libwebp-1.2.1/src/dsp/upsampling_neon.c | 285 + .../libwebp-1.2.1/src/dsp/upsampling_sse2.c | 267 + .../libwebp-1.2.1/src/dsp/upsampling_sse41.c | 239 + internal/libwebp-1.2.1/src/dsp/yuv.c | 308 + internal/libwebp-1.2.1/src/dsp/yuv.h | 210 + internal/libwebp-1.2.1/src/dsp/yuv_mips32.c | 103 + .../libwebp-1.2.1/src/dsp/yuv_mips_dsp_r2.c | 134 + internal/libwebp-1.2.1/src/dsp/yuv_neon.c | 288 + internal/libwebp-1.2.1/src/dsp/yuv_sse2.c | 874 +++ internal/libwebp-1.2.1/src/dsp/yuv_sse41.c | 613 ++ internal/libwebp-1.2.1/src/enc/Makefile.am | 42 + internal/libwebp-1.2.1/src/enc/alpha_enc.c | 443 ++ internal/libwebp-1.2.1/src/enc/analysis_enc.c | 475 ++ .../src/enc/backward_references_cost_enc.c | 790 +++ .../src/enc/backward_references_enc.c | 1030 +++ .../src/enc/backward_references_enc.h | 240 + internal/libwebp-1.2.1/src/enc/config_enc.c | 157 + internal/libwebp-1.2.1/src/enc/cost_enc.c | 342 + internal/libwebp-1.2.1/src/enc/cost_enc.h | 82 + internal/libwebp-1.2.1/src/enc/filter_enc.c | 235 + internal/libwebp-1.2.1/src/enc/frame_enc.c | 894 +++ .../libwebp-1.2.1/src/enc/histogram_enc.c | 1252 ++++ .../libwebp-1.2.1/src/enc/histogram_enc.h | 128 + internal/libwebp-1.2.1/src/enc/iterator_enc.c | 459 ++ .../libwebp-1.2.1/src/enc/near_lossless_enc.c | 151 + .../libwebp-1.2.1/src/enc/picture_csp_enc.c | 1210 ++++ internal/libwebp-1.2.1/src/enc/picture_enc.c | 296 + .../libwebp-1.2.1/src/enc/picture_psnr_enc.c | 258 + .../src/enc/picture_rescale_enc.c | 316 + .../libwebp-1.2.1/src/enc/picture_tools_enc.c | 273 + .../libwebp-1.2.1/src/enc/predictor_enc.c | 772 +++ internal/libwebp-1.2.1/src/enc/quant_enc.c | 1388 ++++ internal/libwebp-1.2.1/src/enc/syntax_enc.c | 388 ++ internal/libwebp-1.2.1/src/enc/token_enc.c | 262 + internal/libwebp-1.2.1/src/enc/tree_enc.c | 504 ++ internal/libwebp-1.2.1/src/enc/vp8i_enc.h | 517 ++ internal/libwebp-1.2.1/src/enc/vp8l_enc.c | 2138 +++++++ internal/libwebp-1.2.1/src/enc/vp8li_enc.h | 120 + internal/libwebp-1.2.1/src/enc/webp_enc.c | 410 ++ internal/libwebp-1.2.1/src/libwebp.pc.in | 11 + internal/libwebp-1.2.1/src/libwebp.rc | 41 + .../libwebp-1.2.1/src/libwebpdecoder.pc.in | 11 + internal/libwebp-1.2.1/src/libwebpdecoder.rc | 41 + internal/libwebp-1.2.1/src/mux/Makefile.am | 22 + internal/libwebp-1.2.1/src/mux/anim_encode.c | 1585 +++++ internal/libwebp-1.2.1/src/mux/animi.h | 43 + .../libwebp-1.2.1/src/mux/libwebpmux.pc.in | 12 + internal/libwebp-1.2.1/src/mux/libwebpmux.rc | 41 + internal/libwebp-1.2.1/src/mux/muxedit.c | 655 ++ internal/libwebp-1.2.1/src/mux/muxi.h | 234 + internal/libwebp-1.2.1/src/mux/muxinternal.c | 548 ++ internal/libwebp-1.2.1/src/mux/muxread.c | 554 ++ internal/libwebp-1.2.1/src/utils/Makefile.am | 51 + .../src/utils/bit_reader_inl_utils.h | 196 + .../src/utils/bit_reader_utils.c | 298 + .../src/utils/bit_reader_utils.h | 194 + .../src/utils/bit_writer_utils.c | 347 + .../src/utils/bit_writer_utils.h | 154 + .../src/utils/color_cache_utils.c | 49 + .../src/utils/color_cache_utils.h | 89 + .../src/utils/endian_inl_utils.h | 93 + .../libwebp-1.2.1/src/utils/filters_utils.c | 76 + .../libwebp-1.2.1/src/utils/filters_utils.h | 32 + .../src/utils/huffman_encode_utils.c | 416 ++ .../src/utils/huffman_encode_utils.h | 60 + .../libwebp-1.2.1/src/utils/huffman_utils.c | 235 + .../libwebp-1.2.1/src/utils/huffman_utils.h | 90 + .../src/utils/quant_levels_dec_utils.c | 291 + .../src/utils/quant_levels_dec_utils.h | 35 + .../src/utils/quant_levels_utils.c | 140 + .../src/utils/quant_levels_utils.h | 36 + .../libwebp-1.2.1/src/utils/random_utils.c | 43 + .../libwebp-1.2.1/src/utils/random_utils.h | 63 + .../libwebp-1.2.1/src/utils/rescaler_utils.c | 160 + .../libwebp-1.2.1/src/utils/rescaler_utils.h | 102 + .../libwebp-1.2.1/src/utils/thread_utils.c | 369 ++ .../libwebp-1.2.1/src/utils/thread_utils.h | 90 + internal/libwebp-1.2.1/src/utils/utils.c | 338 + internal/libwebp-1.2.1/src/utils/utils.h | 200 + internal/libwebp-1.2.1/src/webp/decode.h | 503 ++ internal/libwebp-1.2.1/src/webp/demux.h | 363 ++ internal/libwebp-1.2.1/src/webp/encode.h | 552 ++ .../libwebp-1.2.1/src/webp/format_constants.h | 87 + internal/libwebp-1.2.1/src/webp/mux.h | 530 ++ internal/libwebp-1.2.1/src/webp/mux_types.h | 98 + internal/libwebp-1.2.1/src/webp/types.h | 68 + internal/libwebp-1.2.1/swig/README | 56 + internal/libwebp-1.2.1/swig/libwebp.go | 45 + internal/libwebp-1.2.1/swig/libwebp.jar | Bin 0 -> 2150 bytes internal/libwebp-1.2.1/swig/libwebp.py | 235 + internal/libwebp-1.2.1/swig/libwebp.swig | 438 ++ internal/libwebp-1.2.1/swig/libwebp_gc.c | 52 + internal/libwebp-1.2.1/swig/libwebp_go_wrap.c | 274 + .../libwebp-1.2.1/swig/libwebp_java_wrap.c | 1765 ++++++ .../libwebp-1.2.1/swig/libwebp_python_wrap.c | 5628 +++++++++++++++++ internal/libwebp-1.2.1/swig/setup.py | 40 + internal/libwebp-1.2.1/tests/README | 19 + .../tests/fuzzer/advanced_api_fuzzer.c | 114 + .../tests/fuzzer/animation_api_fuzzer.c | 78 + .../tests/fuzzer/animdecoder_fuzzer.cc | 52 + .../tests/fuzzer/animencoder_fuzzer.cc | 177 + .../tests/fuzzer/enc_dec_fuzzer.cc | 141 + internal/libwebp-1.2.1/tests/fuzzer/fuzz.dict | 17 + .../libwebp-1.2.1/tests/fuzzer/fuzz_utils.h | 207 + .../libwebp-1.2.1/tests/fuzzer/img_alpha.h | 381 ++ .../libwebp-1.2.1/tests/fuzzer/img_grid.h | 125 + .../libwebp-1.2.1/tests/fuzzer/img_peak.h | 5533 ++++++++++++++++ .../libwebp-1.2.1/tests/fuzzer/makefile.unix | 30 + .../tests/fuzzer/mux_demux_api_fuzzer.c | 96 + .../tests/fuzzer/simple_api_fuzzer.c | 88 + internal/libwebp-1.2.1/webp_js/index.html | 75 + .../libwebp-1.2.1/webp_js/index_wasm.html | 90 + .../libwebp-1.2.1/webp_js/test_webp_js.webp | Bin 0 -> 1321542 bytes .../libwebp-1.2.1/webp_js/test_webp_wasm.webp | Bin 0 -> 1321542 bytes internal/libwebp-1.2.1/xcframeworkbuild.sh | 233 + z_libwebp_src_dec_alpha_dec.c | 9 - z_libwebp_src_dec_buffer_dec.c | 9 - z_libwebp_src_dec_frame_dec.c | 9 - z_libwebp_src_dec_idec_dec.c | 9 - z_libwebp_src_dec_io_dec.c | 9 - z_libwebp_src_dec_quant_dec.c | 9 - z_libwebp_src_dec_tree_dec.c | 9 - z_libwebp_src_dec_vp8_dec.c | 9 - z_libwebp_src_dec_vp8l_dec.c | 9 - z_libwebp_src_dec_webp_dec.c | 9 - z_libwebp_src_demux_anim_decode.c | 9 - z_libwebp_src_demux_demux.c | 9 - z_libwebp_src_dsp_alpha_processing.c | 9 - ...ebp_src_dsp_alpha_processing_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_alpha_processing_neon.c | 9 - z_libwebp_src_dsp_alpha_processing_sse2.c | 9 - z_libwebp_src_dsp_alpha_processing_sse41.c | 9 - z_libwebp_src_dsp_cost.c | 9 - z_libwebp_src_dsp_cost_mips32.c | 9 - z_libwebp_src_dsp_cost_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_cost_neon.c | 9 - z_libwebp_src_dsp_cost_sse2.c | 9 - z_libwebp_src_dsp_cpu.c | 9 - z_libwebp_src_dsp_dec.c | 9 - z_libwebp_src_dsp_dec_clip_tables.c | 9 - z_libwebp_src_dsp_dec_mips32.c | 9 - z_libwebp_src_dsp_dec_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_dec_msa.c | 9 - z_libwebp_src_dsp_dec_neon.c | 9 - z_libwebp_src_dsp_dec_sse2.c | 9 - z_libwebp_src_dsp_dec_sse41.c | 9 - z_libwebp_src_dsp_enc.c | 9 - z_libwebp_src_dsp_enc_mips32.c | 9 - z_libwebp_src_dsp_enc_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_enc_msa.c | 9 - z_libwebp_src_dsp_enc_neon.c | 9 - z_libwebp_src_dsp_enc_sse2.c | 9 - z_libwebp_src_dsp_enc_sse41.c | 9 - z_libwebp_src_dsp_filters.c | 9 - z_libwebp_src_dsp_filters_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_filters_msa.c | 9 - z_libwebp_src_dsp_filters_neon.c | 9 - z_libwebp_src_dsp_filters_sse2.c | 9 - z_libwebp_src_dsp_lossless.c | 9 - z_libwebp_src_dsp_lossless_enc.c | 9 - z_libwebp_src_dsp_lossless_enc_mips32.c | 9 - z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_lossless_enc_msa.c | 9 - z_libwebp_src_dsp_lossless_enc_neon.c | 9 - z_libwebp_src_dsp_lossless_enc_sse2.c | 9 - z_libwebp_src_dsp_lossless_enc_sse41.c | 9 - z_libwebp_src_dsp_lossless_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_lossless_msa.c | 9 - z_libwebp_src_dsp_lossless_neon.c | 9 - z_libwebp_src_dsp_lossless_sse2.c | 9 - z_libwebp_src_dsp_rescaler.c | 9 - z_libwebp_src_dsp_rescaler_mips32.c | 9 - z_libwebp_src_dsp_rescaler_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_rescaler_msa.c | 9 - z_libwebp_src_dsp_rescaler_neon.c | 9 - z_libwebp_src_dsp_rescaler_sse2.c | 9 - z_libwebp_src_dsp_ssim.c | 9 - z_libwebp_src_dsp_ssim_sse2.c | 9 - z_libwebp_src_dsp_upsampling.c | 9 - z_libwebp_src_dsp_upsampling_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_upsampling_msa.c | 9 - z_libwebp_src_dsp_upsampling_neon.c | 9 - z_libwebp_src_dsp_upsampling_sse2.c | 9 - z_libwebp_src_dsp_upsampling_sse41.c | 9 - z_libwebp_src_dsp_yuv.c | 9 - z_libwebp_src_dsp_yuv_mips32.c | 9 - z_libwebp_src_dsp_yuv_mips_dsp_r2.c | 9 - z_libwebp_src_dsp_yuv_neon.c | 9 - z_libwebp_src_dsp_yuv_sse2.c | 9 - z_libwebp_src_dsp_yuv_sse41.c | 9 - z_libwebp_src_enc_alpha_enc.c | 9 - z_libwebp_src_enc_analysis_enc.c | 9 - ...ebp_src_enc_backward_references_cost_enc.c | 9 - z_libwebp_src_enc_backward_references_enc.c | 9 - z_libwebp_src_enc_config_enc.c | 9 - z_libwebp_src_enc_cost_enc.c | 9 - z_libwebp_src_enc_filter_enc.c | 9 - z_libwebp_src_enc_frame_enc.c | 9 - z_libwebp_src_enc_histogram_enc.c | 9 - z_libwebp_src_enc_iterator_enc.c | 9 - z_libwebp_src_enc_near_lossless_enc.c | 9 - z_libwebp_src_enc_picture_csp_enc.c | 9 - z_libwebp_src_enc_picture_enc.c | 9 - z_libwebp_src_enc_picture_psnr_enc.c | 9 - z_libwebp_src_enc_picture_rescale_enc.c | 9 - z_libwebp_src_enc_picture_tools_enc.c | 9 - z_libwebp_src_enc_predictor_enc.c | 9 - z_libwebp_src_enc_quant_enc.c | 9 - z_libwebp_src_enc_syntax_enc.c | 9 - z_libwebp_src_enc_token_enc.c | 9 - z_libwebp_src_enc_tree_enc.c | 9 - z_libwebp_src_enc_vp8l_enc.c | 9 - z_libwebp_src_enc_webp_enc.c | 9 - z_libwebp_src_mux_anim_encode.c | 9 - z_libwebp_src_mux_muxedit.c | 9 - z_libwebp_src_mux_muxinternal.c | 9 - z_libwebp_src_mux_muxread.c | 9 - z_libwebp_src_utils_bit_reader_utils.c | 9 - z_libwebp_src_utils_bit_writer_utils.c | 9 - z_libwebp_src_utils_color_cache_utils.c | 9 - z_libwebp_src_utils_filters_utils.c | 9 - z_libwebp_src_utils_huffman_encode_utils.c | 9 - z_libwebp_src_utils_huffman_utils.c | 9 - z_libwebp_src_utils_quant_levels_dec_utils.c | 9 - z_libwebp_src_utils_quant_levels_utils.c | 9 - z_libwebp_src_utils_random_utils.c | 9 - z_libwebp_src_utils_rescaler_utils.c | 9 - z_libwebp_src_utils_thread_utils.c | 9 - z_libwebp_src_utils_utils.c | 9 - 426 files changed, 112767 insertions(+), 1030 deletions(-) create mode 100644 internal/libwebp-1.2.1/.gitattributes create mode 100644 internal/libwebp-1.2.1/.gitignore create mode 100644 internal/libwebp-1.2.1/.mailmap create mode 100644 internal/libwebp-1.2.1/.pylintrc create mode 100644 internal/libwebp-1.2.1/.style.yapf create mode 100644 internal/libwebp-1.2.1/AUTHORS create mode 100644 internal/libwebp-1.2.1/Android.mk create mode 100644 internal/libwebp-1.2.1/CMakeLists.txt create mode 100644 internal/libwebp-1.2.1/CONTRIBUTING.md create mode 100644 internal/libwebp-1.2.1/COPYING create mode 100644 internal/libwebp-1.2.1/ChangeLog create mode 100644 internal/libwebp-1.2.1/Makefile.am create mode 100644 internal/libwebp-1.2.1/Makefile.vc create mode 100644 internal/libwebp-1.2.1/NEWS create mode 100644 internal/libwebp-1.2.1/PATENTS create mode 100644 internal/libwebp-1.2.1/PRESUBMIT.py create mode 100644 internal/libwebp-1.2.1/README create mode 100644 internal/libwebp-1.2.1/README.mux create mode 100644 internal/libwebp-1.2.1/README.webp_js create mode 100755 internal/libwebp-1.2.1/autogen.sh create mode 100644 internal/libwebp-1.2.1/build.gradle create mode 100644 internal/libwebp-1.2.1/cmake/WebPConfig.cmake.in create mode 100644 internal/libwebp-1.2.1/cmake/config.h.in create mode 100644 internal/libwebp-1.2.1/cmake/cpu.cmake create mode 100644 internal/libwebp-1.2.1/cmake/deps.cmake create mode 100644 internal/libwebp-1.2.1/codereview.settings create mode 100644 internal/libwebp-1.2.1/configure.ac create mode 100644 internal/libwebp-1.2.1/doc/README create mode 100644 internal/libwebp-1.2.1/doc/TODO create mode 100644 internal/libwebp-1.2.1/doc/template.html create mode 100644 internal/libwebp-1.2.1/doc/webp-container-spec.txt create mode 100644 internal/libwebp-1.2.1/doc/webp-lossless-bitstream-spec.txt create mode 100644 internal/libwebp-1.2.1/examples/Android.mk create mode 100644 internal/libwebp-1.2.1/examples/Makefile.am create mode 100644 internal/libwebp-1.2.1/examples/anim_diff.c create mode 100644 internal/libwebp-1.2.1/examples/anim_dump.c create mode 100644 internal/libwebp-1.2.1/examples/anim_util.c create mode 100644 internal/libwebp-1.2.1/examples/anim_util.h create mode 100644 internal/libwebp-1.2.1/examples/cwebp.c create mode 100644 internal/libwebp-1.2.1/examples/dwebp.c create mode 100644 internal/libwebp-1.2.1/examples/example_util.c create mode 100644 internal/libwebp-1.2.1/examples/example_util.h create mode 100644 internal/libwebp-1.2.1/examples/gif2webp.c create mode 100644 internal/libwebp-1.2.1/examples/gifdec.c create mode 100644 internal/libwebp-1.2.1/examples/gifdec.h create mode 100644 internal/libwebp-1.2.1/examples/img2webp.c create mode 100644 internal/libwebp-1.2.1/examples/stopwatch.h create mode 100644 internal/libwebp-1.2.1/examples/test.webp create mode 100644 internal/libwebp-1.2.1/examples/test_ref.ppm create mode 100644 internal/libwebp-1.2.1/examples/unicode.h create mode 100644 internal/libwebp-1.2.1/examples/unicode_gif.h create mode 100644 internal/libwebp-1.2.1/examples/vwebp.c create mode 100644 internal/libwebp-1.2.1/examples/webpinfo.c create mode 100644 internal/libwebp-1.2.1/examples/webpmux.c create mode 100644 internal/libwebp-1.2.1/extras/Makefile.am create mode 100644 internal/libwebp-1.2.1/extras/extras.c create mode 100644 internal/libwebp-1.2.1/extras/extras.h create mode 100644 internal/libwebp-1.2.1/extras/get_disto.c create mode 100644 internal/libwebp-1.2.1/extras/quality_estimate.c create mode 100644 internal/libwebp-1.2.1/extras/vwebp_sdl.c create mode 100644 internal/libwebp-1.2.1/extras/webp_quality.c create mode 100644 internal/libwebp-1.2.1/extras/webp_to_sdl.c create mode 100644 internal/libwebp-1.2.1/extras/webp_to_sdl.h create mode 100644 internal/libwebp-1.2.1/gradle.properties create mode 100644 internal/libwebp-1.2.1/gradle/wrapper/gradle-wrapper.jar create mode 100644 internal/libwebp-1.2.1/gradle/wrapper/gradle-wrapper.properties create mode 100755 internal/libwebp-1.2.1/gradlew create mode 100644 internal/libwebp-1.2.1/gradlew.bat create mode 100644 internal/libwebp-1.2.1/imageio/Android.mk create mode 100644 internal/libwebp-1.2.1/imageio/Makefile.am create mode 100644 internal/libwebp-1.2.1/imageio/image_dec.c create mode 100644 internal/libwebp-1.2.1/imageio/image_dec.h create mode 100644 internal/libwebp-1.2.1/imageio/image_enc.c create mode 100644 internal/libwebp-1.2.1/imageio/image_enc.h create mode 100644 internal/libwebp-1.2.1/imageio/imageio_util.c create mode 100644 internal/libwebp-1.2.1/imageio/imageio_util.h create mode 100644 internal/libwebp-1.2.1/imageio/jpegdec.c create mode 100644 internal/libwebp-1.2.1/imageio/jpegdec.h create mode 100644 internal/libwebp-1.2.1/imageio/metadata.c create mode 100644 internal/libwebp-1.2.1/imageio/metadata.h create mode 100644 internal/libwebp-1.2.1/imageio/pngdec.c create mode 100644 internal/libwebp-1.2.1/imageio/pngdec.h create mode 100644 internal/libwebp-1.2.1/imageio/pnmdec.c create mode 100644 internal/libwebp-1.2.1/imageio/pnmdec.h create mode 100644 internal/libwebp-1.2.1/imageio/tiffdec.c create mode 100644 internal/libwebp-1.2.1/imageio/tiffdec.h create mode 100644 internal/libwebp-1.2.1/imageio/webpdec.c create mode 100644 internal/libwebp-1.2.1/imageio/webpdec.h create mode 100644 internal/libwebp-1.2.1/imageio/wicdec.c create mode 100644 internal/libwebp-1.2.1/imageio/wicdec.h create mode 100644 internal/libwebp-1.2.1/infra/common.sh create mode 100755 internal/libwebp-1.2.1/infra/compile.sh create mode 100755 internal/libwebp-1.2.1/infra/compile_android.sh create mode 100755 internal/libwebp-1.2.1/infra/compile_js.sh create mode 100755 internal/libwebp-1.2.1/infra/run_static_analysis.sh create mode 100755 internal/libwebp-1.2.1/iosbuild.sh create mode 100644 internal/libwebp-1.2.1/m4/.gitignore create mode 100644 internal/libwebp-1.2.1/m4/ax_pthread.m4 create mode 100644 internal/libwebp-1.2.1/makefile.unix create mode 100644 internal/libwebp-1.2.1/man/Makefile.am create mode 100644 internal/libwebp-1.2.1/man/cwebp.1 create mode 100644 internal/libwebp-1.2.1/man/dwebp.1 create mode 100644 internal/libwebp-1.2.1/man/gif2webp.1 create mode 100644 internal/libwebp-1.2.1/man/img2webp.1 create mode 100644 internal/libwebp-1.2.1/man/vwebp.1 create mode 100644 internal/libwebp-1.2.1/man/webpinfo.1 create mode 100644 internal/libwebp-1.2.1/man/webpmux.1 create mode 100644 internal/libwebp-1.2.1/src/Makefile.am create mode 100644 internal/libwebp-1.2.1/src/dec/Makefile.am create mode 100644 internal/libwebp-1.2.1/src/dec/alpha_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/alphai_dec.h create mode 100644 internal/libwebp-1.2.1/src/dec/buffer_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/common_dec.h create mode 100644 internal/libwebp-1.2.1/src/dec/frame_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/idec_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/io_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/quant_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/tree_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/vp8_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/vp8_dec.h create mode 100644 internal/libwebp-1.2.1/src/dec/vp8i_dec.h create mode 100644 internal/libwebp-1.2.1/src/dec/vp8l_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/vp8li_dec.h create mode 100644 internal/libwebp-1.2.1/src/dec/webp_dec.c create mode 100644 internal/libwebp-1.2.1/src/dec/webpi_dec.h create mode 100644 internal/libwebp-1.2.1/src/demux/Makefile.am create mode 100644 internal/libwebp-1.2.1/src/demux/anim_decode.c create mode 100644 internal/libwebp-1.2.1/src/demux/demux.c create mode 100644 internal/libwebp-1.2.1/src/demux/libwebpdemux.pc.in create mode 100644 internal/libwebp-1.2.1/src/demux/libwebpdemux.rc create mode 100644 internal/libwebp-1.2.1/src/dsp/Makefile.am create mode 100644 internal/libwebp-1.2.1/src/dsp/alpha_processing.c create mode 100644 internal/libwebp-1.2.1/src/dsp/alpha_processing_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/alpha_processing_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/alpha_processing_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/alpha_processing_sse41.c create mode 100644 internal/libwebp-1.2.1/src/dsp/common_sse2.h create mode 100644 internal/libwebp-1.2.1/src/dsp/common_sse41.h create mode 100644 internal/libwebp-1.2.1/src/dsp/cost.c create mode 100644 internal/libwebp-1.2.1/src/dsp/cost_mips32.c create mode 100644 internal/libwebp-1.2.1/src/dsp/cost_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/cost_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/cost_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/cpu.c create mode 100644 internal/libwebp-1.2.1/src/dsp/dec.c create mode 100644 internal/libwebp-1.2.1/src/dsp/dec_clip_tables.c create mode 100644 internal/libwebp-1.2.1/src/dsp/dec_mips32.c create mode 100644 internal/libwebp-1.2.1/src/dsp/dec_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/dec_msa.c create mode 100644 internal/libwebp-1.2.1/src/dsp/dec_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/dec_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/dec_sse41.c create mode 100644 internal/libwebp-1.2.1/src/dsp/dsp.h create mode 100644 internal/libwebp-1.2.1/src/dsp/enc.c create mode 100644 internal/libwebp-1.2.1/src/dsp/enc_mips32.c create mode 100644 internal/libwebp-1.2.1/src/dsp/enc_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/enc_msa.c create mode 100644 internal/libwebp-1.2.1/src/dsp/enc_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/enc_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/enc_sse41.c create mode 100644 internal/libwebp-1.2.1/src/dsp/filters.c create mode 100644 internal/libwebp-1.2.1/src/dsp/filters_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/filters_msa.c create mode 100644 internal/libwebp-1.2.1/src/dsp/filters_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/filters_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless.h create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_common.h create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_enc.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_enc_mips32.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_enc_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_enc_msa.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_enc_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_enc_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_enc_sse41.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_msa.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/lossless_sse41.c create mode 100644 internal/libwebp-1.2.1/src/dsp/mips_macro.h create mode 100644 internal/libwebp-1.2.1/src/dsp/msa_macro.h create mode 100644 internal/libwebp-1.2.1/src/dsp/neon.h create mode 100644 internal/libwebp-1.2.1/src/dsp/quant.h create mode 100644 internal/libwebp-1.2.1/src/dsp/rescaler.c create mode 100644 internal/libwebp-1.2.1/src/dsp/rescaler_mips32.c create mode 100644 internal/libwebp-1.2.1/src/dsp/rescaler_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/rescaler_msa.c create mode 100644 internal/libwebp-1.2.1/src/dsp/rescaler_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/rescaler_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/ssim.c create mode 100644 internal/libwebp-1.2.1/src/dsp/ssim_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/upsampling.c create mode 100644 internal/libwebp-1.2.1/src/dsp/upsampling_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/upsampling_msa.c create mode 100644 internal/libwebp-1.2.1/src/dsp/upsampling_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/upsampling_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/upsampling_sse41.c create mode 100644 internal/libwebp-1.2.1/src/dsp/yuv.c create mode 100644 internal/libwebp-1.2.1/src/dsp/yuv.h create mode 100644 internal/libwebp-1.2.1/src/dsp/yuv_mips32.c create mode 100644 internal/libwebp-1.2.1/src/dsp/yuv_mips_dsp_r2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/yuv_neon.c create mode 100644 internal/libwebp-1.2.1/src/dsp/yuv_sse2.c create mode 100644 internal/libwebp-1.2.1/src/dsp/yuv_sse41.c create mode 100644 internal/libwebp-1.2.1/src/enc/Makefile.am create mode 100644 internal/libwebp-1.2.1/src/enc/alpha_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/analysis_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/backward_references_cost_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/backward_references_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/backward_references_enc.h create mode 100644 internal/libwebp-1.2.1/src/enc/config_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/cost_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/cost_enc.h create mode 100644 internal/libwebp-1.2.1/src/enc/filter_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/frame_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/histogram_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/histogram_enc.h create mode 100644 internal/libwebp-1.2.1/src/enc/iterator_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/near_lossless_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/picture_csp_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/picture_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/picture_psnr_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/picture_rescale_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/picture_tools_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/predictor_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/quant_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/syntax_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/token_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/tree_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/vp8i_enc.h create mode 100644 internal/libwebp-1.2.1/src/enc/vp8l_enc.c create mode 100644 internal/libwebp-1.2.1/src/enc/vp8li_enc.h create mode 100644 internal/libwebp-1.2.1/src/enc/webp_enc.c create mode 100644 internal/libwebp-1.2.1/src/libwebp.pc.in create mode 100644 internal/libwebp-1.2.1/src/libwebp.rc create mode 100644 internal/libwebp-1.2.1/src/libwebpdecoder.pc.in create mode 100644 internal/libwebp-1.2.1/src/libwebpdecoder.rc create mode 100644 internal/libwebp-1.2.1/src/mux/Makefile.am create mode 100644 internal/libwebp-1.2.1/src/mux/anim_encode.c create mode 100644 internal/libwebp-1.2.1/src/mux/animi.h create mode 100644 internal/libwebp-1.2.1/src/mux/libwebpmux.pc.in create mode 100644 internal/libwebp-1.2.1/src/mux/libwebpmux.rc create mode 100644 internal/libwebp-1.2.1/src/mux/muxedit.c create mode 100644 internal/libwebp-1.2.1/src/mux/muxi.h create mode 100644 internal/libwebp-1.2.1/src/mux/muxinternal.c create mode 100644 internal/libwebp-1.2.1/src/mux/muxread.c create mode 100644 internal/libwebp-1.2.1/src/utils/Makefile.am create mode 100644 internal/libwebp-1.2.1/src/utils/bit_reader_inl_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/bit_reader_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/bit_reader_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/bit_writer_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/bit_writer_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/color_cache_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/color_cache_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/endian_inl_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/filters_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/filters_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/huffman_encode_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/huffman_encode_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/huffman_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/huffman_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/quant_levels_dec_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/quant_levels_dec_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/quant_levels_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/quant_levels_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/random_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/random_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/rescaler_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/rescaler_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/thread_utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/thread_utils.h create mode 100644 internal/libwebp-1.2.1/src/utils/utils.c create mode 100644 internal/libwebp-1.2.1/src/utils/utils.h create mode 100644 internal/libwebp-1.2.1/src/webp/decode.h create mode 100644 internal/libwebp-1.2.1/src/webp/demux.h create mode 100644 internal/libwebp-1.2.1/src/webp/encode.h create mode 100644 internal/libwebp-1.2.1/src/webp/format_constants.h create mode 100644 internal/libwebp-1.2.1/src/webp/mux.h create mode 100644 internal/libwebp-1.2.1/src/webp/mux_types.h create mode 100644 internal/libwebp-1.2.1/src/webp/types.h create mode 100644 internal/libwebp-1.2.1/swig/README create mode 100644 internal/libwebp-1.2.1/swig/libwebp.go create mode 100644 internal/libwebp-1.2.1/swig/libwebp.jar create mode 100644 internal/libwebp-1.2.1/swig/libwebp.py create mode 100644 internal/libwebp-1.2.1/swig/libwebp.swig create mode 100644 internal/libwebp-1.2.1/swig/libwebp_gc.c create mode 100644 internal/libwebp-1.2.1/swig/libwebp_go_wrap.c create mode 100644 internal/libwebp-1.2.1/swig/libwebp_java_wrap.c create mode 100644 internal/libwebp-1.2.1/swig/libwebp_python_wrap.c create mode 100644 internal/libwebp-1.2.1/swig/setup.py create mode 100644 internal/libwebp-1.2.1/tests/README create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/advanced_api_fuzzer.c create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/animation_api_fuzzer.c create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/animdecoder_fuzzer.cc create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/animencoder_fuzzer.cc create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/enc_dec_fuzzer.cc create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/fuzz.dict create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/fuzz_utils.h create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/img_alpha.h create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/img_grid.h create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/img_peak.h create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/makefile.unix create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/mux_demux_api_fuzzer.c create mode 100644 internal/libwebp-1.2.1/tests/fuzzer/simple_api_fuzzer.c create mode 100644 internal/libwebp-1.2.1/webp_js/index.html create mode 100644 internal/libwebp-1.2.1/webp_js/index_wasm.html create mode 100644 internal/libwebp-1.2.1/webp_js/test_webp_js.webp create mode 100644 internal/libwebp-1.2.1/webp_js/test_webp_wasm.webp create mode 100755 internal/libwebp-1.2.1/xcframeworkbuild.sh delete mode 100644 z_libwebp_src_dec_alpha_dec.c delete mode 100644 z_libwebp_src_dec_buffer_dec.c delete mode 100644 z_libwebp_src_dec_frame_dec.c delete mode 100644 z_libwebp_src_dec_idec_dec.c delete mode 100644 z_libwebp_src_dec_io_dec.c delete mode 100644 z_libwebp_src_dec_quant_dec.c delete mode 100644 z_libwebp_src_dec_tree_dec.c delete mode 100644 z_libwebp_src_dec_vp8_dec.c delete mode 100644 z_libwebp_src_dec_vp8l_dec.c delete mode 100644 z_libwebp_src_dec_webp_dec.c delete mode 100644 z_libwebp_src_demux_anim_decode.c delete mode 100644 z_libwebp_src_demux_demux.c delete mode 100644 z_libwebp_src_dsp_alpha_processing.c delete mode 100644 z_libwebp_src_dsp_alpha_processing_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_alpha_processing_neon.c delete mode 100644 z_libwebp_src_dsp_alpha_processing_sse2.c delete mode 100644 z_libwebp_src_dsp_alpha_processing_sse41.c delete mode 100644 z_libwebp_src_dsp_cost.c delete mode 100644 z_libwebp_src_dsp_cost_mips32.c delete mode 100644 z_libwebp_src_dsp_cost_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_cost_neon.c delete mode 100644 z_libwebp_src_dsp_cost_sse2.c delete mode 100644 z_libwebp_src_dsp_cpu.c delete mode 100644 z_libwebp_src_dsp_dec.c delete mode 100644 z_libwebp_src_dsp_dec_clip_tables.c delete mode 100644 z_libwebp_src_dsp_dec_mips32.c delete mode 100644 z_libwebp_src_dsp_dec_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_dec_msa.c delete mode 100644 z_libwebp_src_dsp_dec_neon.c delete mode 100644 z_libwebp_src_dsp_dec_sse2.c delete mode 100644 z_libwebp_src_dsp_dec_sse41.c delete mode 100644 z_libwebp_src_dsp_enc.c delete mode 100644 z_libwebp_src_dsp_enc_mips32.c delete mode 100644 z_libwebp_src_dsp_enc_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_enc_msa.c delete mode 100644 z_libwebp_src_dsp_enc_neon.c delete mode 100644 z_libwebp_src_dsp_enc_sse2.c delete mode 100644 z_libwebp_src_dsp_enc_sse41.c delete mode 100644 z_libwebp_src_dsp_filters.c delete mode 100644 z_libwebp_src_dsp_filters_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_filters_msa.c delete mode 100644 z_libwebp_src_dsp_filters_neon.c delete mode 100644 z_libwebp_src_dsp_filters_sse2.c delete mode 100644 z_libwebp_src_dsp_lossless.c delete mode 100644 z_libwebp_src_dsp_lossless_enc.c delete mode 100644 z_libwebp_src_dsp_lossless_enc_mips32.c delete mode 100644 z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_lossless_enc_msa.c delete mode 100644 z_libwebp_src_dsp_lossless_enc_neon.c delete mode 100644 z_libwebp_src_dsp_lossless_enc_sse2.c delete mode 100644 z_libwebp_src_dsp_lossless_enc_sse41.c delete mode 100644 z_libwebp_src_dsp_lossless_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_lossless_msa.c delete mode 100644 z_libwebp_src_dsp_lossless_neon.c delete mode 100644 z_libwebp_src_dsp_lossless_sse2.c delete mode 100644 z_libwebp_src_dsp_rescaler.c delete mode 100644 z_libwebp_src_dsp_rescaler_mips32.c delete mode 100644 z_libwebp_src_dsp_rescaler_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_rescaler_msa.c delete mode 100644 z_libwebp_src_dsp_rescaler_neon.c delete mode 100644 z_libwebp_src_dsp_rescaler_sse2.c delete mode 100644 z_libwebp_src_dsp_ssim.c delete mode 100644 z_libwebp_src_dsp_ssim_sse2.c delete mode 100644 z_libwebp_src_dsp_upsampling.c delete mode 100644 z_libwebp_src_dsp_upsampling_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_upsampling_msa.c delete mode 100644 z_libwebp_src_dsp_upsampling_neon.c delete mode 100644 z_libwebp_src_dsp_upsampling_sse2.c delete mode 100644 z_libwebp_src_dsp_upsampling_sse41.c delete mode 100644 z_libwebp_src_dsp_yuv.c delete mode 100644 z_libwebp_src_dsp_yuv_mips32.c delete mode 100644 z_libwebp_src_dsp_yuv_mips_dsp_r2.c delete mode 100644 z_libwebp_src_dsp_yuv_neon.c delete mode 100644 z_libwebp_src_dsp_yuv_sse2.c delete mode 100644 z_libwebp_src_dsp_yuv_sse41.c delete mode 100644 z_libwebp_src_enc_alpha_enc.c delete mode 100644 z_libwebp_src_enc_analysis_enc.c delete mode 100644 z_libwebp_src_enc_backward_references_cost_enc.c delete mode 100644 z_libwebp_src_enc_backward_references_enc.c delete mode 100644 z_libwebp_src_enc_config_enc.c delete mode 100644 z_libwebp_src_enc_cost_enc.c delete mode 100644 z_libwebp_src_enc_filter_enc.c delete mode 100644 z_libwebp_src_enc_frame_enc.c delete mode 100644 z_libwebp_src_enc_histogram_enc.c delete mode 100644 z_libwebp_src_enc_iterator_enc.c delete mode 100644 z_libwebp_src_enc_near_lossless_enc.c delete mode 100644 z_libwebp_src_enc_picture_csp_enc.c delete mode 100644 z_libwebp_src_enc_picture_enc.c delete mode 100644 z_libwebp_src_enc_picture_psnr_enc.c delete mode 100644 z_libwebp_src_enc_picture_rescale_enc.c delete mode 100644 z_libwebp_src_enc_picture_tools_enc.c delete mode 100644 z_libwebp_src_enc_predictor_enc.c delete mode 100644 z_libwebp_src_enc_quant_enc.c delete mode 100644 z_libwebp_src_enc_syntax_enc.c delete mode 100644 z_libwebp_src_enc_token_enc.c delete mode 100644 z_libwebp_src_enc_tree_enc.c delete mode 100644 z_libwebp_src_enc_vp8l_enc.c delete mode 100644 z_libwebp_src_enc_webp_enc.c delete mode 100644 z_libwebp_src_mux_anim_encode.c delete mode 100644 z_libwebp_src_mux_muxedit.c delete mode 100644 z_libwebp_src_mux_muxinternal.c delete mode 100644 z_libwebp_src_mux_muxread.c delete mode 100644 z_libwebp_src_utils_bit_reader_utils.c delete mode 100644 z_libwebp_src_utils_bit_writer_utils.c delete mode 100644 z_libwebp_src_utils_color_cache_utils.c delete mode 100644 z_libwebp_src_utils_filters_utils.c delete mode 100644 z_libwebp_src_utils_huffman_encode_utils.c delete mode 100644 z_libwebp_src_utils_huffman_utils.c delete mode 100644 z_libwebp_src_utils_quant_levels_dec_utils.c delete mode 100644 z_libwebp_src_utils_quant_levels_utils.c delete mode 100644 z_libwebp_src_utils_random_utils.c delete mode 100644 z_libwebp_src_utils_rescaler_utils.c delete mode 100644 z_libwebp_src_utils_thread_utils.c delete mode 100644 z_libwebp_src_utils_utils.c diff --git a/capi.go b/capi.go index da9265f..274b266 100644 --- a/capi.go +++ b/capi.go @@ -15,8 +15,8 @@ package webp /* -#cgo CFLAGS: -I./internal/libwebp-1.0.2/ -#cgo CFLAGS: -I./internal/libwebp-1.0.2/src/ +#cgo CFLAGS: -I./internal/libwebp-1.2.1/ +#cgo CFLAGS: -I./internal/libwebp-1.2.1/src/ #cgo CFLAGS: -I./internal/include/ #cgo CFLAGS: -Wno-pointer-sign -DWEBP_USE_THREAD #cgo !windows LDFLAGS: -lm diff --git a/gen_helper.go b/gen_helper.go index 70a768b..895b8b8 100644 --- a/gen_helper.go +++ b/gen_helper.go @@ -26,6 +26,7 @@ func main() { printOldGenFiles() } +const wver="1.2.1" func clearOldGenFiles() { ss, err := filepath.Glob("z_*.c") if err != nil { @@ -38,8 +39,10 @@ func clearOldGenFiles() { } func genIncludeFiles() { - ss := parseCMakeListsTxt("internal/libwebp-1.0.2/CMakeLists.txt", "WEBP_SRC_DIR", "*.c") - muxSS, err := findFiles("internal/libwebp-1.0.2/src/mux", "*.c") + s1:=fmt.Sprintf("internal/libwebp-%s/CMakeLists.txt",wver) + s2:=fmt.Sprintf("internal/libwebp-%s/src/mux",wver) + ss := parseCMakeListsTxt(s1, "WEBP_SRC_DIR", "*.c") + muxSS, err := findFiles(s2, "*.c") if err != nil { log.Fatal(err) } diff --git a/internal/libwebp-1.2.1/.gitattributes b/internal/libwebp-1.2.1/.gitattributes new file mode 100644 index 0000000..fa5a76c --- /dev/null +++ b/internal/libwebp-1.2.1/.gitattributes @@ -0,0 +1,6 @@ +.gitattributes export-ignore +.gitignore export-ignore +.mailmap export-ignore +*.bat text eol=crlf +*.pdf -text -diff +*.ppm -text -diff diff --git a/internal/libwebp-1.2.1/.gitignore b/internal/libwebp-1.2.1/.gitignore new file mode 100644 index 0000000..f74e51d --- /dev/null +++ b/internal/libwebp-1.2.1/.gitignore @@ -0,0 +1,54 @@ +*.l[ao] +*.[ao] +*.pc +.DS_Store +.deps +.libs +/aclocal.m4 +/ar-lib +/autom4te.cache +/compile +/config.* +/configure +/depcomp +/dist +/install-sh +/libtool +/ltmain.sh +/missing +/mkinstalldirs +/stamp-h1 +Makefile +Makefile.in +examples/anim_diff +examples/anim_dump +examples/[cdv]webp +examples/gif2webp +examples/img2webp +examples/webpinfo +examples/webpmux +src/webp/config.h* +src/webp/stamp-h1 +/output +/doc/output +*.idb +*.pdb +/iosbuild +/xcframeworkbuild +/WebP*.*framework +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +.gradle +/build +extras/get_disto +extras/vwebp_sdl +extras/webp_quality +tests/fuzzer/advanced_api_fuzzer +tests/fuzzer/animation_api_fuzzer +tests/fuzzer/animdecoder_fuzzer +tests/fuzzer/animencoder_fuzzer +tests/fuzzer/demux_api_fuzzer +tests/fuzzer/enc_dec_fuzzer +tests/fuzzer/mux_demux_api_fuzzer +tests/fuzzer/simple_api_fuzzer diff --git a/internal/libwebp-1.2.1/.mailmap b/internal/libwebp-1.2.1/.mailmap new file mode 100644 index 0000000..f3560c5 --- /dev/null +++ b/internal/libwebp-1.2.1/.mailmap @@ -0,0 +1,14 @@ +Johann Koenig +Johann Koenig +MikoĊ‚aj Zalewski +Pascal Massimino +Pascal Massimino +Vikas Arora + + + + +Tamar Levy + +Hui Su +James Zern diff --git a/internal/libwebp-1.2.1/.pylintrc b/internal/libwebp-1.2.1/.pylintrc new file mode 100644 index 0000000..4658b84 --- /dev/null +++ b/internal/libwebp-1.2.1/.pylintrc @@ -0,0 +1,441 @@ +# This Pylint rcfile contains a best-effort configuration to uphold the +# best-practices and style described in the Google Python style guide: +# https://google.github.io/styleguide/pyguide.html +# +# Its canonical open-source location is: +# https://google.github.io/styleguide/pylintrc + +[MASTER] + +# Files or directories to be skipped. They should be base names, not paths. +ignore=third_party + +# Files or directories matching the regex patterns are skipped. The regex +# matches against base names, not paths. +ignore-patterns= + +# Pickle collected data for later comparisons. +persistent=no + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=4 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=abstract-method, + apply-builtin, + arguments-differ, + attribute-defined-outside-init, + backtick, + bad-option-value, + basestring-builtin, + buffer-builtin, + c-extension-no-member, + consider-using-enumerate, + cmp-builtin, + cmp-method, + coerce-builtin, + coerce-method, + delslice-method, + div-method, + duplicate-code, + eq-without-hash, + execfile-builtin, + file-builtin, + filter-builtin-not-iterating, + fixme, + getslice-method, + global-statement, + hex-method, + idiv-method, + implicit-str-concat-in-sequence, + import-error, + import-self, + import-star-module-level, + inconsistent-return-statements, + input-builtin, + intern-builtin, + invalid-str-codec, + locally-disabled, + long-builtin, + long-suffix, + map-builtin-not-iterating, + misplaced-comparison-constant, + missing-function-docstring, + metaclass-assignment, + next-method-called, + next-method-defined, + no-absolute-import, + no-else-break, + no-else-continue, + no-else-raise, + no-else-return, + no-init, # added + no-member, + no-name-in-module, + no-self-use, + nonzero-method, + oct-method, + old-division, + old-ne-operator, + old-octal-literal, + old-raise-syntax, + parameter-unpacking, + print-statement, + raising-string, + range-builtin-not-iterating, + raw_input-builtin, + rdiv-method, + reduce-builtin, + relative-import, + reload-builtin, + round-builtin, + setslice-method, + signature-differs, + standarderror-builtin, + suppressed-message, + sys-max-int, + too-few-public-methods, + too-many-ancestors, + too-many-arguments, + too-many-boolean-expressions, + too-many-branches, + too-many-instance-attributes, + too-many-locals, + too-many-nested-blocks, + too-many-public-methods, + too-many-return-statements, + too-many-statements, + trailing-newlines, + unichr-builtin, + unicode-builtin, + unnecessary-pass, + unpacking-in-except, + useless-else-on-loop, + useless-object-inheritance, + useless-suppression, + using-cmp-argument, + wrong-import-order, + xrange-builtin, + zip-builtin-not-iterating, + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". This option is deprecated +# and it will be removed in Pylint 2.0. +files-output=no + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +good-names=main,_,PRESUBMIT + +# Bad variable names which should always be refused, separated by a comma +bad-names= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl + +# Regular expression matching correct function names +function-rgx=^(?:(?PsetUp|tearDown|setUpModule|tearDownModule)|(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct constant names +const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ + +# Regular expression matching correct attribute names +attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ + +# Regular expression matching correct argument names +argument-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=^_?[A-Z][a-zA-Z0-9]*$ + +# Regular expression matching correct module names +module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$ + +# Regular expression matching correct method names +method-rgx=(?x)^(?:(?P_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P_{0,2}[a-z][a-z0-9_]*))$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=10 + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=80 + +# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt +# lines made too long by directives to pytype. + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=(?x)( + ^\s*(\#\ )??$| + ^\s*(from\s+\S+\s+)?import\s+.+$) + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=yes + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check= + +# Maximum number of lines in a module +max-module-lines=99999 + +# String used as indentation unit. The internal Google style guide mandates 2 +# spaces. Google's externaly-published style guide says 4, consistent with +# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google +# projects (like TensorFlow). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=TODO + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=yes + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging,absl.logging,tensorflow.io.logging + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub, + TERMIOS, + Bastion, + rexec, + sets + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant, absl + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls, + class_ + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=StandardError, + Exception, + BaseException diff --git a/internal/libwebp-1.2.1/.style.yapf b/internal/libwebp-1.2.1/.style.yapf new file mode 100644 index 0000000..47ca4cc --- /dev/null +++ b/internal/libwebp-1.2.1/.style.yapf @@ -0,0 +1,2 @@ +[style] +based_on_style = chromium \ No newline at end of file diff --git a/internal/libwebp-1.2.1/AUTHORS b/internal/libwebp-1.2.1/AUTHORS new file mode 100644 index 0000000..30abde0 --- /dev/null +++ b/internal/libwebp-1.2.1/AUTHORS @@ -0,0 +1,51 @@ +Contributors: +- Aidan O'Loan (aidanol at gmail dot com) +- Alan Browning (browning at google dot com) +- Charles Munger (clm at google dot com) +- Cheng Yi (cyi at google dot com) +- Christian Duvivier (cduvivier at google dot com) +- Christopher Degawa (ccom at randomderp dot com) +- Clement Courbet (courbet at google dot com) +- Djordje Pesut (djordje dot pesut at imgtec dot com) +- Hui Su (huisu at google dot com) +- Ilya Kurdyukov (jpegqs at gmail dot com) +- Ingvar Stepanyan (rreverser at google dot com) +- James Zern (jzern at google dot com) +- Jan Engelhardt (jengelh at medozas dot de) +- Jehan (jehan at girinstud dot io) +- Johann Koenig (johann dot koenig at duck dot com) +- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com) +- Jyrki Alakuijala (jyrki at google dot com) +- Konstantin Ivlev (tomskside at gmail dot com) +- Lode Vandevenne (lode at google dot com) +- Lou Quillio (louquillio at google dot com) +- Mans Rullgard (mans at mansr dot com) +- Marcin Kowalczyk (qrczak at google dot com) +- Martin Olsson (mnemo at minimum dot se) +- MikoĊ‚aj Zalewski (mikolajz at google dot com) +- Mislav Bradac (mislavm at google dot com) +- Nico Weber (thakis at chromium dot org) +- Noel Chromium (noel at chromium dot org) +- Oliver Wolff (oliver dot wolff at qt dot io) +- Owen Rodley (orodley at google dot com) +- Parag Salasakar (img dot mips1 at gmail dot com) +- Pascal Massimino (pascal dot massimino at gmail dot com) +- PaweĊ‚ Hajdan, Jr (phajdan dot jr at chromium dot org) +- Pierre Joye (pierre dot php at gmail dot com) +- Sam Clegg (sbc at chromium dot org) +- Scott Hancher (seh at google dot com) +- Scott LaVarnway (slavarnway at google dot com) +- Scott Talbot (s at chikachow dot org) +- Slobodan Prijic (slobodan dot prijic at imgtec dot com) +- Somnath Banerjee (somnath dot banerjee at gmail dot com) +- Sriraman Tallam (tmsriram at google dot com) +- Tamar Levy (tamar dot levy at intel dot com) +- Timothy Gu (timothygu99 at gmail dot com) +- Urvang Joshi (urvang at google dot com) +- Vikas Arora (vikasa at google dot com) +- Vincent Rabaud (vrabaud at google dot com) +- Vlad Tsyrklevich (vtsyrklevich at chromium dot org) +- Wan-Teh Chang (wtc at google dot com) +- Yang Zhang (yang dot zhang at arm dot com) +- Yannis Guyon (yguyon at google dot com) +- Zhi An Ng (zhin at chromium dot org) diff --git a/internal/libwebp-1.2.1/Android.mk b/internal/libwebp-1.2.1/Android.mk new file mode 100644 index 0000000..4ae099c --- /dev/null +++ b/internal/libwebp-1.2.1/Android.mk @@ -0,0 +1,281 @@ +LOCAL_PATH := $(call my-dir) + +WEBP_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD +WEBP_CFLAGS += -fvisibility=hidden + +ifeq ($(APP_OPTIM),release) + WEBP_CFLAGS += -finline-functions -ffast-math \ + -ffunction-sections -fdata-sections + ifeq ($(findstring clang,$(NDK_TOOLCHAIN_VERSION)),) + WEBP_CFLAGS += -frename-registers -s + endif +endif + +# mips32 fails to build with clang from r14b +# https://bugs.chromium.org/p/webp/issues/detail?id=343 +ifeq ($(findstring clang,$(NDK_TOOLCHAIN_VERSION)),clang) + ifeq ($(TARGET_ARCH),mips) + clang_version := $(shell $(TARGET_CC) --version) + ifneq ($(findstring clang version 3,$(clang_version)),) + WEBP_CFLAGS += -no-integrated-as + endif + endif +endif + +ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),) + # Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal + # instructions to be generated for armv7a code. Instead target the neon code + # specifically. + NEON := c.neon + USE_CPUFEATURES := yes + WEBP_CFLAGS += -DHAVE_CPU_FEATURES_H +else + NEON := c +endif + +dec_srcs := \ + src/dec/alpha_dec.c \ + src/dec/buffer_dec.c \ + src/dec/frame_dec.c \ + src/dec/idec_dec.c \ + src/dec/io_dec.c \ + src/dec/quant_dec.c \ + src/dec/tree_dec.c \ + src/dec/vp8_dec.c \ + src/dec/vp8l_dec.c \ + src/dec/webp_dec.c \ + +demux_srcs := \ + src/demux/anim_decode.c \ + src/demux/demux.c \ + +dsp_dec_srcs := \ + src/dsp/alpha_processing.c \ + src/dsp/alpha_processing_mips_dsp_r2.c \ + src/dsp/alpha_processing_neon.$(NEON) \ + src/dsp/alpha_processing_sse2.c \ + src/dsp/alpha_processing_sse41.c \ + src/dsp/cpu.c \ + src/dsp/dec.c \ + src/dsp/dec_clip_tables.c \ + src/dsp/dec_mips32.c \ + src/dsp/dec_mips_dsp_r2.c \ + src/dsp/dec_msa.c \ + src/dsp/dec_neon.$(NEON) \ + src/dsp/dec_sse2.c \ + src/dsp/dec_sse41.c \ + src/dsp/filters.c \ + src/dsp/filters_mips_dsp_r2.c \ + src/dsp/filters_msa.c \ + src/dsp/filters_neon.$(NEON) \ + src/dsp/filters_sse2.c \ + src/dsp/lossless.c \ + src/dsp/lossless_mips_dsp_r2.c \ + src/dsp/lossless_msa.c \ + src/dsp/lossless_neon.$(NEON) \ + src/dsp/lossless_sse2.c \ + src/dsp/lossless_sse41.c \ + src/dsp/rescaler.c \ + src/dsp/rescaler_mips32.c \ + src/dsp/rescaler_mips_dsp_r2.c \ + src/dsp/rescaler_msa.c \ + src/dsp/rescaler_neon.$(NEON) \ + src/dsp/rescaler_sse2.c \ + src/dsp/upsampling.c \ + src/dsp/upsampling_mips_dsp_r2.c \ + src/dsp/upsampling_msa.c \ + src/dsp/upsampling_neon.$(NEON) \ + src/dsp/upsampling_sse2.c \ + src/dsp/upsampling_sse41.c \ + src/dsp/yuv.c \ + src/dsp/yuv_mips32.c \ + src/dsp/yuv_mips_dsp_r2.c \ + src/dsp/yuv_neon.$(NEON) \ + src/dsp/yuv_sse2.c \ + src/dsp/yuv_sse41.c \ + +dsp_enc_srcs := \ + src/dsp/cost.c \ + src/dsp/cost_mips32.c \ + src/dsp/cost_mips_dsp_r2.c \ + src/dsp/cost_neon.$(NEON) \ + src/dsp/cost_sse2.c \ + src/dsp/enc.c \ + src/dsp/enc_mips32.c \ + src/dsp/enc_mips_dsp_r2.c \ + src/dsp/enc_msa.c \ + src/dsp/enc_neon.$(NEON) \ + src/dsp/enc_sse2.c \ + src/dsp/enc_sse41.c \ + src/dsp/lossless_enc.c \ + src/dsp/lossless_enc_mips32.c \ + src/dsp/lossless_enc_mips_dsp_r2.c \ + src/dsp/lossless_enc_msa.c \ + src/dsp/lossless_enc_neon.$(NEON) \ + src/dsp/lossless_enc_sse2.c \ + src/dsp/lossless_enc_sse41.c \ + src/dsp/ssim.c \ + src/dsp/ssim_sse2.c \ + +enc_srcs := \ + src/enc/alpha_enc.c \ + src/enc/analysis_enc.c \ + src/enc/backward_references_cost_enc.c \ + src/enc/backward_references_enc.c \ + src/enc/config_enc.c \ + src/enc/cost_enc.c \ + src/enc/filter_enc.c \ + src/enc/frame_enc.c \ + src/enc/histogram_enc.c \ + src/enc/iterator_enc.c \ + src/enc/near_lossless_enc.c \ + src/enc/picture_enc.c \ + src/enc/picture_csp_enc.c \ + src/enc/picture_psnr_enc.c \ + src/enc/picture_rescale_enc.c \ + src/enc/picture_tools_enc.c \ + src/enc/predictor_enc.c \ + src/enc/quant_enc.c \ + src/enc/syntax_enc.c \ + src/enc/token_enc.c \ + src/enc/tree_enc.c \ + src/enc/vp8l_enc.c \ + src/enc/webp_enc.c \ + +mux_srcs := \ + src/mux/anim_encode.c \ + src/mux/muxedit.c \ + src/mux/muxinternal.c \ + src/mux/muxread.c \ + +utils_dec_srcs := \ + src/utils/bit_reader_utils.c \ + src/utils/color_cache_utils.c \ + src/utils/filters_utils.c \ + src/utils/huffman_utils.c \ + src/utils/quant_levels_dec_utils.c \ + src/utils/random_utils.c \ + src/utils/rescaler_utils.c \ + src/utils/thread_utils.c \ + src/utils/utils.c \ + +utils_enc_srcs := \ + src/utils/bit_writer_utils.c \ + src/utils/huffman_encode_utils.c \ + src/utils/quant_levels_utils.c \ + +################################################################################ +# libwebpdecoder + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(dec_srcs) \ + $(dsp_dec_srcs) \ + $(utils_dec_srcs) \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src + +# prefer arm over thumb mode for performance gains +LOCAL_ARM_MODE := arm + +ifeq ($(USE_CPUFEATURES),yes) + LOCAL_STATIC_LIBRARIES := cpufeatures +endif + +LOCAL_MODULE := webpdecoder_static + +include $(BUILD_STATIC_LIBRARY) + +ifeq ($(ENABLE_SHARED),1) +include $(CLEAR_VARS) + +LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static + +LOCAL_MODULE := webpdecoder + +include $(BUILD_SHARED_LIBRARY) +endif # ENABLE_SHARED=1 + +################################################################################ +# libwebp + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(dsp_enc_srcs) \ + $(enc_srcs) \ + $(utils_enc_srcs) \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src + +# prefer arm over thumb mode for performance gains +LOCAL_ARM_MODE := arm + +LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static + +LOCAL_MODULE := webp + +ifeq ($(ENABLE_SHARED),1) + include $(BUILD_SHARED_LIBRARY) +else + include $(BUILD_STATIC_LIBRARY) +endif + +################################################################################ +# libwebpdemux + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(demux_srcs) + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src + +# prefer arm over thumb mode for performance gains +LOCAL_ARM_MODE := arm + +LOCAL_MODULE := webpdemux + +ifeq ($(ENABLE_SHARED),1) + LOCAL_SHARED_LIBRARIES := webp + include $(BUILD_SHARED_LIBRARY) +else + LOCAL_STATIC_LIBRARIES := webp + include $(BUILD_STATIC_LIBRARY) +endif + +################################################################################ +# libwebpmux + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(mux_srcs) + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src + +# prefer arm over thumb mode for performance gains +LOCAL_ARM_MODE := arm + +LOCAL_MODULE := webpmux + +ifeq ($(ENABLE_SHARED),1) + LOCAL_SHARED_LIBRARIES := webp + include $(BUILD_SHARED_LIBRARY) +else + LOCAL_STATIC_LIBRARIES := webp + include $(BUILD_STATIC_LIBRARY) +endif + +################################################################################ + +WEBP_SRC_PATH := $(LOCAL_PATH) +include $(WEBP_SRC_PATH)/imageio/Android.mk +include $(WEBP_SRC_PATH)/examples/Android.mk + +ifeq ($(USE_CPUFEATURES),yes) + $(call import-module,android/cpufeatures) +endif diff --git a/internal/libwebp-1.2.1/CMakeLists.txt b/internal/libwebp-1.2.1/CMakeLists.txt new file mode 100644 index 0000000..3761965 --- /dev/null +++ b/internal/libwebp-1.2.1/CMakeLists.txt @@ -0,0 +1,732 @@ +# Copyright (c) 2020 Google LLC. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE 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. + +cmake_minimum_required(VERSION 3.7) + +if(POLICY CMP0072) + cmake_policy(SET CMP0072 NEW) +endif() + +project(WebP C) + +# Options for coder / decoder executables. +if(NOT EMSCRIPTEN) + # Disable SIMD on Emscripten by default, as it's a new unstable Wasm feature. + # Users can still explicitly opt-in to make a SIMD-enabled build. + set(WEBP_ENABLE_SIMD_DEFAULT ON) +endif() +option(WEBP_ENABLE_SIMD "Enable any SIMD optimization." + ${WEBP_ENABLE_SIMD_DEFAULT}) +option(WEBP_BUILD_ANIM_UTILS "Build animation utilities." ON) +option(WEBP_BUILD_CWEBP "Build the cwebp command line tool." ON) +option(WEBP_BUILD_DWEBP "Build the dwebp command line tool." ON) +option(WEBP_BUILD_GIF2WEBP "Build the gif2webp conversion tool." ON) +option(WEBP_BUILD_IMG2WEBP "Build the img2webp animation tool." ON) +option(WEBP_BUILD_VWEBP "Build the vwebp viewer tool." ON) +option(WEBP_BUILD_WEBPINFO "Build the webpinfo command line tool." ON) +option(WEBP_BUILD_LIBWEBPMUX "Build the libwebpmux library." ON) +option(WEBP_BUILD_WEBPMUX "Build the webpmux command line tool." ON) +option(WEBP_BUILD_EXTRAS "Build extras." ON) +option(WEBP_BUILD_WEBP_JS "Emscripten build of webp.js." OFF) +option(WEBP_USE_THREAD "Enable threading support" ON) +option(WEBP_NEAR_LOSSLESS "Enable near-lossless encoding" ON) +option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces." + OFF) +set(WEBP_BITTRACE "0" CACHE STRING "Bit trace mode (0=none, 1=bit, 2=bytes)") +set_property(CACHE WEBP_BITTRACE PROPERTY STRINGS 0 1 2) + +# Option needed for handling Unicode file names on Windows. +if(WIN32) + option(WEBP_UNICODE "Build Unicode executables." ON) +endif() + +if(WEBP_BUILD_WEBP_JS) + set(WEBP_BUILD_ANIM_UTILS OFF) + set(WEBP_BUILD_CWEBP OFF) + set(WEBP_BUILD_DWEBP OFF) + set(WEBP_BUILD_GIF2WEBP OFF) + set(WEBP_BUILD_IMG2WEBP OFF) + set(WEBP_BUILD_VWEBP OFF) + set(WEBP_BUILD_WEBPINFO OFF) + set(WEBP_BUILD_WEBPMUX OFF) + set(WEBP_BUILD_EXTRAS OFF) + set(WEBP_USE_THREAD OFF) + + if(WEBP_ENABLE_SIMD) + message("wasm2js does not support SIMD, disabling webp.js generation.") + endif() +endif() + +set(WEBP_DEP_LIBRARIES) +set(WEBP_DEP_INCLUDE_DIRS) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" + CACHE STRING "Build type: Release, Debug, MinSizeRel or RelWithDebInfo" + FORCE) +endif() + +# Include dependencies. +include(cmake/deps.cmake) +include(GNUInstallDirs) + +# ############################################################################## +# Options. +if(WEBP_ENABLE_SWAP_16BIT_CSP) + add_definitions(-DWEBP_SWAP_16BIT_CSP=1) +endif() + +if(NOT WEBP_BITTRACE STREQUAL "0") + add_definitions(-DBITTRACE=${WEBP_BITTRACE}) +endif() + +if(WEBP_UNICODE) + # Windows recommends setting both UNICODE and _UNICODE. + add_definitions(-DUNICODE -D_UNICODE) +endif() + +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix "\$\{prefix\}") +set(libdir "\$\{prefix\}/lib") +set(includedir "\$\{prefix\}/include") +set(PTHREAD_LIBS ${CMAKE_THREAD_LIBS_INIT}) +set(INSTALLED_LIBRARIES) + +# ############################################################################## +# Android only. +if(ANDROID) + include_directories(${ANDROID_NDK}/sources/android/cpufeatures) + add_library(cpufeatures STATIC + ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c) + list(APPEND INSTALLED_LIBRARIES cpufeatures) + target_link_libraries(cpufeatures dl) + set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} cpufeatures) + set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS} + ${ANDROID_NDK}/sources/android/cpufeatures) + add_definitions(-DHAVE_CPU_FEATURES_H=1) + set(HAVE_CPU_FEATURES_H 1) +else() + set(HAVE_CPU_FEATURES_H 0) +endif() + +function(configure_pkg_config FILE) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${FILE}.in" + "${CMAKE_CURRENT_BINARY_DIR}/${FILE}") + + if(HAVE_MATH_LIBRARY) + # MSVC doesn't have libm + file(READ ${CMAKE_CURRENT_BINARY_DIR}/${FILE} data) + string(REPLACE "-lm" "" data ${data}) + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${FILE} ${data}) + endif() + + install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/${FILE}" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + ) +endfunction() + +# ############################################################################## +# WebP source files. Read the Makefile.am to get the source files. + +# We expect the Makefiles to define the sources as defined in the first regex. +# E.g.: libimagedec_la_SOURCES = image_dec.c image_dec.h +function(parse_Makefile_am FOLDER VAR SRC_REGEX) + file(READ ${FOLDER}/Makefile.am MAKEFILE_AM) + string(REGEX MATCHALL + "${SRC_REGEX}_SOURCES[ ]*\\+?=[ ]+[0-9a-z\\._ ]*" + FILES_PER_LINE + ${MAKEFILE_AM}) + set(SRCS ${${VAR}}) + foreach(FILES ${FILES_PER_LINE}) + string(FIND ${FILES} "=" OFFSET) + math(EXPR OFFSET "${OFFSET} + 2") + string(SUBSTRING ${FILES} + ${OFFSET} + -1 + FILES) + if(FILES) + string(REGEX MATCHALL + "[0-9a-z\\._]+" + FILES + ${FILES}) + foreach(FILE ${FILES}) + list(APPEND SRCS ${FOLDER}/${FILE}) + endforeach() + endif() + endforeach() + set(${VAR} ${SRCS} PARENT_SCOPE) +endfunction() + +set(WEBP_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) +parse_makefile_am(${WEBP_SRC_DIR}/dec "WEBP_DEC_SRCS" "") +parse_makefile_am(${WEBP_SRC_DIR}/demux "WEBP_DEMUX_SRCS" "") +parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_COMMON_SRCS" "COMMON") +parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_ENC_SRCS" "ENC") +parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_ENC_SRCS" "dsp_[^ ]*") +parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_DEC_SRCS" "decode_[^ ]*") +parse_makefile_am(${WEBP_SRC_DIR}/enc "WEBP_ENC_SRCS" "") +parse_makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_COMMON_SRCS" "COMMON") +parse_makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_ENC_SRCS" "ENC") +parse_makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_DEC_SRCS" "decode_[^ ]*") + +# Remove the files specific to SIMD we don't use. +foreach(FILE ${WEBP_SIMD_FILES_NOT_TO_INCLUDE}) + list(REMOVE_ITEM WEBP_DSP_ENC_SRCS ${FILE}) + list(REMOVE_ITEM WEBP_DSP_DEC_SRCS ${FILE}) +endforeach() + +# Generate the config.h file. +configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/src/webp/config.h) +add_definitions(-DHAVE_CONFIG_H) + +# ############################################################################## +# Build the webpdecoder library. + +# Creates a source file with an unused stub function in $CMAKE_BINARY_DIR and +# adds it to the specified target. Currently used only with Xcode. +# +# See also: +# https://cmake.org/cmake/help/v3.18/command/add_library.html#object-libraries +# "Some native build systems (such as Xcode) may not like targets that have +# only object files, so consider adding at least one real source file to any +# target that references $." +function(libwebp_add_stub_file TARGET) + set(stub_source_dir "${CMAKE_BINARY_DIR}") + set(stub_source_file + "${stub_source_dir}/libwebp_${TARGET}_stub.c") + set(stub_source_code + "// Generated file. DO NOT EDIT!\n" + "// C source file created for target ${TARGET}.\n" + "void libwebp_${TARGET}_stub_function(void)\;\n" + "void libwebp_${TARGET}_stub_function(void) {}\n") + file(WRITE "${stub_source_file}" ${stub_source_code}) + + target_sources(${TARGET} PRIVATE ${stub_source_file}) +endfunction() + +if(MSVC) + # avoid security warnings for e.g., fopen() used in the examples. + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +else() + add_definitions(-Wall) +endif() +include_directories(${WEBP_DEP_INCLUDE_DIRS}) +add_library(webpdecode OBJECT ${WEBP_DEC_SRCS}) +target_include_directories(webpdecode + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(webpdspdecode OBJECT ${WEBP_DSP_COMMON_SRCS} ${WEBP_DSP_DEC_SRCS}) +target_include_directories(webpdspdecode + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(webputilsdecode + OBJECT + ${WEBP_UTILS_COMMON_SRCS} + ${WEBP_UTILS_DEC_SRCS}) +target_include_directories(webputilsdecode + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(webpdecoder + $ + $ + $) +if(XCODE) + libwebp_add_stub_file(webpdecoder) +endif() +target_link_libraries(webpdecoder ${WEBP_DEP_LIBRARIES}) +target_include_directories( + webpdecoder + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} + INTERFACE $ + $) +set_target_properties( + webpdecoder + PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\ +${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h") + +configure_pkg_config("src/libwebpdecoder.pc") + +# Build the webp library. +add_library(webpencode OBJECT ${WEBP_ENC_SRCS}) +target_include_directories(webpencode + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(webpdsp + OBJECT + ${WEBP_DSP_COMMON_SRCS} + ${WEBP_DSP_DEC_SRCS} + ${WEBP_DSP_ENC_SRCS}) +target_include_directories(webpdsp + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(webputils + OBJECT + ${WEBP_UTILS_COMMON_SRCS} + ${WEBP_UTILS_DEC_SRCS} + ${WEBP_UTILS_ENC_SRCS}) +target_include_directories(webputils + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(webp + $ + $ + $ + $) +if(XCODE) + libwebp_add_stub_file(webp) +endif() +target_link_libraries(webp ${WEBP_DEP_LIBRARIES}) +target_include_directories( + webp + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + PUBLIC $ + $) +set_target_properties( + webp + PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\ +${CMAKE_CURRENT_SOURCE_DIR}/src/webp/encode.h;\ +${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h") + +# Make sure the OBJECT libraries are built with position independent code (it is +# not ON by default). +set_target_properties(webpdecode + webpdspdecode + webputilsdecode + webpencode + webpdsp + webputils + PROPERTIES POSITION_INDEPENDENT_CODE ON) +configure_pkg_config("src/libwebp.pc") + +# Build the webp demux library. +add_library(webpdemux ${WEBP_DEMUX_SRCS}) +target_link_libraries(webpdemux webp) +target_include_directories(webpdemux + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + PUBLIC $) +set_target_properties( + webpdemux + PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\ +${CMAKE_CURRENT_SOURCE_DIR}/src/webp/demux.h;\ +${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux_types.h;\ +${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h") + +configure_pkg_config("src/demux/libwebpdemux.pc") + +# Set the version numbers. +macro(set_version FILE TARGET_NAME NAME_IN_MAKEFILE) + file(READ ${CMAKE_CURRENT_SOURCE_DIR}/src/${FILE} SOURCE_FILE) + string(REGEX MATCH + "${NAME_IN_MAKEFILE}_la_LDFLAGS[^\n]* -version-info [0-9:]+" + TMP + ${SOURCE_FILE}) + string(REGEX MATCH + "[0-9:]+" + TMP + ${TMP}) + string(REGEX + REPLACE ":" + " " + LT_VERSION + ${TMP}) + + # See the libtool docs for more information: + # https://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info + # + # c=, a=, r= + # + # libtool generates a .so file as .so.[c-a].a.r, while -version-info c:r:a is + # passed to libtool. + # + # We set FULL = [c-a].a.r and MAJOR = [c-a]. + separate_arguments(LT_VERSION) + list(GET LT_VERSION 0 LT_CURRENT) + list(GET LT_VERSION 1 LT_REVISION) + list(GET LT_VERSION 2 LT_AGE) + math(EXPR LT_CURRENT_MINUS_AGE "${LT_CURRENT} - ${LT_AGE}") + + set_target_properties( + ${TARGET_NAME} + PROPERTIES VERSION + ${LT_CURRENT_MINUS_AGE}.${LT_AGE}.${LT_REVISION} + SOVERSION + ${LT_CURRENT_MINUS_AGE}) +endmacro() +set_version(Makefile.am webp webp) +set_version(Makefile.am webpdecoder webpdecoder) +set_version(demux/Makefile.am webpdemux webpdemux) +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_FILE) +string(REGEX MATCH + "AC_INIT\\([^\n]*\\[[0-9\\.]+\\]" + TMP + ${CONFIGURE_FILE}) +string(REGEX MATCH + "[0-9\\.]+" + PROJECT_VERSION + ${TMP}) + +# Define the libraries to install. +list(APPEND INSTALLED_LIBRARIES webpdecoder webp webpdemux) + +# Deal with SIMD. Change the compile flags for SIMD files we use. +list(LENGTH WEBP_SIMD_FILES_TO_INCLUDE WEBP_SIMD_FILES_TO_INCLUDE_LENGTH) +math(EXPR WEBP_SIMD_FILES_TO_INCLUDE_RANGE + "${WEBP_SIMD_FILES_TO_INCLUDE_LENGTH}-1") + +foreach(I_FILE RANGE ${WEBP_SIMD_FILES_TO_INCLUDE_RANGE}) + list(GET WEBP_SIMD_FILES_TO_INCLUDE ${I_FILE} FILE) + list(GET WEBP_SIMD_FLAGS_TO_INCLUDE ${I_FILE} SIMD_COMPILE_FLAG) + set_source_files_properties(${FILE} + PROPERTIES + COMPILE_FLAGS + ${SIMD_COMPILE_FLAG}) +endforeach() + +if(NOT WEBP_BUILD_LIBWEBPMUX) + set(WEBP_BUILD_GIF2WEBP OFF) + set(WEBP_BUILD_IMG2WEBP OFF) + set(WEBP_BUILD_WEBPMUX OFF) +endif() + +if(WEBP_BUILD_GIF2WEBP AND NOT GIF_FOUND) + set(WEBP_BUILD_GIF2WEBP OFF) +endif() + +if(WEBP_BUILD_ANIM_UTILS AND NOT GIF_FOUND) + set(WEBP_BUILD_ANIM_UTILS OFF) +endif() + +# Build the executables if asked for. +if(WEBP_BUILD_ANIM_UTILS + OR WEBP_BUILD_CWEBP + OR WEBP_BUILD_DWEBP + OR WEBP_BUILD_GIF2WEBP + OR WEBP_BUILD_IMG2WEBP + OR WEBP_BUILD_VWEBP) + # Example utility library. + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "EXAMPLEUTIL_SRCS" + "example_util_[^ ]*") + list(APPEND EXAMPLEUTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h) + add_library(exampleutil STATIC ${EXAMPLEUTIL_SRCS}) + target_link_libraries(exampleutil imageioutil) + target_include_directories( + exampleutil + PUBLIC $) + + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEIOUTILS_SRCS" + "imageio_util_[^ ]*") + add_library(imageioutil STATIC ${IMAGEIOUTILS_SRCS}) + target_link_libraries(imageioutil webp) + + # Image-decoding utility library. + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEDEC_SRCS" + "imagedec_[^ ]*") + add_library(imagedec STATIC ${IMAGEDEC_SRCS}) + target_link_libraries(imagedec + imageioutil + webpdemux + webp + ${WEBP_DEP_IMG_LIBRARIES}) + + # Image-encoding utility library. + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEENC_SRCS" + "imageenc_[^ ]*") + add_library(imageenc STATIC ${IMAGEENC_SRCS}) + target_link_libraries(imageenc imageioutil webp) + + set_property(TARGET exampleutil + imageioutil + imagedec + imageenc + PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR}/src) +endif() + +if(WEBP_BUILD_DWEBP) + # dwebp + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "DWEBP_SRCS" "dwebp") + add_executable(dwebp ${DWEBP_SRCS}) + target_link_libraries(dwebp exampleutil imagedec imageenc) + target_include_directories(dwebp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) + install(TARGETS dwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() + +if(WEBP_BUILD_CWEBP) + # cwebp + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "CWEBP_SRCS" "cwebp") + add_executable(cwebp ${CWEBP_SRCS}) + target_link_libraries(cwebp exampleutil imagedec webp) + target_include_directories(cwebp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) + install(TARGETS cwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() + +if(WEBP_BUILD_LIBWEBPMUX) + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/mux "WEBP_MUX_SRCS" "") + add_library(libwebpmux ${WEBP_MUX_SRCS}) + target_link_libraries(libwebpmux webp) + target_include_directories(libwebpmux + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}) + set_version(mux/Makefile.am libwebpmux webpmux) + set_target_properties(libwebpmux + PROPERTIES PUBLIC_HEADER + "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux.h;\ +${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux_types.h;\ +${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h;") + set_target_properties(libwebpmux PROPERTIES OUTPUT_NAME webpmux) + list(APPEND INSTALLED_LIBRARIES libwebpmux) + configure_pkg_config("src/mux/libwebpmux.pc") +endif() + +if(WEBP_BUILD_GIF2WEBP) + # gif2webp + include_directories(${WEBP_DEP_GIF_INCLUDE_DIRS}) + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "GIF2WEBP_SRCS" + "gif2webp") + add_executable(gif2webp ${GIF2WEBP_SRCS}) + target_link_libraries(gif2webp + exampleutil + imageioutil + webp + libwebpmux + ${WEBP_DEP_GIF_LIBRARIES}) + target_include_directories(gif2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) + install(TARGETS gif2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() + +if(WEBP_BUILD_IMG2WEBP) + # img2webp + include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS}) + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "IMG2WEBP_SRCS" + "img2webp") + add_executable(img2webp ${IMG2WEBP_SRCS}) + target_link_libraries(img2webp + exampleutil + imagedec + imageioutil + webp + libwebpmux) + target_include_directories(img2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) + install(TARGETS img2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() + +if(WEBP_BUILD_VWEBP) + # vwebp + find_package(GLUT) + if(GLUT_FOUND) + include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS}) + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "VWEBP_SRCS" "vwebp") + add_executable(vwebp ${VWEBP_SRCS}) + target_link_libraries(vwebp + ${OPENGL_LIBRARIES} + exampleutil + ${GLUT_glut_LIBRARY} + imageioutil + webp + webpdemux) + target_include_directories(vwebp + PRIVATE ${GLUT_INCLUDE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/src + ${OPENGL_INCLUDE_DIR}) + install(TARGETS vwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + check_c_compiler_flag("-Wno-deprecated-declarations" HAS_NO_DEPRECATED) + if(HAS_NO_DEPRECATED) + target_compile_options(vwebp PRIVATE "-Wno-deprecated-declarations") + endif() + endif() + endif() +endif() + +if(WEBP_BUILD_WEBPINFO) + # webpinfo + include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS}) + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "WEBPINFO_SRCS" + "webpinfo") + add_executable(webpinfo ${WEBPINFO_SRCS}) + target_link_libraries(webpinfo exampleutil imageioutil) + target_include_directories(webpinfo PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) + install(TARGETS webpinfo RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() + +if(WEBP_BUILD_WEBPMUX) + # webpmux + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "WEBPMUX_SRCS" + "webpmux") + add_executable(webpmux ${WEBPMUX_SRCS}) + target_link_libraries(webpmux exampleutil imageioutil libwebpmux webp) + target_include_directories(webpmux PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) + install(TARGETS webpmux RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() + +if(WEBP_BUILD_EXTRAS) + set(EXTRAS_MAKEFILE "${CMAKE_CURRENT_SOURCE_DIR}/extras") + parse_makefile_am(${EXTRAS_MAKEFILE} "WEBP_EXTRAS_SRCS" "libwebpextras_la") + parse_makefile_am(${EXTRAS_MAKEFILE} "GET_DISTO_SRCS" "get_disto") + parse_makefile_am(${EXTRAS_MAKEFILE} "WEBP_QUALITY_SRCS" "webp_quality") + parse_makefile_am(${EXTRAS_MAKEFILE} "VWEBP_SDL_SRCS" "vwebp_sdl") + + # get_disto + add_executable(get_disto ${GET_DISTO_SRCS}) + target_link_libraries(get_disto imagedec) + target_include_directories(get_disto + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/src) + + # webp_quality + add_executable(webp_quality ${WEBP_QUALITY_SRCS} ${WEBP_EXTRAS_SRCS}) + target_link_libraries(webp_quality exampleutil imagedec) + target_include_directories(webp_quality + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}) + + # vwebp_sdl + find_package(SDL) + if(SDL_FOUND) + add_executable(vwebp_sdl ${VWEBP_SDL_SRCS}) + target_link_libraries(vwebp_sdl ${SDL_LIBRARY} imageioutil webp) + target_include_directories(vwebp_sdl + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/src + ${SDL_INCLUDE_DIR}) + set(WEBP_HAVE_SDL 1) + target_compile_definitions(vwebp_sdl PUBLIC WEBP_HAVE_SDL) + endif() +endif() + +if(WEBP_BUILD_WEBP_JS) + # wasm2js does not support SIMD. + if(NOT WEBP_ENABLE_SIMD) + # JavaScript version + add_executable(webp_js ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c) + target_link_libraries(webp_js webpdecoder SDL) + target_include_directories(webp_js PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + set(WEBP_HAVE_SDL 1) + set_target_properties( + webp_js + PROPERTIES LINK_FLAGS "-s WASM=0 \ + -s EXPORTED_FUNCTIONS='[\"_WebpToSDL\"]' -s INVOKE_RUN=0 \ + -s EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'") + set_target_properties(webp_js PROPERTIES OUTPUT_NAME webp) + target_compile_definitions(webp_js PUBLIC EMSCRIPTEN WEBP_HAVE_SDL) + endif() + + # WASM version + add_executable(webp_wasm ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c) + target_link_libraries(webp_wasm webpdecoder SDL) + target_include_directories(webp_wasm PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + set_target_properties( + webp_wasm + PROPERTIES LINK_FLAGS "-s WASM=1 \ + -s EXPORTED_FUNCTIONS='[\"_WebpToSDL\"]' -s INVOKE_RUN=0 \ + -s EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'") + target_compile_definitions(webp_wasm PUBLIC EMSCRIPTEN WEBP_HAVE_SDL) + + target_compile_definitions(webpdspdecode PUBLIC EMSCRIPTEN) +endif() + +if(WEBP_BUILD_ANIM_UTILS) + # anim_diff + include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS} ${WEBP_DEP_GIF_INCLUDE_DIRS}) + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "ANIM_DIFF_SRCS" + "anim_diff") + add_executable(anim_diff ${ANIM_DIFF_SRCS}) + target_link_libraries(anim_diff + exampleutil + imagedec + imageenc + imageioutil + webp + webpdemux + ${WEBP_DEP_GIF_LIBRARIES}) + target_include_directories(anim_diff PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) + + # anim_dump + include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS} ${WEBP_DEP_GIF_INCLUDE_DIRS}) + parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "ANIM_DUMP_SRCS" + "anim_dump") + add_executable(anim_dump ${ANIM_DUMP_SRCS}) + target_link_libraries(anim_dump + exampleutil + imagedec + imageenc + imageioutil + webp + webpdemux + ${WEBP_DEP_GIF_LIBRARIES}) + target_include_directories(anim_dump PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) +endif() + +# Install the different headers and libraries. +install(TARGETS ${INSTALLED_LIBRARIES} + EXPORT ${PROJECT_NAME}Targets + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webp + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake/) +install(EXPORT ${PROJECT_NAME}Targets + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${ConfigPackageLocation}) + +# Create the CMake version file. +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake" + VERSION ${PACKAGE_VERSION} + COMPATIBILITY AnyNewerVersion) + +# Create the Config file. +include(CMakePackageConfigHelpers) +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/WebPConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake + INSTALL_DESTINATION + ${ConfigPackageLocation}) + +# Install the generated CMake files. +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake" + DESTINATION ${ConfigPackageLocation}) + +# Install the man pages. +set(MAN_PAGES + cwebp.1 + dwebp.1 + gif2webp.1 + img2webp.1 + vwebp.1 + webpmux.1 + webpinfo.1) +set(EXEC_BUILDS + "CWEBP" + "DWEBP" + "GIF2WEBP" + "IMG2WEBP" + "VWEBP" + "WEBPMUX" + "WEBPINFO") +list(LENGTH MAN_PAGES MAN_PAGES_LENGTH) +math(EXPR MAN_PAGES_RANGE "${MAN_PAGES_LENGTH} - 1") + +foreach(I_MAN RANGE ${MAN_PAGES_RANGE}) + list(GET EXEC_BUILDS ${I_MAN} EXEC_BUILD) + if(WEBP_BUILD_${EXEC_BUILD}) + list(GET MAN_PAGES ${I_MAN} MAN_PAGE) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/man/${MAN_PAGE} + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 + COMPONENT doc) + endif() +endforeach() diff --git a/internal/libwebp-1.2.1/CONTRIBUTING.md b/internal/libwebp-1.2.1/CONTRIBUTING.md new file mode 100644 index 0000000..7a73a30 --- /dev/null +++ b/internal/libwebp-1.2.1/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution; +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use a [Gerrit](https://www.gerritcodereview.com) instance hosted at +https://chromium-review.googlesource.com for this purpose. See the +[WebM Project page](https://www.webmproject.org/code/contribute/submitting-patches/) +for additional details. + +## Community Guidelines + +This project follows +[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). diff --git a/internal/libwebp-1.2.1/COPYING b/internal/libwebp-1.2.1/COPYING new file mode 100644 index 0000000..7a6f995 --- /dev/null +++ b/internal/libwebp-1.2.1/COPYING @@ -0,0 +1,30 @@ +Copyright (c) 2010, Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/internal/libwebp-1.2.1/ChangeLog b/internal/libwebp-1.2.1/ChangeLog new file mode 100644 index 0000000..6185fb6 --- /dev/null +++ b/internal/libwebp-1.2.1/ChangeLog @@ -0,0 +1,4231 @@ +d9191588 fuzzer/*: normalize src/ includes +53b6f762 fix indent +731246ba update ChangeLog (tag: v1.2.1-rc2) +d250f01d dsp/*: use WEBP_HAVE_* to determine Init availability +3a4d3ecd update NEWS +b2bc8093 bump version to 1.2.1 +e542fc7a update AUTHORS +e0241154 Merge "libwebp/CMake: Add to webp incl" into main +edea6444 libwebp/CMake: Add to webp incl +ece18e55 dsp.h: respect --disable-sse2/sse4.1/neon +a89a3230 wicdec: support alpha from WebP WIC decoder +26f4aa01 Merge "alpha_processing: fix visual studio warnings" into main +8f594663 alpha_processing: fix visual studio warnings +46d844e6 Merge "cpu.cmake: fix compiler flag detection w/3.17.0+" into main +298d26ea Merge changes I593adf92,If20675e7,Ifac68eac into main +a1e5dae0 alpha_processing*: use WEBP_RESTRICT qualifier +327ef24f cpu.cmake: fix compiler flag detection w/3.17.0+ +f70819de configure: enable libwebpmux by default +dc7e2b42 configure: add informational notices when disabling binaries +9df23ddd configure: move lib flag checks before binaries +a2e18f10 Merge "WebPConfig.config.in: correct WEBP_INCLUDE_DIRS" into main +e1a8d4f3 Merge "bit_reader_inl_utils: uniformly apply WEBP_RESTRICT" into main +4de35f43 rescaler.c: fix alignment +0f13eec7 bit_reader_inl_utils: uniformly apply WEBP_RESTRICT +277d3074 Fix size_t overflow in WebPRescalerInit +97adbba5 WebPConfig.config.in: correct WEBP_INCLUDE_DIRS +b60d4603 advanced_api_fuzzer: add extreme config value coverage +72fe52f6 anim_encode.c,cosmetics: normalize indent +116d235c anim_encode: Fix encoded_frames_[] overflow +6f445b3e CMake: set CMP0072 to NEW +b1cf887f define WEBP_RESTRICT for MSVC +3e265136 Add WEBP_RESTRICT & use it in VP8BitReader +f6d29247 vp8l_dec::ProcessRows: fix int overflow in multiply +de3b4ba8 CMake: add WEBP_BUILD_LIBWEBPMUX +7f09d3d1 CMakeLists.txt: rm libwebpmux dep from anim_{diff,dump} +4edea4a6 Init{RGB,YUV}Rescaler: fix a few more int overflows +c9e26bdb rescaler_utils: set max valid scaled w/h to INT_MAX/2 +28d488e6 utils.h: add SizeOverflow() +695bdaa2 Export/EmitRescaledRowsRGBA: fix pointer offset int overflow +685d073e Init{RGB,YUV}Rescaler: fix int overflows in multiplication +d38bd0dd WebPFlipBuffer: fix integer overflow +109ff0f1 utils: allow MALLOC_LIMIT to indicate a max +a2fce867 WebPRescalerImportRowExpand_C: promote some vals before multiply +776983d4 AllocateBuffer: fix int multiplication overflow check +315abbd6 Merge "Revert "Do not use a palette for one color images."" +eae815d0 Merge changes Ica3bbf75,I82f82954 +afbca5a1 Require Emscripten 2.0.18 +3320416b CMakeLists,emscripten: use EXPORTED_RUNTIME_METHODS +29145ed6 Update README instructions for using Emscripten +1f579139 cosmetics: remove use of 'sanity' / 'master' +29b6129c WebPAnimEncoderNewInternal: remove some unnecessary inits +b60869a1 Revert "Do not use a palette for one color images." +6fb4cddc demux: move padded size calc post unpadded validation +05b72d42 vp8l_enc.c: normalize index types +b6513fba Do not use a palette for one color images. +98bbe35b Fix multi-threading with palettes. +b1674240 Add modified Zeng's method to palette sorting. +88c90c45 add CONTRIBUTING.md +6a9916d7 WebPRescalerInit: add missing int64_t promotion +b6cf52d5 WebPIoInitFromOptions: treat use_scaling as a bool +3b12b7f4 WebPIoInitFromOptions: treat use_cropping as a bool +595fa13f add WebPCheckCropDimensions() +8fdaecb0 Disable cross-color when palette is used. +8933bac2 WebPIoInitFromOptions: respect incoming bypass_filtering val +7d416ff0 webpdec,cosmetics: match error text to function call +ec6cfeb5 Fix typo on WebPPictureAlloc() in README +7e58a1a2 *.cmake: add license header +5651a6b2 cmake: fix .so versioning +25ae67b3 xcframeworkbuild.sh: add arm64 simulator target +5d4ee4c3 cosmetics: remove use of the term 'dummy' +01b38ee1 faster CollectColorXXXTransforms_SSE41 +652aa344 Merge "Use BitCtz for FastSLog2Slow_C" +0320e1e3 add the missing default BitsCtz() code +8886f620 Use BitCtz for FastSLog2Slow_C +fae41617 faster CombinedShannonEntropy_SSE2 +5bd2704e Introduce the BitCtz() function. +fee64287 Merge "wicdec,icc: treat unsupported op as non-fatal" +33ddb894 lossless_sse{2,41}: remove some unneeded includes +b27ea852 wicdec,icc: treat unsupported op as non-fatal +b78494a9 Merge "Fix undefined signed shift." +e79974cd Fix undefined signed shift. +a8853394 SSE4.1 versions of BGRA to RGB/BGR color-space conversions +a09a6472 SSE4.1 version of TransformColorInverse +401da22b Merge "pngdec: check version before using png_get_chunk_malloc_max" +26907822 pngdec: check version before using png_get_chunk_malloc_max +06c1e72e Code cleanup +8f0d41aa Merge changes Id135bbf4,I99e59797 +373eb170 gif2webp: don't store loop-count if there's only 1 frame +759b9d5a cmake: add WEBP_USE_THREAD option +926ce921 cmake: don't install binaries from extras/ +9c367bc6 WebPAnimDecoderNewInternal: validate bitstream before alloc +47f64f6e filters_sse2: import Chromium change +cc3577e9 fuzzer/*: use src/ based include paths +004d77ff Merge tag 'v1.2.0' +fedac6cc update ChangeLog (tag: v1.2.0-rc3, tag: v1.2.0) +170a8712 Fix check_c_source_compiles with pthread. +ceddb5fc Fix check_c_source_compiles with pthread. +85995719 disable CombinedShannonEntropy_SSE2 on x86 +289757fe TiffDec: enforce stricter mem/dimension limit on tiles +8af7436f Merge "{ios,xcframework}build.sh: make min version(s) more visible" into 1.2.0 +e56c3c5b pngdec: raise memory limit if needed +8696147d pngdec: raise memory limit if needed +13b8e9fe {ios,xcframework}build.sh: make min version(s) more visible +a9225410 animdecoder_fuzzer: fix memory leak +d6c2285d update gradle to 6.1.1 +8df77fb1 animdecoder_fuzzer: fix memory leak +52ce6333 update NEWS +28c49820 bump version to 1.2.0 +7363dff2 webp/encode.h: restore WEBP_ENCODER_ABI_VERSION to v1.1.0 +826aafa5 update AUTHORS +63258823 animdecoder_fuzzer: validate canvas size +9eb26381 CMake: remove duplicate "include(GNUInstallDirs)" +2e7bed79 WebPPicture: clarify the ownership of user-owned data. +cccf5e33 webpmux: add an '-set loop ' option +c9a3f6a1 Merge changes Ie29f9867,I289c54c4 +319f56f1 iosbuild.sh: sync some aspects of xcframeworkbuild.sh +e8e8db98 add xcframeworkbuild.sh +ae545534 dsp.h: allow config.h to override MSVC SIMD autodetection +fef789f3 Merge "cmake: fix per-file assembly flags" +fc14fc03 Have C encoding predictors use decoding predictors. +7656f0b3 README,cosmetics: fix a couple typos +d2e245ea cmake: disable webp.js if WEBP_ENABLE_SIMD=1 +96099a79 cmake: fix per-file assembly flags +5abb5582 Merge "cmake: fix compilation w/Xcode generator" +8484a120 cmake: fix compilation w/Xcode generator +d7bf01c9 Merge changes Ifcae0f38,Iee2d7401 +36c81ff6 WASM-SIMD: port 2 patches from rreverser@'s tree +988b02ab Merge "Couple of fixes to allow SIMD on Emscripten" +26faf770 wicdec: fail with animated images +ab2d08a8 [cd]webp: document lack of animated webp support +52273943 Couple of fixes to allow SIMD on Emscripten +8870ba7f Fix skia bug #10952 +4b3c6953 Detect if StoreFrame read more than anmf_payload_size bytes +17fd4ba8 webp/decode.h,cosmetics: normalize 'flip' comment +411d3677 remove some unreachable break statements +3700ffd7 WebPPictureHasTransparency: remove unreachable return +83604bf3 {animencoder,enc_dec}_fuzzer: convert some abort()s to returns +eb44119c Merge changes I8ae09473,I678c8b1e +9f6055fc fuzz_utils.h: rename max() to Max() +695788e7 fuzz_utils.h: make functions WEBP_INLINE +906c1fcd make ImgIoUtilReadFile use WebPMalloc instead of malloc +8cb7e536 rename demux_api_fuzzer.c -> mux_demux_api_fuzzer.c +443db47d add animdecoder_fuzzer.cc +36a6eea3 Merge "import fuzzers from oss-fuzz/chromium" +ec5f12c1 Makefile.vc: remove deprecated /Gm option +64425a08 picture_tools_enc: fix windows build warning +bd94090a import fuzzers from oss-fuzz/chromium +cf847cba use WEBP_DSP_INIT_FUNC for Init{GammaTables*,GetCoeffs} +55a080e5 Add WebPReplaceTransparentPixels() in dsp +84739717 GetBackgroundColorGIF: promote to uint32_t before << 24 +def64e92 cwebp: Fix -print_psnr for near_lossless +cf2f88b3 Add palette and spatial for q >= 75 and -m 5 +f0110bae Add no-color cache configuration to the cruncher +749a8b99 Better estimate of the cache cost. +4f9f00cc Use spatial predictors on top of palette no matter what. +7658c686 Add spatial prediction on top of palette in cruncher. +133ff0e3 webp_js: force WASM=0 option explicitly +e3c259a2 Fix integer overflow in EmitFancyRGB. +b3ff0bde man/{gif2,img2}webp,webpmux: normalize some wording +f9b30586 fix ABI breakage introduced by 6a0ff358 +1d58dcfc README.webp_js: update note about emscripten version +44070266 README.webp_js: s/fastcomp/upstream/ +2565fa8f README.webp_js: update cmake command +47309ef5 webp: WEBP_OFFSET_PTR() +687ab00e DC{4,8,16}_NEON: replace vmovl w/vaddl +1b92fe75 DC16_NEON,aarch64: use vaddlv +53f3d8cf dec_neon,DC8_NEON: use vaddlv instead of movl+vaddv +27d08240 Fix integer overflow in WebPAnimDecoderGetNext() +69776e38 Merge "remove call to MBAnalyzeBestIntra4Mode for method >= 5" +a99078c1 remove call to MBAnalyzeBestIntra4Mode for method >= 5 +22e404cc CMakeLists.txt: fix set(CACHE) argument order +71690b52 fix MSVC warning +6a0ff358 Enc: add a qmin / qmax range for quality factor +0fa56f30 Merge tag 'v1.1.0' +6cf504d0 PNM decoding: handle max_value != 255 +d7844e97 update ChangeLog (tag: v1.1.0-rc2, tag: v1.1.0, origin/1.1.0) +7f006436 Makefile.vc: fix webp_quality.exe link +cf047e83 Makefile.vc: fix webp_quality.exe link +c074c653 update NEWS +30f09551 bump version to 1.1.0 +a76694a1 update AUTHORS +6e3ef7b3 extras: fix WEBP_SWAP_16BIT_CSP check +47178dbd extras: add WebPUnmultiplyARGB() convenience function +22cbae33 idec_dec: fix 0 offset of NULL pointer +290dd0b4 muxread: fix 0 offset of NULL pointer +0df474ac Merge "lossless_(enc_|)sse2: avoid offsetting a NULL pointer" +c6b75a19 lossless_(enc_|)sse2: avoid offsetting a NULL pointer +295e5e38 fix UBSAN warning +e2575e05 DC8_NEON,aarch64: use vaddv +b0e09e34 dec_neon: Fix build failure under some toolchains +cf0e903c dsp/lossless: Fix non gcc ARM builds +bb7bc40b Remove ubsan errors. +78881b76 CMake: fix GLUT library link +9f750f7a cmake: fix BUILD_SHARED_LIBS build on mac +17850e74 libwebp: Remove char-subscripts warning in pnmdec.c +2fa2552d Merge "Expose WebPMalloc() in addition to WebPFree()" +a4df4aae Expose WebPMalloc() in addition to WebPFree() +853ea3d8 imageio/tiff: Return error before allocating bad tile size +af650c0b Fix a Wxor-used-as-pow false positive +601ef17c libwebp.py: update to swig 3.0.12 +0e48d889 bugfix: last alpha rows were incorrectly decoded +24d2ccb4 webp: Fix imageio ReadPNM() TUPLTYPE +fab8f9cf cosmetics: normalize '*' association +94138e0e update .gitignore +0fe1a89d update ChangeLog (tag: v1.0.3-rc1, tag: v1.0.3) +2ad0916d update NEWS +1287362b bump version to 1.0.3 +7b968cc2 update AUTHORS +9d6988f4 Fix the oscillating prediction problem at low quality +312f74d0 makefile.unix: allow *_LIBS to be overridden w/EXTRA_LIBS +92dbf237 filters_sse2,cosmetics: shorten some long lines +a277d197 filters_sse2.c: quiet integer sanitizer warnings +804540f1 Fix cpufeatures in CMake. +bf00c15b Add CMake option for bittrace. +a788b498 filters_sse2.c: quiet integer sanitizer warnings +e6a92c5e filters.c: quiet integer sanitizer warnings +ec1cc40a lossless.c: remove U32 -> S8 conversion warnings +1106478f remove conversion U32 -> S8 warnings +812a6b49 lossless_enc: fix some conversion warning +4627c1c9 lossless_enc,TransformColorBlue: quiet uint32_t conv warning +c84673a6 lossless_enc_sse{2,41}: quiet signed conv warnings +776a7757 dec_sse2: quiet signed conv warnings +bd39c063 Merge "thread_utils: release mutex before signaling" +0550576f Merge "(alpha_processing,enc}_sse2: quiet signed conv warnings" +6682f2c4 thread_utils: release mutex before signaling +e78dea75 (alpha_processing,enc}_sse2: quiet signed conv warnings +9acf18ba iosbuild.sh: add WebP{Demux,Mux}.framework +b9be7e65 vwebp: remove the -fit option (and make it default) +1394a2bb Merge "README.webp_js: update Emscripten.cmake note" +dd3e7f8a README.webp_js: update Emscripten.cmake note +32cf8801 predictor_enc,GetBestGreenRedToBlue: quiet implicit conv warnings +e1c8acb5 Merge "vwebp: add a -fit option" +cbd23dd5 vwebp: add a -fit option +2e672351 bit_writer_utils,Flush: quiet implicit conversion warnings +1326988d swig: update libwebp_python_wrap.c +0e7f8548 update generated swig files +17ed1438 Merge "PutLE{16,24}: quiet implicit conversion warnings" +24686538 PutLE{16,24}: quiet implicit conversion warnings +153bb3a0 fix some clang-7 warnings: +ab2dc893 Rescaler: fix rounding error +aa65f89a HistogramCombineStochastic: fix free of uninit value +af0bac64 Merge "encode.h: mention 'exact' default in WebPEncodeLossless*" +6d2e11ec encode.h: mention 'exact' default in WebPEncodeLossless* +8c3f04fe AndroidCPUInfo: reorder terms in conditional +fcfd9c71 BitTrace: if BITTRACE is > 0, record and print syntax bits used +067031ea Speedups for unused Huffman groups. +01ac46ba libwebp: Display "libjpeg error:" in imageio/jpegdec +d9a662e1 WebPRescalerGetScaledDimensions: round scaled dimension up +62eb3f08 libwebp: Fix missing '{' in README +e05f785a Merge "unicode,INIT_WARGV: add missing cast" +63c9a69f tag the VP8LHashPix() function for potential uint roll-over +2b7214ab unicode,INIT_WARGV: add missing cast +bf424b46 tag the GetPixPairHash64() function for potential uint roll-over +7d05d6ca Have the color cache computation be u32-bit only. +6bcf8769 Remove BINARYEN_METHOD in wasm settings. +2b98df90 update ChangeLog (tag: v1.0.2-rc1, tag: v1.0.2) +61e372b7 update NEWS +7ae658a0 bump version to 1.0.2 +51c4907d update AUTHORS +666bd6c6 man/cwebp.1: refine near-lossless text +561cdce5 Clarify the doc about GetFeatures. +aec2cf02 near_lossless: fix fuzzing-detected integer overflow +928a75de webp: Fix VP8LBitWriterClone() bug +5173d4ee neon IsFlat +5b081219 IsFlat: inline when possible +381b7b54 IsFlat: use int for thresh +6ed15ea1 fix unprobable leak in webp_sdl.c +22bbb24e Merge "IsFlat: return int" +8b3fb238 Merge tag 'v1.0.1' +f435de95 IsFlat: return int +41521aed utils.h: only define WEBP_NEED_LOG_TABLE_8BIT when needed +9f4d4a3f neon: GetResidualCost +0fd7514b neon: SetResidualCoeffs +f95a996c Simpler histogram clustering. +e85d3313 update ChangeLog (tag: v1.0.1-rc2, tag: v1.0.1) +fa8210e4 Fix pair update in stochastic entropy merging. +fd198f73 add codereview.settings +825389ac README.mux: add a reference to the AnimDecoder API +3be698c3 CMake: fix webp_js compilation +485ff86f Fix pair update in stochastic entropy merging. +4cd0582d CMake: fix webp_js compilation +4cbb4caf update NEWS +f5a5918d bump version to 1.0.1 +d61385db Speed-up: Make sure we only initialize histograms when needed. +6752904b Speed-up: Make sure we only initialize histograms when needed. +0c570316 update AUTHORS +301a2dda img2webp: add help note about arguments from a file +f0abab92 Speedups for empty histograms. +f2dfd925 Split HistogramAdd to only have the high level logic in C. +06b7bc7d Fix compilation on windows and clang-cl+ninja. +b6284d82 img2webp: add help note about arguments from a file +decf6f6b Speedups for empty histograms. +dea3e899 Split HistogramAdd to only have the high level logic in C. +632798ae Merge "Fix compilation on windows and clang-cl+ninja." +dc1a9518 Merge "libwebp: Unicode command tools on Windows" +9cf9841b libwebp: Unicode command tools on Windows +98179495 remove some minor TODOs +a376e7b9 Fix compilation on windows and clang-cl+ninja. +cbf82cc0 Remove AVX2 files. +5030e902 Merge "TIFF decoder: remove unused KINV definition" +ac543311 Remove a few more useless #defines +123d3306 TIFF decoder: remove unused KINV definition +ef1094b0 Merge "- install pkg-config files during the CMake build" +b911fbc9 libwebp: Remove duplicate GIFDisplayError in anim_util +eee00b66 - install pkg-config files during the CMake build +ac3ec8c9 Merge "Clean-up the common sources in dsp." +3e13da7b Clean-up the common sources in dsp. +5c395f1d libwebp: cmake-format all +e7a69729 libwebp: Add extras targets in CMakeLists.txt +e52485d6 libwebp: Rename macros in webpmux.c +92dc0f09 clean-up MakeInputImageCopy() +39952de2 VP8IteratorImport: add missing 'const' +382af7a2 clean-up WebPBlendAlpha +14d020f6 libwebp: Use ExUtilGet*() in anim_diff +0d92ff25 libwebp: remove useless variable in gif2webp +556cb1b4 Merge "CMake: Set WEBP_BUILD_GIF2WEBP to off" +da26ee49 CMake: Set WEBP_BUILD_GIF2WEBP to off +b2a867c0 cwebp: Don't premultiply during -resize if -exact +637141bc pngdec: fix build w/libpng < 1.4.x +bc5092b1 pngdec: set memory functions +50d8345a Fix CMake math library. +6aa3e8aa Fix math library on Visual Studio. +d71df4e2 Fix math library finding in CMake. +de08d727 cosmetics: normalize include guard comment +009562b4 vwebp: Fix bug when Dispose then NoBlend frames +423f2579 Fix up CMake to create targets. +907208f9 Wait for all threads to be done in DecodeRemaining. +4649b3c4 vwebp: Add background color display option +78ad57a3 Fix bad glClearColor parameters +da96d8d9 Allow for a non-initialized alpha decompressor in DoRemap. +2563db47 fix rescaling rounding inaccuracy +211f37ee fix endian problems in pattern copy +5f0f5c07 Make sure partition #0 is read before VP8 data in IDecode. +de98732b fix GetColorf() bug +4338cd36 misc fixes in libwebpmux +e00af13e fix signatures after a9ceda7ff1 +a9ceda7f Speed-up chunk list operations. +2281bbf6 Merge "Better handling of bogus Huffman codes." +39cb9aad Better handling of bogus Huffman codes. +89cc9d37 Merge "fix read-overflow while parsing VP8X chunk" +95fd6507 fix read-overflow while parsing VP8X chunk +9e729fe1 Fix VP8IoTeardownHook being called twice on worker sync failure +29fb8562 Merge "muxread,anmf: fail on multiple image chunks" +eb82ce76 muxread,anmf: fail on multiple image chunks +1344a2e9 fix alpha-filtering crash when image width is larger than radius +be738c6d muxread,ChunkVerifyAndAssign: validate chunk_size +2c70ad76 muxread,CreateInternal: fix riff size checks +569001f1 Fix for thread race heap-use-after-free +c56a02d9 Android.mk: use LOCAL_EXPORT_C_INCLUDES w/public libs +15795596 CMakeLists.txt,cosmetics: normalize if() formatting +1a44c233 Merge "cmake: add support for webpmux" +e9569ad7 Merge "configure,*am,cosmetics: s/WANT_/BUILD_/" +35c7de6f cmake: add support for webpmux +0f25e61c WebpToSDL(): fix the return value in case of error +5d8985de configure,*am,cosmetics: s/WANT_/BUILD_/ +895fd28f Merge "man/Makefile.am: add img2webp.1" +5cf3e2af man/Makefile.am: add img2webp.1 +2a9de5b9 Add build rules for anim_diff & anim_dump utils. +71ed73cf fix invalid check for buffer size +af0e4fbb gif2webp: fix transcode of loop count=65535 +dce5d764 Limit memory allocation when reading invalid Huffman codes. +f9df0081 Merge "cmake: quiet glut deprecation warnings on OS X" +dc39b16f webpmux.1: correct grammar +c7aa1264 cwebp.c: fix a missing \n +53aa51e9 Merge tag 'v1.0.0' +698b8844 update ChangeLog (tag: v1.0.0) +8d510751 webp-container-spec: correct frame duration=0 note +e6b2164e vwebp: Copy Chrome's behavior w/frame duration == 0 +094b3b28 cmake: quiet glut deprecation warnings on OS X +71c39a06 webp-container-spec: correct frame duration=0 note +fd3d5756 vwebp: Copy Chrome's behavior w/frame duration == 0 +b0c966fb Build vwebp from CMake. +d20b7707 update ChangeLog (tag: v1.0.0-rc3) +0d5fad46 add WEBP_DSP_INIT / WEBP_DSP_INIT_FUNC +d77bf512 add WEBP_DSP_INIT / WEBP_DSP_INIT_FUNC +c1cb86af fix 16b overflow in SSE2 +e577feb7 makefile.unix: add DEBUG flag for compiling w/ debug-symbol +99be34b3 cwebp,get_disto: fix bpp output +e122e511 cwebp,get_disto: fix bpp output +f5565ca8 cmake: Make sure we use near-lossless by default. +d898dc14 fix bug in WebPImport565: alpha value was not set +1c8f358d Fix CMake with WASM. +a0215fb7 webp_js: fix webp_js demo html +882784b0 update ChangeLog (tag: v1.0.0-rc2) +2f930e08 Revert "Use proper targets for CMake." +8165e8fb Use proper targets for CMake. +3f157dd5 Remove some very hard TODOs. +abb47760 Merge "Use proper targets for CMake." +cd758a17 {de,}mux/Makefile.am: add missing headers +e155dda0 Use proper targets for CMake. +b892b8ba makefile.unix,dist: use ascii for text output +64a57d05 add -version option to anim_dump,anim_diff and img2webp +994be82d Merge "Remove some very hard TODOs." +4033e1d7 Remove some very hard TODOs. +fc1b8e3a webp_js: fix webp_js demo html +15aa48d9 update ChangeLog (tag: v1.0.0-rc1) +e607dabc update AUTHORS +38410c08 [CFI] Remove function pointer casts +978eec25 [CFI] Remove function pointer casts +c57b2736 bump version to 1.0.0 +cba28853 update NEWS +c909d531 Merge "remove some deprecation warning on MacOSX" +217443c7 remove some deprecation warning on MacOSX +b672bdfa configure: quiet glut deprecation warnings on OS X +daa9fcaf configure: use sdl-config if available +dd174cae Merge "imagedec: support metadata reading for WebP image decoding" +641cedcc imagedec: support metadata reading for WebP image decoding +065b2ce1 anim_diff: add a couple missing newlines in Help() +c4cc1147 Merge "gif2webp: force low duration frames to 100ms" +09333097 gif2webp: force low duration frames to 100ms +e03f0ec3 sharp_yuv: use 14b fixed-point precision for gamma +b2db361c image_enc,WebPWritePNG: move locals after setjmp +74e82ec6 Merge "WebPPictureDistortion: fix big-endian results order" +645d04ca Merge "cwebp,get_disto: report bpp" +120f58c3 Merge "lossless*sse2: improve non-const 16-bit vector creation" +a7fe9412 WebPPictureDistortion: fix big-endian results order +e26fe066 cwebp,get_disto: report bpp +9df64e28 Merge changes Id5b4a1a4,Ia20ce844 +8043504f lossless*sse2: improve non-const 16-bit vector creation +1e3dfc48 Import: extract condition from loop +3b07d327 Import,RGBA: fix for BigEndian import +551948e4 Remove unused argument in VP8LBitsEntropy. +3005237a ReadWebP: fix for big-endian +499c395a Merge "anim_diff: expose the -max_diff option" +f69dcd69 Merge "remove WEBP_EXPERIMENTAL_FEATURES" +07d884d5 anim_diff: expose the -max_diff option +f4dd9256 remove WEBP_EXPERIMENTAL_FEATURES +94a8377b extract the command-line parsing helpers to example_util +fc09e6e2 PNM decoder: prevent unsupported depth=2 PAM case. +6de58603 MIPS64: Fix defined-but-not-used errors with WEBP_REDUCE_CSP +cbde5728 gif2webp: add support for reading from stdin +cf1c5054 Add an SSE4 version of some lossless color transforms. +45a8b5eb Fix lint error with man page. +cff38e8f Merge "PNG decoder: handle gAMA chunk" +59cb1a48 Merge "enable dc error-diffusion always" +78318b30 PNG decoder: handle gAMA chunk +664c21dd Merge "remove some TODOs" +815652de enable dc error-diffusion always +aec45cec remove some TODOs +5715dfce fix block-count[] increment in case of large image +c2d04f3e enable DC error-diffusion always for multi-pass +96bf07c5 use DC error diffusion for U/V at low-quality +1c59020b fix missing sse41 targets in makefile.unix +7a8e814b cosmetics: s/color_space/colorspace/ +05f6fe24 upsampling: rm asserts w/REDUCE_CSP+OMIT_C_CODE +b4cf5597 Merge "Upsampling SSE2/SSE4 speedup." +ccbeb32c Makefile.vc: add missing sse41 files +55403a9a Upsampling SSE2/SSE4 speedup. +807b53c4 Implement the upsampling/yuv functions in SSE41 +84101a81 Fix wasm WebP compilation +8bebd2a3 fix warning on MSVC +a7f93fe3 webpmux: allow reading argument from a file +b69f18a7 gif2webp.1: fix -loop_compatibility layout +72d530c0 Merge "fix lossless decoding w/WEBP_REDUCE_SIZE" +296c7dc4 fix lossless decoding w/WEBP_REDUCE_SIZE +0d5d029c Merge "ImgIoUtilReadFile: fix file leak upon error" +ae568ce7 ImgIoUtilReadFile: fix file leak upon error +796b5a8a Merge tag 'v0.6.1' +6b7a95fd update ChangeLog (tag: v0.6.1) +f66955de WEBP_REDUCE_CSP: restrict colorspace support +1af0df76 Merge "WEBP_REDUCE_CSP: restrict colorspace support" +6de20df0 WEBP_REDUCE_CSP: restrict colorspace support +a289d8e7 update ChangeLog (tag: v0.6.1-rc2) +c10a493c vwebp: disable double buffering on windows & mac +0d4466c2 webp_to_sdl.c: fix file mode +1b27bf8b WEBP_REDUCE_SIZE: disable all rescaler code +126be109 webpinfo: add -version option +0df22b9e WEBP_REDUCE_SIZE: disable all rescaler code +9add62b5 bump version to 0.6.1 +d3e26144 update NEWS +2edda639 README: add webpinfo section +9ca568ef Merge "right-size some tables" +31f1995c Merge "SSE2 implementation of HasAlphaXXX" +a80c46bd SSE2 implementation of HasAlphaXXX +083507f2 right-size some tables +2e5785b2 anim_utils.c: remove warning when !defined(WEBP_HAVE_GIF) +b299c47e add WEBP_REDUCE_SIZE +f593d71a enc: disable pic->stats/extra_info w/WEBP_DISABLE_STATS +541179a9 Merge "predictor_enc: fix build w/--disable-near-lossless" +5755a7ec predictor_enc: fix build w/--disable-near-lossless +eab5bab7 add WEBP_DISABLE_STATS +8052c585 remove some petty TODOs from vwebp. +c245343d move LOAD8x4 and STORE8x2 closer to their use location +b9e734fd dec,cosmetics: normalize function naming style +c188d546 dec: harmonize function suffixes +28c5ac81 dec_sse41: harmonize function suffixes +e65b72a3 Merge "introduce WebPHasAlpha8b and WebPHasAlpha32b" +b94cee98 dec_sse2: remove HE8uv_SSE2 +44a0ee3f introduce WebPHasAlpha8b and WebPHasAlpha32b +aebf59ac Merge "WebPPictureAllocARGB: align argb allocation" +c184665e WebPPictureAllocARGB: align argb allocation +3daf7509 WebPParseHeaders: remove obsolete animation TODO +80285d97 cmake: avoid security warnings under msvc +650eac55 cmake: don't set -Wall with MSVC +c462cd00 Remove useless code. +01a98217 Merge "remove WebPWorkerImpl declaration from the header" +3c49fc47 Merge "thread_utils: fix potentially bad call to Execute" +fde2782e thread_utils: fix potentially bad call to Execute +2a270c1d remove WebPWorkerImpl declaration from the header +f1f437cc remove mention of 'lossy-only parameters' from the doc +3879074d Merge "WebPMemToUint32: remove ptr cast to int" +04b029d2 WebPMemToUint32: remove ptr cast to int +b7971d0e dsp: avoid defining _C functions w/NEON builds +6ba98764 webpdec: correct alloc size check w/use_argb +5cfb3b0f normalize include guards +f433205e Merge changes Ia17c7dfc,I75423abb,Ia2f716b4,I161caa14,I4210081a, ... +8d033b14 {dec,enc}_neon: harmonize function suffixes x2 +0295e981 upsampling_neon: harmonize function suffixes +d572c4e5 yuv_neon: harmonize function suffixes +ab9c2500 rescaler_neon: harmonize function suffixes +93e0ce27 lossless_neon: harmonize function suffixes +22fbc50e lossless_enc_neon: harmonize function suffixes +447875b4 filters_neon,cosmetics: fix indent +e51bdd43 remove unused VP8TokenToStats() function +785da7ea enc_neon: harmonize function suffixes +bc1a251f dec_neon: harmonize function suffixes +61e535f1 dsp/lossless: workaround gcc-4.8 bug on arm +68b2eab7 cwebp: fix alpha reporting w/lossless & metadata +30042faa WebPDemuxGetI: add doc details around WebPFormatFeature +0a17f471 Merge "WIP: list includes as descendants of the project dir" +a4399721 WIP: list includes as descendants of the project dir +08275708 Merge "Make sure we reach the full range for alpha blending." +d361a6a7 yuv_sse2: harmonize function suffixes +6921aa6f upsampling_sse2: harmonize function suffixes +08c67d3e ssim_sse2: harmonize function suffixes +582a1b57 rescaler_sse2: harmonize function suffixes +2c1b18ba lossless_sse2: harmonize function suffixes +0ac46e81 lossless_enc_sse2: harmonize function suffixes +bc634d57 enc_sse2: harmonize function suffixes +bcb7347c dec_sse2: harmonize function suffixes +e14ad93c Make sure we reach the full range for alpha blending. +7038ca8d demux,StoreFrame: restore hdr size check to min req +fb3daad6 cpu: fix ssse3 check +be590e06 Merge "Fix CMake redefinition for HAVE_CPU_FEATURES_H" +35f736e1 Fix CMake redefinition for HAVE_CPU_FEATURES_H +a5216efc Fix integer overflow warning. +a9c8916b decode.h,WebPIDecGetRGB: clarify output ptr validity +3c74c645 gif2webp: handle 1-frame case properly + fix anim_diff +c7f295d3 Merge "gif2webp: introduce -loop_compatibility option" +b4e04677 gif2webp: introduce -loop_compatibility option +f78da3de add LOCAL_CLANG_PREREQ and avoid WORK_AROUND_GCC w/3.8+ +01c426f1 define WEBP_USE_INTRINSICS w/gcc-4.9+ +8635973d use sdl-config (if available) to determine the link flags +e9459382 use CPPFLAGS before CFLAGS +4a9d788e Merge "Android.mk,mips: fix clang build with r15" +4fbdc9fb Android.mk,mips: fix clang build with r15 +a80fcc4a ifdef code not used by Chrome/Android. +3993af12 Fix signed integer overflows. +f66f94ef anim_dump: small tool to dump frames from animated WebP +6eba857b Merge "rationalize the Makefile.am" +c5e34fba function definition cleanup +3822762a rationalize the Makefile.am +501ef6e4 configure style fix: animdiff -> anim_diff +f8bdc268 Merge "protect against NULL dump_folder[] value in ReadAnimatedImage()" +23bfc652 protect against NULL dump_folder[] value in ReadAnimatedImage() +8dc3d71b cosmetics,ReadAnimatedWebP: correct function comment +5bd40066 Merge changes I66a64a0a,I4d2e520f +7945575c cosmetics,webpinfo: remove an else after a return +8729fa11 cosmetics,cwebp: remove an else after a return +f324b7f9 cosmetics: normalize fn proto & decl param names +869eb369 CMake cleanups. +289e62a3 Remove declaration of unimplemented VP8ApplyNearLosslessPredict +20a94186 pnmdec,PAM: validate depth before calculating bytes_per_px +34130afe anim_encode: fix integer overflow +42c79aa6 Merge "Encoder: harmonize function suffixes" +b09307dc Encoder: harmonize function suffixes +bed0456d Merge "SSIM: harmonize the function suffix" +54f6a3cf lossless_sse2.c: fix some missed suffix changes +088f1dcc SSIM: harmonize the function suffix +86fc4dd9 webpdec: use ImgIoUtilCheckSizeArgumentsOverflow +08ea9ecd imageio: add ability restrict max image size +6f9daa4a jpegdec,ReadError: fix leaks on error +a0f72a4f VP8LTransformColorFunc: drop an non-respected 'const' from the signature. +8c934902 Merge "Lossess dec: harmonize the function suffixes" +622242aa Lossess dec: harmonize the function suffixes +1411f027 Lossless Enc: harmonize the function suffixes +24ad2e3c add const to two variables +46efe062 Merge "Allow the lossless cruncher to work for alpha." +8c3f9a47 Speed-up LZ77. +1aef4c71 Allow the lossless cruncher to work for alpha. +b8821dbd Improve the box LZ77 speed. +7beed280 add missing ()s to macro parameters +6473d20b Merge "fix Android standalone toolchain build" +dcefed95 Merge "build.gradle: fix arm64 build" +0c83a8bc Merge "yuv: harmonize suffix naming" +c6d1db4b fix Android standalone toolchain build +663a6d9d unify the ALTERNATE_CODE flag usage +73ea9f27 yuv: harmonize suffix naming +c71b68ac build.gradle: fix arm64 build +c4568b47 Rescaler: harmonize the suffix naming +6cb13b05 Merge "alpha_processing: harmonize the naming suffixes to be _C()" +83a3e69a Merge "simplify WEBP_EXTERN macro" +7295fde2 Merge "filters: harmonize the suffixes naming to _SSE2(), _C(), etc." +8e42ba4c simplify WEBP_EXTERN macro +331ab34b cost*.c: harmonize the suffix namings +b161f670 filters: harmonize the suffixes naming to _SSE2(), _C(), etc. +dec5e4d3 alpha_processing: harmonize the naming suffixes to be _C() +6878d427 fix memory leak in SDL_Init() +461ae555 Merge "configure: fix warnings in sdl check" +62486a22 configure: test for -Wundef +92982609 dsp.h: fix -Wundef w/__mips_dsp_rev +0265cede configure: fix warnings in sdl check +88c73d8a backward_references_enc.h: fix WINDOW_SIZE_BITS check +4ea49f6b rescaler_sse2.c: fix WEBP_RESCALER_FIX -> _RFIX typo +1b526638 Clean-up some CMake +87f57a4b Merge "cmake: fix gif lib detection when cross compiling" +b34a9db1 cosmetics,dec_sse2: remove some redundant comments +471c5755 cmake: fix gif lib detection when cross compiling +c793417a cmake: disable gif2webp if gif lib isn't found +dcbc1c88 cmake: split gif detection from IMG deps +66ad84f0 Merge "muxread: remove unreachable code" +50ec3ab7 muxread: remove unreachable code +7d67a164 Lossy encoding: smoothen transparent areas to improve compression +e50650c7 Merge "fix signature for DISABLE_TOKEN_BUFFER compilation" +671d2567 fix signature for DISABLE_TOKEN_BUFFER compilation +d6755580 cpu.cmake: use unique flag to test simd disable flags +28914528 Merge "Remove the argb* files." +8acb4942 Remove the argb* files. +3b62347b README: correct cmake invocation note +7ca0df13 Have the SSE2 version of PackARGB use common code. +7b250459 Merge "Re-use the transformed image when trying several LZ77 in lossless." +e132072f Re-use the transformed image when trying several LZ77 in lossless. +5d7a50ef Get code to compile in C++. +7b012987 configure: test for -Wparentheses-equality +f0569adb Fix man pages for multi-threading. +f1d5a397 multithread cruncher: only copy stats when picture->stats != NULL +f8c2ac15 Multi-thread the lossless cruncher. +a88c6522 Merge "Integrate a new LZ77 looking for matches in the neighborhood of a pixel only." +8f6df1d0 Unroll Predictors 10, 11 and 12. +355c3d1b Integrate a new LZ77 looking for matches in the neighborhood of a pixel only. +a1779a01 Refactor LZ77 handling in preparation for a new method. +67de68b5 Android.mk/build.gradle: fix mips build with clang from r14b +f209a548 Use the plane code and not the distance when computing statistics. +b903b80c Split cost-based backward references in its own file. +498cad34 Cosmetic changes in backward reference. +e4eb4587 lossless, VP8LTransformColor_C: make sure no overflow happens with colors. +af6deaff webpinfo: handle alpha flag mismatch +7caef29b Fix typo that creeped in. +39e19f92 Merge "near lossless: fix unsigned int overflow warnings." +9bbc0891 near lossless: fix unsigned int overflow warnings. +e1118d62 Merge "cosmetics,FindClosestDiscretized: use uint in mask creation" +186bc9b7 Merge "webpinfo: tolerate ALPH+VP8L" +b5887297 cosmetics,FindClosestDiscretized: use uint in mask creation +f1784aee near_lossless,FindClosestDiscretized: use unsigned ops +0d20abb3 webpinfo: tolerate ALPH+VP8L +972104b3 webpmux: tolerate false positive Alpha flag +dd7e83cc tiffdec,ReadTIFF: ensure data_size is < tsize_t max +d988eb7b tiffdec,MyRead: quiet -Wshorten-64-to-32 warning +dabda707 webpinfo: add support to parse Alpha bitstream +4c117643 webpinfo: correct background color output, BGRA->ARGB +defc98d7 Doc: clarify the role of quality in WebPConfig. +d78ff780 Merge "Fix code to compile with C++." +c8f14093 Fix code to compile with C++. +497dc6a7 pnmdec: sanitize invalid header output +d78e5867 Merge "configure: test for -Wconstant-conversion" +481e91eb Merge "pnmdec,PAM: set bytes_per_px based on depth when missing" +93b12753 configure: test for -Wconstant-conversion +645f0c53 pnmdec,PAM: set bytes_per_px based on depth when missing +e9154605 Merge "vwebp: activate GLUT double-buffering" +818d795b vwebp: activate GLUT double-buffering +d63e6f4b Add a man page for webpinfo +4d708435 Merge "NEON: implement ConvertRGB24ToY/BGR24/ARGB/RGBA32ToUV/ARGBToUV" +faf42213 NEON: implement ConvertRGB24ToY/BGR24/ARGB/RGBA32ToUV/ARGBToUV +b4d576fa Install man pages with CMake. +cbc1b921 webpinfo: add features to parse bitstream header +e644c556 Fix bad bit writer initialization. +b62cdad2 Merge "Implement a cruncher for lossless at method 6." +da3e4dfb use the exact constant for the gamma transfer function +a9c701e0 Merge "tiffdec: fix EXTRASAMPLES check" +adab8ce0 Implement a cruncher for lossless at method 6. +1b92b237 Merge "Fix VP8ApplyNearLossless to respect const and stride." +1923ff02 tiffdec: fix EXTRASAMPLES check +97cce5ba tiffdec: only request EXTRASAMPLES w/> 3 samples/px +0dcd85b6 Fix VP8ApplyNearLossless to respect const and stride. +f7682189 yuv: rationalize the C/SSE2 function naming +52245424 NEON implementation of some Sharp-YUV420 functions +690efd82 Avoid several backward reference copies. +4bb1f607 src/dec/vp8_dec.h, cosmetics: fix comments +285748be cmake: build/install webpinfo +78fd199c backward_references_enc.c: clear -Wshadow warnings +ae836410 WebPLog2FloorC: clear -Wshadow warning +d0b7404e Merge "WASM support" +134e314f WASM support +c08adb6f Merge "VP8LEnc: remove use of BitsLog2Ceiling()" +28c37ebd VP8LEnc: remove use of BitsLog2Ceiling() +2cb58ab2 webpinfo: output format as a human readable string +bb175a93 Merge "rename some symbols clashing with MSVC headers" +39eda658 Remove a duplicated pixel hash implementation. +36b8274d rename some symbols clashing with MSVC headers +274daf54 Add webpinfo tool. +ec5036e4 add explicit reference to /usr/local/{lib,inc} +18f0dfac Merge "fix TIFF encoder regarding rgbA/RGBA" +4e2b0b50 Merge "webpdec.h: fix a doc typo" +e2eeabff Merge "Install binaries, libraries and headers in CMake." +836607e6 webpdec.h: fix a doc typo +9273e441 fix TIFF encoder regarding rgbA/RGBA +17e3c11f Add limited PAM decoding support +5f624871 Install binaries, libraries and headers in CMake. +976adac1 Merge "lossless incremental decoding: fix missing eos_ test" +f8fad4fa lossless incremental decoding: fix missing eos_ test +27415d41 Merge "vwebp_sdl: fix the makefile.unix" +49566182 Merge "ImgIoUtilWriteFile(): use ImgIoUtilSetBinaryMode" +6f75a51b Analyze the transform entropy on the whole image. +a5e4e3af Use palette only if we can in entropy analysis. +75a9c3c4 Improve compression by better entropy analysis. +39cf6f4f vwebp_sdl: fix the makefile.unix +699b0416 ImgIoUtilWriteFile(): use ImgIoUtilSetBinaryMode +7d985bd1 Fix small entropy analysis bug. +6e7caf06 Optimize the color cache size. +833c9219 More efficient stochastic histogram merge. +5183326b Refactor the greedy histogram merge. +99f6f462 Merge "histogram_enc.c,MyRand: s/ul/u/ for unsigned constants" +80a22186 ssim.c: remove dead include +a128dfff histogram_enc.c,MyRand: s/ul/u/ for unsigned constants +693bf74e move the SSIM calculation code in ssim.c / ssim_sse2.c +10d791ca Merge "Fix the random generator in HistogramCombineStochastic." +fa63a966 Fix the random generator in HistogramCombineStochastic. +16be192f VP8LSetBitPos: remove the eos_ setting +027151ca don't erase the surface before blitting. +4105d565 disable WEBP_USE_XXX optimisations when EMSCRIPTEN is defined +9ee32a75 Merge "WebP-JS: emscripten-based Javascript decoder" +ca9f7b7d WebP-JS: emscripten-based Javascript decoder +868aa690 Perform greedy histogram merge in a unified way. +5b393f2d Merge "fix path typo for vwebp_sdl in Makefile.vc" +e0012bea CMake: only use libwebpdecoder for building dwebp +84c2a7b0 fix path typo for vwebp_sdl in Makefile.vc +1b0e4abf Merge "Add a flag to disable SIMD optimizations." +32263250 Add a flag to disable SIMD optimizations. +b494fdec optimize the ARGB->ARGB Import to use memcpy +f1536039 Merge "ReadWebP: decode directly into a pre-allocated buffer" +e69ed291 ReadWebP: decode directly into a pre-allocated buffer +57d8de8a Merge "vwebp_sdl: simple viewer based on SDL" +5cfd4ebc LZ77 interval speedups. Faster, smaller, simpler. +1e7ad88b PNM header decoder: add some basic numerical validation +17c7890c Merge "Add a decoder only library for WebP in CMake." +be733786 Merge "Add clang build fix for MSA" +03cda0e4 Add a decoder only library for WebP in CMake. +aa893914 Add clang build fix for MSA +31a92e97 Merge "imageio: add limited PNM support for reading" +dcf9d82a imageio: add limited PNM support for reading +6524fcd6 vwebp_sdl: simple viewer based on SDL +6cf24a24 get_disto: fix reference file read +43d472aa Merge tag 'v0.6.0' +50d1a848 update ChangeLog (tag: v0.6.0, origin/0.6.0) +20a7fea0 extras/Makefile.am: fix libwebpextras.la reference +415f3ffe update ChangeLog (tag: v0.6.0-rc3) +3c6d1224 update NEWS +ee4a4141 update AUTHORS +32ed856f Fix "all|no frames are keyframes" settings. +1c3190b6 Merge "Fix "all|no frames are keyframes" settings." +f4dc56fd disable GradientUnfilter_NEON +4f3e3bbd disable GradientUnfilter_NEON +2dc0bdca Fix "all|no frames are keyframes" settings. +0d8e0588 img2webp: treat -loop as a no-op w/single images +b0450139 ReadImage(): restore size reporting +0ad3b4ef update ChangeLog (tag: v0.6.0-rc2) +6451709e img2webp,get_disto: fix image decode w/WIC builds +92504d21 get_disto: make ReadPicture() return a bool +c3e4b3a9 update NEWS +3363eb6d man/img2webp.1: fix formatting warning +4d1312f2 update NEWS +36c42ea4 bump version to 0.6.0 +bb498a51 update AUTHORS +84cef16f Makefile.vc: fix CFG=debug-dynamic build +919f9e2f Merge "add .rc files for windows dll versioning" +f1ae8af4 Merge ".gitignore: add img2webp" +4689ce16 cwebp: add a -sharp_yuv option for 'sharp' RGB->YUV conversion +79bf46f1 rename the pretentious SmartYUV into SharpYUV +eb1dc89a silently expose use_delta_palette in the WebPConfig API +c85b0dde .gitignore: add img2webp +43d3f01a add .rc files for windows dll versioning +668e1dd4 src/{dec,enc,utils}: give filenames a unique suffix +0e6b7f33 Merge "iosbuild.sh: only add required headers to framework" +29ed6f9a iosbuild.sh: only add required headers to framework +71c53f1a NEON: speed-up strong filtering +73f567ea Merge "get_disto: remove redundant reader check" +9e14276f Merge "makefiles: prune get_disto & webp_quality deps" +99965bac Merge "Makefile.vc: add get_disto.exe, webp_quality.exe" +d4912238 get_disto: remove redundant reader check +ea482409 makefiles: prune get_disto & webp_quality deps +2ede5a19 Makefile.vc: add get_disto.exe, webp_quality.exe +a345068a ARM: speed up bitreader by avoiding tables +1dc82a6b Merge "introduce a generic GetCoeffs() function pointer" +8074b89e introduce a generic GetCoeffs() function pointer +749a45a5 Merge "NEON: implement alpha-filters (horizontal/vertical/gradient)" +74c053b5 Merge "NEON: fix overflow in SSE NxN calculation" +0a3aeff7 Merge "dsp: WebPExtractGreen function for alpha decompression" +1de931c6 NEON: implement alpha-filters (horizontal/vertical/gradient) +9b3aca40 NEON: fix overflow in SSE NxN calculation +1c07a3c6 dsp: WebPExtractGreen function for alpha decompression +9ed5e3e5 use pointers for WebPRescaler's in WebPDecParams +db013a8d Merge "ARM: don't use USE_GENERIC_TREE" +fcd4784d use a 8b table for C-version for clz() +fbb5c473 ARM: don't use USE_GENERIC_TREE +8fda5612 Merge "add a kSlowSSSE3 feature for CPUInfo" +86bbd245 add a kSlowSSSE3 feature for CPUInfo +7c2779e9 Get code to fully compile in C++. +250c3586 Merge "When compiling as C++, avoid narrowing warnings." +c0648ac2 When compiling as C++, avoid narrowing warnings. +0d55f60c 40% faster ApplyAlphaMultiply_SSE2 +49d0280d NEON: implement several alpha-processing functions +48b1e85f SSE2: 15% faster alpha-processing functions +e3b8abbc fix warning from static analysis. +28fe054e SSE2: 30% faster ApplyAlphaMultiply() +f44acd25 Merge "Properly compute the optimal color cache size." +527844fe Properly compute the optimal color cache size. +be0ef639 fix a comment typo +8874b162 Fix a non-deterministic color cache size computation. +d712e20d Do not allow a color cache size bigger than the number of colors. +ecff04f6 re-introduce some comments in Huffman Cost. +259e9828 replace 'ptr + y * stride' by 'ptr += stride' +00b08c88 Merge "NEON: 5% faster conversion to RGB565 and RGBA4444" +0e7f4447 Merge "NEON: faster fancy upsampling" +b016cb91 NEON: faster fancy upsampling +1cb63801 Call the C function to finish off lossless SSE loops only when necessary. +875fafc1 Implement BundleColorMap in SSE2. +3674d49e Merge "remove Clang warnings with unused arch arguments." +f04eb376 Merge tag 'v0.5.2' +341d711c NEON: 5% faster conversion to RGB565 and RGBA4444 +abb54827 remove Clang warnings with unused arch arguments. +ece9684f update ChangeLog (tag: v0.5.2-rc2, tag: v0.5.2, origin/0.5.2) +aa7744ca anim_util: quiet implicit conv warnings in 32-bit +d9120271 jpegdec: correct ContextFill signature +24eb3940 Remove some errors when compiling the code as C++. +a4a8e5f3 vwebp: clear canvas during resize w/o animation +67c25ad5 vwebp: clear canvas during resize w/o animation +a4bbe4b3 fix indentation +31ca2a80 tiffdec: restore libtiff 3.9.x compatibility +b2f77b57 update NEWS +5ab6d9de AnimEncoder: avoid freeing uninitialized memory pointer. +f29bf582 WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless. +3ebe1c00 AnimEncoder: avoid freeing uninitialized memory pointer. +df780e0e fix a potential overflow with MALLOC_LIMIT +58fc5078 Merge "PredictorSub: implement fully-SSE2 version" +9cc42167 PredictorSub: implement fully-SSE2 version +0aa1f35c remove dependency of imageio/ to stopwatch.h +cb9ec84b Merge "remove the dependency to stop_watch.[ch] in imageio" +dc0c01fb Merge "anim_util: quiet implicit conv warnings in 32-bit" +827d3c50 Merge "fix a potential overflow with MALLOC_LIMIT" +1e2e25b0 anim_util: quiet implicit conv warnings in 32-bit +218460cd bump version to 0.5.2 +de7d654d update AUTHORS & .mailmap +273367c1 Merge "dsp/lossless.c,cosmetics: fix indent" +76bbcf2e fix a potential overflow with MALLOC_LIMIT +8ac1abfe Merge "jpegdec: correct ContextFill signature" +cb215aed remove the dependency to stop_watch.[ch] in imageio +2423017a dsp/lossless.c,cosmetics: fix indent +74a12b10 iosbuild.sh: add WebPDecoder.framework + encoder +a9cc7621 Merge "iosbuild.sh: add WebPDecoder.framework + encoder" +fbba5bc2 optimize predictor #1 in plain-C For some reason, gcc has hard time inlining this one... +9ae0b3f6 Merge "SSE2: slightly (~2%) faster Predictor #1" +c1f97bd7 SSE2: slightly (~2%) faster Predictor #1 +ea664b89 SSE2: 10% faster Predictor #11 +be7dcc08 AnimEncoder: Correctly skip a frame when sub-rectangle is empty. +40885830 Fix assertions in WebPRescalerExportRow() +1d5046d1 iosbuild.sh: add WebPDecoder.framework + encoder +cec72014 jpegdec: correct ContextFill signature +8f38c72e fix a typo in WebPPictureYUVAToARGB's doc +33ca93f9 systematically call WebPDemuxReleaseIterator() on dec->prev_iter_ +76e19073 doc: use two's complement explicitly for uint8->int8 conversion +f91ba963 Anim_encoder: correctly handle enc->prev_candidate_undecided_ +25d74e65 WebPPictureDistortion(): free() -> WebPSafeFree() +03f1c008 mux/Makefile.am: add missing -lm +58410cd6 fix bug in RefineUsingDistortion() +e168af8c fix filtering auto-adjustment +ed9dec41 fix doc and code snippet for WebPINewDecoder() doc +3c49178f prevent 32b overflow for very large canvas_width / height +9595f290 fix anim_util.c compilation when HAVE_GIF is not defined. +7ec9552c Make gif transparent color to be transparent black +b3fb8bb6 slightly faster Predictor #11 in NEON +9871335f Add a CMake option for WEBP_SWAP_16BIT_CSP. +0ae32226 Fix missing cpu-features for Android. +ab4c8056 cpu.cmake: improve webp_check_compiler_flag output +eec5fa3a Provide support for CMake on Android studio 2.2. +004d5690 Split the main CMake file. +4fe5d588 Android.mk: use -fvisibility=hidden +bd63a31a vwebp: ensure setenv() is available in stdlib.h +363a5681 vwebp: handle window resizing properly +a0d2753f lower WEBP_MAX_ALLOCABLE_MEMORY default +31fe11a5 fix infinite loop in case of PARTITION0 overflow +532215dd Change the rule of picking UV mode in MBAnalyzeBestUVMode() +9c75dbd3 cwebp.1: improve some grammar +af2e05cb vwebp: Clear previous frame when a key triggers a redraw +26ffa296 Add descriptions of default configuration in help info. +7416280d Fix an unsigned integer overflow error in enc/cost.h +13cf1d2e Do token recording and counting in a single loop +eb9a4b97 Reset segment id if we decide not to update segment map +42ebe3b7 configure: fix NEON flag detection under gcc 6 +76ebbfff NEON: implement predictor #13 +95b12a08 Merge "Revert Average3 and Average4" +54ab2e75 Revert Average3 and Average4 +fe12330c 3-5% faster Predictor #5, #6, #7 and #10 for NEON +fbfb3bef ~2% faster predictor #10 for NEON +d4b7d801 lossless_sse2: use the local functions +a5e3b225 Lossless decoder SSE2 improvements. +58a1f124 ~2% faster predictor #12 in NEON. +906c3b63 Merge "Implement lossless transforms in NEON." +d23abe4e Implement lossless transforms in NEON. +2e6cb6f3 Give more flexibility to the predictor generating macro. +28e0bb70 Merge "Fix race condition in multi-threading initialization." +64704530 Fix race condition in multi-threading initialization. +bded7848 img2webp: fix default -lossless value and use pic.argb=1 +0e61a513 Merge "img2webp: convert a sequence of images to an animated webp" +1cc79e92 AnimEncoder: Correctly skip a frame when sub-rectangle is empty. +03f40955 img2webp: convert a sequence of images to an animated webp +ea72cd60 add missing 'extern' keyword for predictor dcl +67879e6d SSE implementation of decoding predictors. +34aee990 Merge "vwebp: make 'd' key toggle the debugging of fragments" +a41296ae Fix potentially uninitialized value. +c85adb33 vwebp: make 'd' key toggle the debugging of fragments +4239a148 Make the lossless predictors work on a batch of pixels. +bc18ebad fix extra 'const's in signatures +71e2f5ca Remove memcpy in lossless decoding. +7474d46e Do not use a register array in SSE. +67748b41 Improve latency of FTransform2. +16951b19 Merge "Provide an SSE implementation of ConvertBGRAToRGB" +6540cd0e Provide an SSE implementation of ConvertBGRAToRGB +de568abf Android.mk: use -fvisibility=hidden +3c2a61b0 remove some unneeded casts +9ac063c3 add dsp functions for SmartYUV +22efabdd Merge "smart_yuv: switch to planar instead of packed r/g/b processing" +1d6e7bf3 smart_yuv: switch to planar instead of packed r/g/b processing +0a3838ca fix bug in RefineUsingDistortion() +c0699515 webpmux -duration: set default 'end' value equal to 'start' +83cbfa09 Import: use relative pointer offsets +a1ade40e PreprocessARGB: use relative pointer offsets +fd4d090f ConvertWRGBToYUV: use relative pointer offsets +9daad459 ImportYUVAFromRGBA: use relative pointer offsets +f90c60d1 Merge "add a "-duration duration,start,end" option to webpmux" +3f182d36 add a "-duration duration,start,end" option to webpmux +342e15f0 Import: use relative pointer offsets +1147ab4e PreprocessARGB: use relative pointer offsets +e4cd4daf fix filtering auto-adjustment +e7152856 fix doc and code snippet for WebPINewDecoder() doc +de9fa507 ConvertWRGBToYUV: use relative pointer offsets +deb1b831 ImportYUVAFromRGBA: use relative pointer offsets +c284780f imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow +e375080d gifdec,Remap: avoid out of bounds colormap read +c222a053 additional fix for stride type as size_t +bb233617 fix potential overflow when width * height * 4 >= (1<<32) +883d41fb gif2webp: fix crash with NULL extension data +cac9a36a gifdec,Remap: avoid out of bounds colormap read +4595e01f Revert "gifdec,Remap: avoid out of bounds colormap read" +fb52d443 gifdec: make some constants unsigned +f048d38d gifdec,Remap: avoid out of bounds colormap read +31b1e343 fix SSIM metric ... by ignoring too-dark area +2f51b614 introduce WebPPlaneDistortion to compute plane distortion +0104d730 configure: fix NEON flag detection under gcc 6 +265abbe9 Merge "additional fix for stride type as size_t" +f7601aa6 Merge "Introduce a generic WebPGetImageReader(type) function" +ce873320 Introduce a generic WebPGetImageReader(type) function +2a2773ea imageio/*dec,Read*: add input parameter checks +9f5c8eca additional fix for stride type as size_t +4eb5df28 remove unused stride fields from VP8Iterator +11bc423a MIN_LENGTH cleanups. +273d035a Merge "fix a typo in WebPPictureYUVAToARGB's doc" +4db82a17 Merge "fix potential overflow when width * height * 4 >= (1<<32)" +e2affacc fix potential overflow when width * height * 4 >= (1<<32) +dc789ada fix a typo in WebPPictureYUVAToARGB's doc +539f5a68 Fix non-included header in config.c. +aaf2a6a6 systematically call WebPDemuxReleaseIterator() on dec->prev_iter_ +20ef9915 Merge "imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow" +bc86b7a8 imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow +806f6279 gif2webp: fix crash with NULL extension data +68ae5b67 Add libwebp/src/mux/animi.h +28ce3043 Remove some errors when compiling the code as C++. +b34abcb8 Favor keeping the areas locally similar in spatial prediction mode selection +ba843a92 fix some SSIM calculations +51b71fd2 Merge "vwebp: ensure setenv() is available in stdlib.h" +fb01743a get_disto: fix the r/g/b order for luma calculation +bfab8947 vwebp: ensure setenv() is available in stdlib.h +9310d192 vwebp: handle window resizing properly +f79450ca Speedup ApplyMap. +cfdda7c6 Merge "prevent 32b overflow for very large canvas_width / height" +e36396ba Merge "get_disto: new option to compute SSIM map and convert to gray" +18a9a0ab Add an API to import a color-mapped image. +30d43706 Speed-up Combined entropy for palettized histograms. +36aa087b get_disto: new option to compute SSIM map and convert to gray +86a84b35 2x faster SSE2 implementation of SSIMGet +b8384b53 lower WEBP_MAX_ALLOCABLE_MEMORY default +1c364400 prevent 32b overflow for very large canvas_width / height +eee0cce1 Merge "Small LZ77 speedups." +5f1caf29 Small LZ77 speedups. +1effde7b fix anim_util.c compilation when HAVE_GIF is not defined. +a2fe9bf4 Speedup TrellisQuantizeBlock(). +573cce27 smartYUV improvements +21e7537a fix infinite loop in case of PARTITION0 overflow +053a1565 Merge "Change the rule of picking UV mode in MBAnalyzeBestUVMode()" +1377ac2e Change the rule of picking UV mode in MBAnalyzeBestUVMode() +7c1fb7d0 fix uint32_t initialization (0. -> 0) +bfff0bf3 speed-up SSIM calculation +64577de8 De-VP8L-ize GetEntropUnrefinedHelper. +a7be7328 Merge "refactor the PSNR / SSIM calculation code" +50c3d7da refactor the PSNR / SSIM calculation code +d6228aed indentation fix after I7055d3ee3bd7ed5e78e94ae82cb858fa7db3ddc0 +dd538b19 Remove unused declaration. +6cc48b17 Move some lossless logic out of dsp. +78363e9e Merge "Remove a redundant call to InitLeft() in VP8IteratorReset()" +ffd01929 Refactor VP8IteratorNext(). +c4f6d9c9 Remove a redundant call to InitLeft() in VP8IteratorReset() +c27d8210 Merge "smartYUV: simplify main loop" +07795296 smartYUV: simplify main loop +c9b45863 Split off common lossless dsp inline functions. +490ae5b1 smartYUV: improve initial state for faster convergence +894232be smartYUV: fix and simplify the over-zealous stop criterion +8de08483 Remove unused code in webpi.h +41cab7fe imageio/Android.mk: correct imagedec dependencies +82c91c70 Merge "libimageenc.a: extract image-saving code from dwebp" +af1ad3e2 libimageenc.a: extract image-saving code from dwebp +dd7309e3 Merge "doc: use two's complement explicitly for uint8->int8 conversion" +6105777e Merge "add gif2webp to CMake" +13ae011e doc: use two's complement explicitly for uint8->int8 conversion +4bda0cfb add gif2webp to CMake +6029c7fe Merge "remove mention of fragment, frgm, FRGM, etc." +545c147f remove mention of fragment, frgm, FRGM, etc. +5b46f7fc cwebp.1: improve some grammar +9e478f80 dec/vp8l.c: add assertions in EmitRescaledRowsRGBA/YUVA +43bd8958 Make gif transparent color to be transparent black +0887fc2d Merge "get_disto: add a '-o file' option to save a diff map" +0de48e18 get_disto: add a '-o file' option to save a diff map +0a57ad0d cosmetics: WebPSafeAlloc -> WebPSafeMalloc +0a4699bc Merge "WebPPictureDistortion(): free() -> WebPSafeFree()" +29fedbf5 Anim_encoder: correctly handle enc->prev_candidate_undecided_ +32dead4e WebPPictureDistortion(): free() -> WebPSafeFree() +85cd5d06 Smarter LZ77 for uniform regions. +6585075f Change PixelsAreSimilar() to handle black pixels correctly. +c0a27fd2 vwebp: Clear previous frame when a key triggers a redraw +57a5e3b6 webp_quality should return '0' in case of success. +7f1b897b Faster stochastic histogram merging. +48c810b8 Merge "remove WEBP_FORCE_ALIGNED and use memcpy() instead." +3884972e remove WEBP_FORCE_ALIGNED and use memcpy() instead. +485cac1a switch libimagedec.a and libimageio_util.a to avoid undefined symbol +005e15b1 Merge "{extras,mux}/Makefile.am: add missing -lm" +6ab496ed fix some 'unsigned integer overflow' warnings in ubsan +8a4ebc6a Revert "fix 'unsigned integer overflow' warnings in ubsan" +9d4f209f Merge changes I25711dd5,I43188fab +e44f5248 fix 'unsigned integer overflow' warnings in ubsan +27b5d991 Fix assertions in WebPRescalerExportRow() +74f6f9e7 Add descriptions of default configuration in help info. +aaf2530c {extras,mux}/Makefile.am: add missing -lm +1269dc7c Refactor VP8LColorCacheContains() +40872fb2 dec_neon,NeedsHev: micro optimization +7b54e26b Add a CMake option for WEBP_SWAP_16BIT_CSP. +d2223d8d Fix missing cpu-features for Android. +bf16a4b4 Merge "cpu.cmake: improve webp_check_compiler_flag output" +ee1057e3 cpu.cmake: improve webp_check_compiler_flag output +b551e587 cosmetics: add {}s on continued control statements +d2e4484e dsp/Makefile.am: put msa source in correct lib +c7f66c82 Merge "utils/thread.c,cosmetics: join a few lines" +98d8f295 Merge "examples/Makefile.am,cosmetics: sort binary targets" +39f4ffbc utils/thread.c,cosmetics: join a few lines +a86ce2b1 Merge "extras/Makefile.am: don't install libwebpextras" +6fa9fe24 extras/Makefile.am: don't install libwebpextras +0b2c58a9 Fix an unsigned integer overflow error in enc/cost.h +d7ce4a2e examples/Makefile.am,cosmetics: sort binary targets +386e4ba2 Reset segment id if we decide not to update segment map +7b87e848 Merge "Add MSA optimized YUV to RGB upsampling functions" +d3ddacb6 Add MSA optimized YUV to RGB upsampling functions +eb98d8d8 webp_quality: detect lossless format and features +ebee57f4 move imageio/example_util.[hc] (back to) examples/ +99542bbf webpdec: s/ExUtil// +da573cf4 imageio_util: s/ExUtil/ImgIoUtil/ +bdda5bd4 split example_util.h +15ed462b .gitignore: add extras/{get_disto,webp_quality} +7be57489 Merge "VP8EstimateQuality(): roughty estimate webp bitstream quality factor" +57020525 Makefile.vc: add missing imageio target +e8ab6a82 VP8EstimateQuality(): roughty estimate webp bitstream quality factor +fee7b3d6 Merge "'extras/get_disto' example: compute PSNR between two files" +1e7d4401 'extras/get_disto' example: compute PSNR between two files +4cecab63 pngdec.c,jpegdec.[hc]: remove unnecessary includes +259f0434 makefile.unix: normalize image decode lib name +ed34c39b fix: examples/libexample_dec.a => imageio/libexample_dec.a +33d8d0d4 Merge "move examples/{example_util,image_dec} to imageio/" +c960b82e Merge "extras.h: correct include guard" +fe3cd28a Merge ".gitignore: add .gradle, /build" +45fbeba5 Merge "Do token recording and counting in a single loop" +4f33c820 .gitignore: add .gradle, /build +c379b55a move examples/{example_util,image_dec} to imageio/ +5108d9aa extras.h: correct include guard +ad497fbc move src/extras to the top-level +0c0fb832 Do token recording and counting in a single loop +9ac74f92 Add MSA optimized rescaling functions +cb19dbc1 Add MSA optimized color transform functions +3f4042b5 WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless. +5e2eb89e cosmetics,dsp/*msa.c: associate '*' with the type +5b60db5c FastMBAnalyze() for quick i16/i4 decision +567e6977 Add MSA optimized CollectHistogram function +c54ab8dd Add MSA optimized quantization functions +ec6f68c5 Merge "Remove QuantizeBlockWHT() in enc.c" +2a5c417c Apply the RLE heuristic to LZ77. +91b59e88 Remove QuantizeBlockWHT() in enc.c +fe572737 Add MSA optimized SSE functions +6b53ca87 cosmetics,(dec|enc)_sse2.c: fix indent +b15d00d9 Merge "Add MSA optimized encoder IntraChromaPreds function" +afe3cec8 Add MSA optimized encoder IntraChromaPreds function +fc8cad9f reduce the number of malloc/free cycles in huffman.c +7b4b05e0 Add MSA optimized encoder Intra16Preds function +c18787a0 Add MSA optimized encoder Intra4Preds function +479d1908 webpmux: Also print compression info per frame. +a80e8cfd Provide support for CMake on Android studio 2.2. +6c628410 Split the main CMake file. +bbb6ecd9 Merge "Add MSA optimized distortion functions" +7915396f Add MSA optimized distortion functions +652e944f Merge "build.gradle: remove tab" +c0991a14 io,EmitRescaledAlphaYUV: factor out a common expr +48bf5ed1 build.gradle: remove tab +bfef6c9f Merge tag 'v0.5.1' +3d97bb75 update ChangeLog (tag: v0.5.1, origin/0.5.1) +deb54d91 Clarify the expected 'config' lifespan in WebPIDecode() +435308e0 Add MSA optimized encoder transform functions +dce64bfa Add MSA optimized alpha filter functions +429120d0 Add MSA optimized color transform functions +c7e2d245 update ChangeLog (tag: v0.5.1-rc5) +55b2fede normalize the macros' "do {...} while (0)" constructs +701c772e Add MSA optimized colorspace conversion functions +c7eb06f7 Fix corner case in CostManagerInit. +f918cb10 fix rescaling bug: alpha plane wasn't filled with 0xff +ab7937a5 gif2webp: normalize the number of .'s in the help message +3cdec847 vwebp: normalize the number of .'s in the help message +bdf6241e cwebp: normalize the number of .'s in the help message +06a38c7b fix rescaling bug: alpha plane wasn't filled with 0xff +319e37be Improve lossless compression. +6a197937 Add MSA optimized intra pred chroma functions +447adbce 'our bug tracker' -> 'the bug tracker' +97b9e644 normalize the number of .'s in the help message +293d786f Added MSA optimized intra prediction 16x16 functions +0afa0ce2 Added MSA optimized intra prediction 4x4 functions +a6621bac Added MSA optimized simple edge filtering functions +bb50bf42 pngdec,ReadFunc: throw an error on invalid read +38063af1 decode.h,WebPGetInfo: normalize function comment +1ebf193c Added MSA optimized chroma edge filtering functions +9ad2352d Merge "Added MSA optimized edge filtering functions" +60751096 Added MSA optimized edge filtering functions +9e8e1b7b Inline GetResidual for speed. +7d58d1b7 Speed-up uniform-region processing. +8ec7032b simplify HistogramCombineEntropyBin() +23e29cb1 Merge "Fix a boundary case in BackwardReferencesHashChainDistanceOnly." into 0.5.1 +472a049b remove bin_map[] allocation altogether +0bb23b2c free -> WebPSafeFree() +a977b4b5 Merge "rewrite the bin_map clustering to use less memory" +3591ba66 rewrite the bin_map clustering to use less memory +e6ac450c utils.[hc]: s/MAX_COLOR_COUNT/MAX_PALETTE_SIZE/ +e7b91772 Merge "DecodeImageData(): change the incorrect assert" into 0.5.1 +2abfa54f DecodeImageData(): change the incorrect assert +5a48fcd8 Merge "configure: test for -Wfloat-conversion" +0174d18d Fix a boundary case in BackwardReferencesHashChainDistanceOnly. +6a9c262a Merge "Added MSA optimized transform functions" +cfbcc5ec Make sure to consider small distances in LZ77. +5e60c42a Added MSA optimized transform functions +3dc28d76 configure: test for -Wfloat-conversion +f2a0946a add some asserts to delimit the perimeter of CostManager's operation +9a583c66 fix invalid-write bug for alpha-decoding +f66512db make gradlew executable +6fda58f1 backward_references: quiet double->int warning +a48cc9d2 Merge "Fix a compression regression for images with long uniform regions." into 0.5.1 +cc2720c1 Merge "Revert an LZ77 boundary constant." into 0.5.1 +059aab4f Fix a compression regression for images with long uniform regions. +b0c7e49e Check more backward matches with higher quality. +a3611513 Revert an LZ77 boundary constant. +8190374c README: fix typo +7551db44 update NEWS +0fb2269c bump version to 0.5.1 +f4537610 update AUTHORS & .mailmap +3259571e Refactor GetColorPalette method. +1df5e260 avoid using tmp histogram in PreparePair() +7685123a fix comment typos +a246b921 Speedup backward references. +76d73f18 Merge "CostManager: introduce a free-list of ~10 intervals" +eab39d81 CostManager: introduce a free-list of ~10 intervals +4c59aac0 Merge "mips msa webp configuration" +043c33f1 Merge "Improve speed and compression in backward reference for lossless." +71be9b8c Merge "clarify variable names in HistogramRemap()" +0ba7fd70 Improve speed and compression in backward reference for lossless. +0481d42a CostManager: cache one interval and re-use it when possible +41b7e6b5 Merge "histogram: fix bin calculation" +96c3d624 histogram: fix bin calculation +fe9e31ef clarify variable names in HistogramRemap() +ce3c8247 disable near-lossless quantization if palette is used +e11da081 mips msa webp configuration +5f8f998d mux: Presence of unknown chunks should trigger VP8X chunk output. +cadec0b1 Merge "Sync mips32 and dsp_r2 YUV->RGB code with C verison" +d9637758 Compute the hash chain once and for all for lossless compression. +50a48665 Sync mips32 and dsp_r2 YUV->RGB code with C verison +eee788e2 Merge "introduce a common signature for all image reader function" +d77b877c introduce a common signature for all image reader function +ca8d9519 remove some obsolete TODOs +ae2a7222 collect all decoding utilities from examples/ in libexampledec.a +0b8ae852 Merge "Move DitherCombine8x8 to dsp/dec.c" +77cad885 Merge "ReadWebP: avoid conversion to ARGB if final format is YUVA" +ab8d6698 ReadWebP: avoid conversion to ARGB if final format is YUVA +f8b7ce9e Merge "test pointer to NULL explicitly" +5df6f214 test pointer to NULL explicitly +77f21c9c Move DitherCombine8x8 to dsp/dec.c +c9e6d865 Add gradle support +c65f41e8 Revert "Add gradle support" +bf731ede Add gradle support +08333b85 WebPAnimEncoder: Detect when canvas is modified, restore only when needed. +0209d7e6 Merge "speed-up MapToPalette() with binary search" +fdd29a3d speed-up MapToPalette() with binary search +cf4a651b Revert "Refactor GetColorPalette method." +0a27aca3 Merge changes Idfa8ce83,I19adc9c4 +f25c4406 WebPAnimEncoder: Restore original canvas between multiple encodes. +169004b1 Refactor GetColorPalette method. +576362ab VP8LDoFillBitWindow: support big-endian in fast path +ac49e4e4 bit_reader.c: s/VP8L_USE_UNALIGNED_LOAD/VP8L_USE_FAST_LOAD/ +d39ceb58 VP8LDoFillBitWindow: remove stale TODO +2ec2de14 Merge "Speed-up BackwardReferencesHashChainDistanceOnly." +3e023c17 Speed-up BackwardReferencesHashChainDistanceOnly. +f2e1efbe Improve near lossless compression when a prediction filter is used. +e15afbce dsp.h: fix ubsan macro name +e53c9ccb dsp.h: add WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW +af81fdb7 utils.h: quiet -fsanitize=undefined warnings +ea0be354 dsp.h: remove utils.h include +cd276aec utils/*.c: ../utils/utils.h -> ./utils.h +c8927131 utils/Makefile.am: add some missing headers +ea24e026 Merge "dsp.h: add WEBP_UBSAN_IGNORE_UNDEF" +369e264e dsp.h: add WEBP_UBSAN_IGNORE_UNDEF +0d020a78 Merge "add runtime NEON detection" +5ee2136a Merge "add VP8LAddPixels() to lossless.h" +47435a61 add VP8LAddPixels() to lossless.h +8fa6ac68 remove two ubsan warnings +74fb56fb add runtime NEON detection +4154a839 MIPS update to new Unfilter API +c80b9fc8 Merge "cherry-pick decoder fix for 64-bit android devices" +6235147e cherry-pick decoder fix for 64-bit android devices +d41b8c43 configure: test for -Wformat-* w/-Wformat present +5f95589f Fix WEBP_ALIGN in case the argument is a pointer to a type larger than a byte. +2309fd5c replace num_parts_ by num_parts_minus_one_ (unsigned) +9629f4bc SimplifySegments: quiet -Warray-bounds warning +de47492e Merge "update the Unfilter API in dsp to process one row independently" +2102ccd0 update the Unfilter API in dsp to process one row independently +e3912d56 WebPAnimEncoder: Restore canvas before evaluating blending possibility. +6e12e1e3 WebPAnimEncoder: Fix for single-frame optimization. +602f344a Merge changes I1d03acac,Ifcb64219 +95ecccf6 only apply color-mapping for alpha on the cropped area +47dd0708 anim_diff: Add an experimental option for max inter-frame diff. +aa809cfe only allocate alpha_plane_ up to crop_bottom row +31f2b8d8 WebPAnimEncoder: FlattenSimilarPixels(): look for similar +774dfbdc perform alpha filtering within the decoding loop +a4cae68d lossless decoding: only process decoded row up to last_row +238cdcdb Only call WebPDequantizeLevels() on cropped area +cf6c713a alpha: preparatory cleanup +b95ac0a2 Merge "VP8GetHeaders(): initialize VP8Io with sane value for crop/scale dimensions" +89231394 VP8GetHeaders(): initialize VP8Io with sane value for crop/scale dimensions +5828e199 use_8b_decode -> use_8b_decode_ +8dca0247 fix bug in alpha.c that was triggering a memory error in incremental mode +9a950c53 WebPAnimEncoder: Disable filtering when blending is used with lossy encoding. +eb423903 WebPAnimEncoder: choose max diff for framerect based on quality. +ff0a94be WebPAnimEncoder lossy: ignore small pixel differences for frame rectangles. +f8040084 gif2webp: Remove the 'prev_to_prev_canvas' buffer. +6d8c07d3 Merge "WebPDequantizeLevels(): use stride in CountLevels()" +d96fe5e0 WebPDequantizeLevels(): use stride in CountLevels() +ec1b2407 WebPPictureImport*: check output pointer +c0768769 Merge "Revert "Re-enable encoding of alpha plane with color cache for next release."" +41f14bcb WebPPictureImport*: check src pointer +64eed387 Pass stride parameter to WebPDequantizeLevels() +97934e24 Revert "Re-enable encoding of alpha plane with color cache for next release." +e88c4ca0 fix -m 2 mode-cost evaluation (causing partition0 overflow) +4562e83d Merge "add extra meaning to WebPDecBuffer::is_external_memory" +abdb109f add extra meaning to WebPDecBuffer::is_external_memory +875aec70 enc_neon,cosmetics: break long comment +71e856cf GetMBSSIM,cosmetics: fix alignment +a90edffb fix missing 'extern' for SSIM function in dsp/ +423ecaf4 move some SSIM-accumulation function for dsp/ +f08e6624 Merge "Fix FindClosestDiscretized in near lossless:" +0d40cc5e enc_neon,Disto4x4: remove an unnecessary transpose +e8feb20e Fix FindClosestDiscretized in near lossless: +82006430 anim_util: quiet static analysis warning +a6f23c49 Merge "AnimEncoder: Support progress hook and user data." +a5193774 Merge "Near lossless feature: fix some comments." +da98d31c AnimEncoder: Support progress hook and user data. +33357131 Near lossless feature: fix some comments. +0beed01a cosmetics: fix indent after 2f5e898 +6753f35c Merge "FTransformWHT optimization." +6583bb1a Improve SSE4.1 implementation of TTransform. +7561d0c3 FTransformWHT optimization. +7ccdb734 fix indentation after patch #328220 +6ec0d2a9 clarify the logic of the error path when decoding fails. +8aa352b2 Merge "Remove an unnecessary transposition in TTransform." +db860884 Merge "remove useless #include" +9960c316 Remove an unnecessary transposition in TTransform. +6e36b511 Small speedup in FTransform. +9dbd4aad Merge "fix C and SIMD flags completion." +e60853ea Add missing common_sse2.h file to makefile.unix +696eb2b0 fix C and SIMD flags completion. +2b4fe33e Merge "fix multiple allocation for transform buffer" +2f5e8986 fix multiple allocation for transform buffer +bf2b4f11 Regroup common SSE code + optimization. +4ed650a1 force "-pass 6" if -psnr or -size is used but -pass isn't. +3ef1ce98 yuv_sse2: fix -Wconstant-conversion warning +a7a03e9f Merge changes I4852d18f,I51ccb85d +5e122bd6 gif2webp: set enc_options.verbose = 0 w/-quiet +ab3c2583 anim_encode,DefaultEncoderOptions: init verbose +8f0dee77 Merge "configure: fix builtin detection w/-Werror" +4a7b85a9 cmake: fix builtin detection w/-Werror +b74657fb configure: fix builtin detection w/-Werror +3661b980 Add a CMakeLists.txt +75f4af4d remove useless #include +6c1d7631 avoid Yoda style for comparison +8ce975ac SSE optimization for vector mismatch. +7db53831 Merge tag 'v0.5.0' +37f04949 update ChangeLog (tag: v0.5.0-rc1, tag: v0.5.0, origin/0.5.0) +7e7b6ccc faster rgb565/rgb4444/argb output +4c7f565f update NEWS +1f62b6b2 update AUTHORS +e224fdc8 update mailmap +71100500 bump version to 0.5.0 +230a685e README: update help text, repo link +d48e427b Merge "demux: accept raw bitstreams" +99a01f4f Merge "Unify some entropy functions." +4b025f10 Merge "configure: disable asserts by default" +92cbddf8 Merge "fix PrintBlockInfo()" +ca509a33 Unify some entropy functions. +367bf903 fix PrintBlockInfo() +b0547ff0 move back common constants for lossless_enc*.c into the .h +fb4c7832 lossless: simpler alpha cleanup preprocessing +ba7f4b68 Merge "anim_diff: add brief description of options" +47ddd5a4 Move some codec logic out of ./dsp . +b4106c44 anim_diff: add brief description of options +357f455d yuv_sse2: fix 32-bit visual studio build +b9d80fa4 configure: disable asserts by default +7badd3da cosmetic fix: sizeof(type) -> sizeof(*var) +80ce27d3 Speed up 24-bit packing / unpacking in YUV / RGB conversions. +68eebcb0 remove a TODO about rotation +2dee2966 remove few obsolete TODO about aligned loads in SSE2 +e0c0bb34 remove TODO about unused ref_lf_delta[] +9cf1cc2b remove few TODO: * 256 -> RD_DISTO_MULT * don't use TDisto for UV mode picking +79189645 Merge changes from topic 'demux-fragment-cleanup' +47399f92 demux: remove GetFragment() +d3cfb79a demux: remove dead fragment related TODO +ab714b8a demux, Frame: remove is_fragment_ field +b105921c yuv_sse2, cosmetics: fix indent +466c92e8 demux,WebPIterator: remove fragment_num/num_fragments +11714ff1 demux: remove WebPDemuxSelectFragment +c0f7cc47 fix for bug #280: UMR in next->bits +578beeb8 Merge "enc/Makefile.am: add missing headers" +1a819f00 makefile.unix: make visibility=hidden the default +d4f9c2ef enc/Makefile.am: add missing headers +846caff4 configure: check for -fvisibility=hidden +3f3ea2c5 demux: accept raw bitstreams +d6dad5d0 man cwebp: add precision about exactness of the 'lossless' mode +46bb1e34 Merge "gifdec: remove utils.h include" +2b882e94 Merge "Makefile.vc: define WEBP_HAVE_GIF for gifdec.c" +892b9238 Merge "man/*, AUTHORS: clarify origin of the tool" +e5687a18 Merge "fix optimized build with -mcmodel=medium" +e56e6859 Makefile.vc: define WEBP_HAVE_GIF for gifdec.c +4077d944 gifdec: remove utils.h include +b5e30dac man/*, AUTHORS: clarify origin of the tool +b275e598 fix optimized build with -mcmodel=medium +64da45a9 cosmetics, cwebp: fix indent +038a060d Merge "add disto-based refinement for UV mode (if method = 1 or 2)" +2835089d Provide an SSE2 implementation of CombinedShannonEntropy. +e6c93519 add disto-based refinement for UV mode (if method = 1 or 2) +04507dc9 Merge "fix undefined behaviour during shift, using a cast" +793c5261 Merge "wicdec: add support for reading from stdin" +d3d16397 Optimize the heap usage in HistogramCombineGreedy. +202a710b fix undefined behaviour during shift, using a cast +14d27a46 improve method #2 by merging DistoRefine() and SimpleQuantize() +cb1ce996 Merge "10% faster table-less SSE2/NEON version of YUV->RGB conversion" +ac761a37 10% faster table-less SSE2/NEON version of YUV->RGB conversion +79fcf29a wicdec: add support for reading from stdin +015f173f Merge "cwebp: add support for stdin input" +a9947c32 cwebp: add support for stdin input +7eb01ff3 Merge "Improved alpha cleanup for the webp encoder when prediction transform is used." +fb8c9106 Merge "introduce WebPMemToUint32 and WebPUint32ToMem for memory access" +bd91af20 Merge "bit_reader: remove aarch64 BITS TODO" +6c702b81 Speed up hash chain initialization using memset. +4c60f63c make ReadPNG and ReadJPEG take a filename instead of a FILE +464ed10f bit_reader: remove aarch64 BITS TODO +d478e589 Merge "configure: update issue tracker" +69381113 Improved alpha cleanup for the webp encoder when prediction transform is used. +2c08aac8 introduce WebPMemToUint32 and WebPUint32ToMem for memory access +010ca3d1 Fix FindMatchLength with non-aligned buffers. +a90e1e3f README: add prerequisites for an autoconf build +458f0866 configure: update issue tracker +33914595 vwebp: work around the transparent background with GLUT bug +e4a7eed4 cosmetics: fix indent +08375129 Merge "Make a separate case for low_effort in CopyImageWithPrediction" +aa2eb2d4 Merge "cosmetics: fix indent" +b7551e90 cosmetics: fix indent +5bda52d4 Make a separate case for low_effort in CopyImageWithPrediction +66fa598a Merge "configure: fix intrinsics build w/older gcc" +5ae220be backward_references.c: Fixed compiler warning +1556da09 Merge "configure: restore 2 warnings" +71a17e58 configure: restore 2 warnings +9eeabc07 configure: fix intrinsics build w/older gcc +363babe2 Merge "fix some warning about unaligned 32b reads" +a1411782 Optimization in hash chain comparison for 64 bit Arrays were compared 32 bits at a time, it is now done 64 bits at a time. Overall encoding speed-up is only of 0.2% on @skal's small PNG corpus. It is of 3% on my initial 1.3 Mp desktop screenshot image. +829bd141 Combine Huffman cost and bit entropy into one loop +a7a954c8 Merge "lossless: make prediction in encoder work per scanline" +61b605b4 Merge "fix of undefined multiply (int32 overflow)" +239421c5 lossless: make prediction in encoder work per scanline +f5ca40e0 fix of undefined multiply (int32 overflow) +5cd2ef4c Merge changes from topic 'win-threading-compat' +76ce9187 Makefile.vc: enable WEBP_USE_THREAD for windows phone +d2afe974 thread: use CreateThread for windows phone +0fd0e12b thread: use WaitForSingleObjectEx if available +63fadc9f thread: use InitializeCriticalSectionEx if available +110ad583 thread: use native windows cond var if available +912c9fdf dec/webp: use GetLE(24|32) from utils +f1694481 utils/GetLE32: correct uint32 promotion +158763de Merge "always call WebPInitSamplers(), don't try to be smart" +3770f3bb Merge "cleanup the YFIX/TFIX difference by removing some code and #define" +a40f60a9 Merge "3% speed improvement for lossless webp encoder for low effort mode:" +ed1c2bc6 always call WebPInitSamplers(), don't try to be smart +b8c44f1a 3% speed improvement for lossless webp encoder for low effort mode: +997e1038 cleanup the YFIX/TFIX difference by removing some code and #define +d73d1c8b Merge "Make discarding invisible RGB values (cleanup alpha) the default." +1f9be97c Make discarding invisible RGB values (cleanup alpha) the default. +f240117b Make dwebp listen more to the -quiet flag +b37b0179 fix for issue #275: don't compare to out-of-bound pointers +21735e06 speed-up trivial one-symbol decoding case for lossless +397863bd Refactor CopyPlane() and CopyPixels() methods: put them in utils. +6ecd72f8 Re-enable encoding of alpha plane with color cache for next release. +1f7148a4 Merge "remove unused fields from WebPDecoderOptions and WebPBitstreamFeatures" +6ae395fa Merge "use ExReadFile() for ReadYUV()" +8076a00e gitignore list: add anim_diff. +1c1702d8 use ExReadFile() for ReadYUV() +775d3a37 remove unused fields from WebPDecoderOptions and WebPBitstreamFeatures +c13245c7 AnimEncoder: Add a GetError() method. +688b265d AnimDecoder API: Add a GetDemuxer() method. +1aa4e3d6 WebPAnimDecoder: add an option to enable multi-threaded decoding. +3584abca AnimDecoder: option to decode to common color modes. +afd5a62c Merge "mux.h does NOT need to include encode.h" +8550d443 Merge "migrate anim_diff tool from C++ to C89" +96201e50 migrate anim_diff tool from C++ to C89 +945cfa3b mux.h does NOT need to include encode.h +8da07e8d Merge "~2x faster SSE2 RGB24toY, BGR24toY, ARGBToY|UV" +bfd3fc02 ~2x faster SSE2 RGB24toY, BGR24toY, ARGBToY|UV +02432427 man/cwebp.1, cosmetics: escape '-'s +96f5b423 man/cwebp: group lossy-only options +52fdbdfe extract some RGB24 to Luma conversion function from enc/ to dsp/ +ab8c2300 add missing \n +8304179a sync NEWS with 0.4.4 +5bd04a08 sync versions with 0.4.4 +8f1fcc15 Merge "Move ARGB->YUV functions from dec/vp8l.c to dsp/yuv.c" +25bf2ce5 fix some warning about unaligned 32b reads +922268fd s/TIFF/WebP +fa8927ef Move ARGB->YUV functions from dec/vp8l.c to dsp/yuv.c +9b373598 Merge "for ReadXXXX() image-readers, use the value of pic->use_argb" +f7c507a5 Merge "remove unnecessary #include "yuv.h"" +7861578b for ReadXXXX() image-readers, use the value of pic->use_argb +14e4043b remove unnecessary #include "yuv.h" +469ba2cd vwebp: fix incorrect clipping w/NO_BLEND +4b9186b2 update issue tracker url +d64d376c change WEBP_ALIGN_CST value to 31 +f717b828 vp8l.c, cosmetics: fix indent after 95509f9 +927ccdc4 Merge "fix alignment of allocated memory in AllocateTransformBuffer" +fea94b2b fix alignment of allocated memory in AllocateTransformBuffer +5aa8d61f Merge "MIPS: rescaler code synced with C implementation" +e7fb267d MIPS: rescaler code synced with C implementation +93c86ed5 Merge "format_constants.h: MKFOURCC, correct cast" +5d791d26 format_constants.h: MKFOURCC, correct cast +65726cd3 dsp/lossless: Average2, make a constant unsigned +d26d9def Use __has_builtin to check clang support +12ec204e moved ALIGN_CST into util/utils.h and renamed WEBP_ALIGN_xxx +a2640838 Merge "rescaler: ~20% faster SSE2 implementation for lossless ImportRowExpand" +3fb600d5 Merge "wicdec: fix alpha detection w/64bpp BGRA/RGBA" +67c547fd rescaler: ~20% faster SSE2 implementation for lossless ImportRowExpand +99e3f812 Merge "large re-organization of the delta-palettization code" +95509f99 large re-organization of the delta-palettization code +74fb458b fix for weird msvc warning message +ae49ad86 Merge "SSE2 implementation of ImportRowShrink" +932fd4df SSE2 implementation of ImportRowShrink +badfcbaa wicdec: fix alpha detection w/64bpp BGRA/RGBA +35cafa6c Merge "iosbuild: fix linking with Xcode 7 / iOS SDK 9" +b0c9d8af label rename: NO_CHANGE -> NoChange +b4e731cd neon-implementation for rescaler code +db1321a6 iosbuild: fix linking with Xcode 7 / iOS SDK 9 +6dfa5e3e rescaler: better handling of the fxy_scale=0 special case. +55c05293 Revert "rescaler: better handling of the fxy_scale=0 special case." +9f226bf8 rescaler: better handling of the fxy_scale=0 special case. +f7b8f907 delta_palettization.*: add copyright +c1e1b710 Changed delta palette to compress better +0dd28267 Merge "Add delta_palettization feature to WebP" +48f66b66 Add delta_palettization feature to WebP +27933e2a anim_encoder: drop a frame if it has same pixels as the prev frame. +df9f6ec8 Merge "webpmux/DisplayInfo: send non-error output to stdout" +8af4993b Merge "rescaler_mips_dsp_r2: cosmetics, fix indent" +2b9d2495 Merge "rescaler: cosmetics, join two lines" +cc020a8c webpmux/DisplayInfo: send non-error output to stdout +a288e746 configure: add -Wshorten-64-to-32 +c4c3cf2d pngdec: fix type conversion warnings +bef8e97d webpmux: fix type conversion warning +5a84460d rescaler_mips_dsp_r2: cosmetics, fix indent +acde0aae rescaler: cosmetics, join two lines +306ce4fd rescaler: move the 1x1 or 2x1 handling one level up +cced974b remove _mm_set_epi64x(), which is too specific +56668c9f fix warnings about uint64_t -> uint32_t conversion +76a7dc39 rescaler: add some SSE2 code +1df1d0ee rescaler: harmonize function protos +9ba1894b rescaler: simplify ImportRow logic +5ff0079e fix rescaler vertical interpolation +cd82440e VP8LAllocateHistogramSet: align histogram[] entries +a406b1dd Merge "fix memory over-allocation in lossless rescaler init" +0fde33e3 add missing const in VP8InitFrame signature +ac7d5e8d fix memory over-allocation in lossless rescaler init +017f8ccc Loosen the buffer size checks for Y/U/V/A too. +15ca5014 loosen the padding check on buffer size +d623a870 dec_neon: add whitespace around stringizing operator +29377d55 dsp/mips: cosmetics: add whitespace around XSTR macro +eebaf97f dsp/mips: add whitespace around stringizing operator +d39dc8f3 Create a WebPAnimDecoder API. +03fb7522 gif2webp: print output file size +14efabbf Android: limit use of cpufeatures +7b83adbe preparatory cosmetics for Rescaler code fix and clean-up +77fb41c2 dec/vp8l/DecodeAlphaData: remove redundant cast +90fcfcd9 Insert less hash chain entries from the beginnings of long copies. +bd55604d SSE2: add yuv444 converters, re-using yuv_sse2.c +41a5d99d add a -quiet option to 'dwebp' +80ab3edb Merge "README: update dwebp help output after 1e595fe" +32b71b2e README: update dwebp help output after 1e595fe +3ec11827 use the DispatchAlpha() call from dsp +c5f00621 incorporate bzero() into WebPRescalerInit() instead of call site +3ebcdd41 remove duplicate "#include " +1e595fe1 dwebp: add -resize as a synonym for -scale +24a96932 dec: allow 0 as a scaling dimension +b9187242 utils/rescaler: add WebPRescalerGetScaledDimensions +923e8eda Merge "update NEWS" +020fd099 Merge "WebPPictureDistortion: support ARGB format for 'pic' when computing distortion." +6a5292f6 update NEWS +56a2e9f5 WebPPictureDistortion: support ARGB format for 'pic' when computing distortion. +0ae582e4 configure: test and add -Wunreachable-code +c2f9dc06 bit_writer: convert VP8L macro values to immediates +b969f888 Reduce magic in palette reordering +acb297e9 anim_diff: add a -raw_comparison flag +155c1b22 Merge changes I76f4d6fe,I45434639 +717e4d5a mips32/mipsDSPr2: function ImportRow rebased +7df93893 fix rescaling bug (uninitialized read, see bug #254). +5cdcd561 lossless_enc_neon: add VP8LTransformColor +a53c3369 lossless_neon: add VP8LTransformColorInverse +99131e7f Merge changes I9fb25a89,Ibc648e9e +c4556766 simplify the main loop for downscaling +2a010f99 lossless_neon: remove predictors 5-13 +ca221bbc ll_enc_neon: enable VP8LSubtractGreenFromBlueAndRed +585d93db Container spec: clarify ordering of ALPH chunk. +01d61fd9 lossless: ~20 % speedup +f722c8f0 lossless: Speed up ComputeCacheEntropy by 40 % +1ceecdc8 add a VP8LColorCacheSet() method for color cache +17eb6099 lossless: Allow copying from prev row in rle-mode. +f3a7a5bf lossless: bit writer optimization +d97b9ff7 Merge changes from topic 'lossless-enc-improvements' +0250dfcc msvc: fix pointer type warning in BitsLog2Floor +52931fd5 lossless: combine the Huffman code with extra bits +c4855ca2 lossless: Inlining add literal +8e9c94de lossless: simplify HashChainFindCopy heuristics +888429f4 lossless: 0.5 % compression density improvement +7b23b198 lossless: Add zeroes into the predicted histograms. +85b44d8a lossless: encoding, don't compute unnecessary histo +d92453f3 lossless: Remove about 25 % of the speed degradation +2cce0317 Faster alpha coding for webp +5e75642e lossless: rle mode not to accept lengths smaller than 4. +84326e4a lossless: Less code for the entropy selection +16ab951a lossless: 0.37 % compression density improvement +822f113e add WebPFree() to the API +0ae2c2e4 SSE2/SSE41: optimize SSE_16xN loops +39216e59 cosmetics: fix indent after 32462a07 +559e54ca Merge "SSE2: slightly faster FTransformWHT" +8ef9a63b SSE2: slightly faster FTransformWHT +f27f7735 lossless_neon: enable VP8LAddGreenToBlueAndRed +36e9c4bc SSE2: minor cosmetrics on in-loop filter code +4741fac4 dsp/lossless_*sse2: remove some unnecessary inlines +1819965e fix warning ("left shift of negative value") using a cast +70170014 SSE2: speed-up some lossless-encoding functions +abcb0128 Merge "SSE2: slightly faster (~5%) AddGreenToBlueAndRed()" +2df5bd30 Merge "Speedup to HuffmanCostCombinedCount" +9e356d6b SSE2: slightly faster (~5%) AddGreenToBlueAndRed() +fc6c75a2 SSE2: 53% faster TransformColor[Inverse] +49073da6 SSE2: 46% speed-up of TransformColor[Inverse] +32462a07 Speedup to HuffmanCostCombinedCount +f3d687e3 SSE4.1 implementation of some lossless encoding functions +bfc300c7 SSE4.1 implementation of some alpha-processing functions +7f9c98f2 Merge "sse2 in-loop: simplify SignedShift8b() a bit" +ef314a5d dec_sse2/GetNotHEV: micro optimization +a729cff9 sse2 in-loop: simplify SignedShift8b() a bit +422ec9fb simplify Load8x4() a bit +8df238ec Merge "remove some duplicate FlipSign()" +751506c4 remove some duplicate FlipSign() +65ef5afc Merge "lossless: 0.13% compression density gain" +2beef2f2 lossless: 0.13% compression density gain +3033f24c lossless: 0.06 % compression density improvement +64960da9 dec_neon: add VE8uv / VE16 +14dbd87b dec_neon: add HE8uv / HE16 +ac768011 introduce FTransform2 to perform two transforms at a time. +aa6065ae dec_neon: use vld1_dup(mem) rather than vdup(mem[0]) +8b63ac78 Merge "dec_neon: add TM16" +f51be09e Merge "dec_neon/TrueMotion: simply left border load" +dc48196b dec_neon: add TM16 +ea95b305 dec_neon/TrueMotion: simply left border load +f262d612 speed-up SetResidualSSE2 +bf46d0ac fix mips2 build target +929a0fdc enc_sse2/TTransform: simplify abs calculation +17dbd058 enc_sse2/CollectHistogram: simplify abs calculation +a6c15936 dec_neon: add DC16 intra predictors +03b4f50d Makefile.vc: add anim_diff build support. +1b989874 Merge changes I9cd84125,Iee7e387f,I7548be72 +acd7b5af Introduce a test tool anim_diff. +f274a96c dsp/enc_sse2: add luma4 intra predictors +040b11bd dsp/enc_sse2: add chroma intra predictors +aee021bb dsp/enc_sse2: add luma16 intra predictors +9e00a499 makefile.unix: remove superclean target +cefc9c09 makefile.unix: clean up after extras target +4c9af023 dec_neon: add DC8uvNoTopLeft +dd55b873 Merge "doc/webp-container-spec: update repo browser link" +f0486968 doc/webp-container-spec: update repo browser link +9287761d Merge "GetResidualCostSSE2: simplify abs calculation" +0e009366 dsp/cpu.c(x86): check maximum supported cpuid feature +b243a4bc GetResidualCostSSE2: simplify abs calculation +6d4602b8 Merge "fix typo: constitutes -> constitute" +5fe1fe37 fix typo: constitutes -> constitute +b83bd7c4 Merge "populate 'libwebpextras' with: import gray, rgb565 and rgb4444 functions" +b0114a32 Merge "histogram.h: cosmetics: remove unnecessary includes" +feab45ef gifdec: Move inclusion of webp/config.h to header. +dbba67d1 histogram.h: cosmetics: remove unnecessary includes +e978fec6 Merge "VP8LBitReader: fix remaining ubsan error with large shifts" +d6fe5884 Merge "ReconstructRow: move some one-time inits out of the main loop" +a21d647c ReconstructRow: move some one-time inits out of the main loop +7a01c3c3 VP8LBitReader: fix remaining ubsan error with large shifts +7fa67c9b change GetPixPairHash64() return type to uint32_t +ec1fb9f8 Merge "dsp/enc.c: cosmetics: move DST() def closer to use" +7073bfb3 Merge "split 64-mult hashing into two 32-bit multiplies" +0768b252 dsp/enc.c: cosmetics: move DST() def closer to use +6a48b8f0 Merge "fix MSVC size_t->int conversion warning" +1db07cde Merge "anim_encode: cosmetics: fix alignment" +e28271a3 anim_encode: cosmetics: fix alignment +7fe357b8 split 64-mult hashing into two 32-bit multiplies +af74c145 populate 'libwebpextras' with: import gray, rgb565 and rgb4444 functions +61214134 remove VP8Residual::cost unused field +e2544823 fix MSVC size_t->int conversion warning +b69a6c35 vwebp: don't redefine snprintf with VS2015+ +0ac29c51 AnimEncoder API: Consistent use of trailing underscores in struct. +d4845550 AnimEncoder API: Use timestamp instead of duration as input to Add(). +9904e365 dsp/dec_sse2: DC8uv / DC8uvNoLeft speedup +7df20497 dsp/dec_sse2: DC16 / DC16NoLeft speedup +8e515dfe Merge "makefile.unix: add some missing headers" +db12250f cosmetics: vp8enci.h: break long line +bf516a87 makefile.unix: add some missing headers +b44eda3f dsp: add DSP_INIT_STUB +03e76e96 clarify the comment about double-setting the status in SetError() +9fecdd71 remove unused EmitRGB() +43f010dd move ReconstructRow to top +82d98020 add a dec/common.h header to collect common enc/dec #defines +5d4744a2 Merge "enc_sse41: add Disto4x4 / Disto16x16" +e38886a7 mux.h: Bump up ABI version +46305ca6 configure: add --disable- +2fc8b658 CPPFLAGS->CFLAGS for detecting sse4.1 in preprocessor +1a338fb3 enc_sse41: add Disto4x4 / Disto16x16 +94055503 encoding SSE4.1 stub for StoreHistogram + Quantize + SSE_16xN +c64659e1 remove duplicate variables after the lossless{_enc}.c split +67ba7c7a enc_sse2: call local FTransform in CollectHistogram +18249799 dsp: s/VP8LSetHistogramData/VP8SetHistogramData/ +ede5e158 cosmetics: dsp/lossless.h: reorder prototypes +553051f7 dsp/lossless: split enc/dec functions +9064adc8 Merge "conditionally add -msse4.1 in Makefile.unix" +cecf5096 dsp/yuv*.c: rework WEBP_USE_ ifdef +6584d398 dsp/upsampling*.c: rework WEBP_USE_ ifdef +80809422 dsp/rescaler*.c: rework WEBP_USE_ ifdef +1d93ddec dsp/lossless*.c: rework WEBP_USE_ ifdef +73805ff2 dsp/filters*.c: rework WEBP_USE_ ifdef +fbdcef24 dsp/enc*.c: rework WEBP_USE_ ifdef +66de69c1 dsp/dec*.c: rework WEBP_USE_ ifdef +48e4ffd1 dsp/cost*.c: rework WEBP_USE_ ifdef +29fd6f90 dsp/argb*.c: rework WEBP_USE_ ifdef +80ff3813 dsp/alpha*.c: rework WEBP_USE_ ifdef +bf09cf1e conditionally add -msse4.1 in Makefile.unix +e9570dd9 stub for SSE4.1 support. +4a95384b Merge "dsp: add sse4.1 detection" +cabf4bd2 dsp: add sse4.1 detection +4ecba1ab thread.h: rename interface param +b8d706c8 Merge "sync versions with 0.4.3" +ae64a711 Merge "add shell for libwebpextras" +92a5da9c sync versions with 0.4.3 +9d4e2d16 Merge "~30% faster smart-yuv (-pre 4) with early-out criterion" +b1bdbbab ~30% faster smart-yuv (-pre 4) with early-out criterion +7efb9748 Merge "Disable NEON code on Native Client" +ac4f5784 Disable NEON code on Native Client +0873f85b AnimEncoder API: Support input frames in YUV(A) format. +5c176d2d add shell for libwebpextras +44bd9561 fix signature for VP8RecordCoeffTokens() +c9b8ea0e small cosmetics on TokenBuffer. +76394c09 Merge "MIPS: dspr2: added optimization for TrueMotion" +0f773693 WebPPictureRescale: add a note about 0 width/height +241bb5d9 MIPS: dspr2: added optimization for TrueMotion +6cef0e4f examples/Android.mk: add webpmux_example target +53c16ff0 Android.mk: add webpmux target +21852a00 Android.mk: add webpdemux target +8697a3bc Android.mk: add webpdecoder{,_static} targets +4a670491 Android.mk: split source lists per-directory +b5e79422 MIPS: dspr2: Added optimization for some convert functions +0f595db6 MIPS: dspr2: Added optimization for some convert functions +8a218b4a MIPS: [mips32|dspr2]: GetResidualCost rebased +ef987500 Speedup method StoreImageToBitMask by 5%. +602a00f9 fix iOS arm64 build with Xcode 6.3 +23820507 1-2% faster encoding by removing an indirection in GetResidualCost() +eddb7e70 MIPS: dspr2: added otpimization for DC8uv, DC8uvNoTop and DC8uvNoLeft +73ba2915 MIPS: dspr2: added optimization for functions RD4 and LD4 +c7129da5 Merge "4-5% faster encoding using SSE2 for GetResidualCost" +94380d00 MIPS: dspr2: added optimizaton for functions VE4 and DC4 +2a407092 4-5% faster encoding using SSE2 for GetResidualCost +17e19862 Merge "MIPS: dspr2: added optimization for simple filtering functions" +3ec404c4 Merge "dsp: normalize WEBP_TSAN_IGNORE_FUNCTION usage" +b969f5df dsp: normalize WEBP_TSAN_IGNORE_FUNCTION usage +d7b8e711 MIPS: dspr2: added optimization for simple filtering functions +235f774e Merge "MIPS: dspr2: Added optimization for function VP8LTransformColorInverse_C" +42a8a628 MIPS: dspr2: Added optimization for function VP8LTransformColorInverse_C +b442bef3 Merge "ApplyFiltersAndEncode: only copy lossless stats" +b510fbfe doc/webp-container-spec: note MSB order for chunk diagrams +9bc0f922 ApplyFiltersAndEncode: only copy lossless stats +3030f115 Merge "dsp/mips: add some missing TSan annotations" +dfcf4593 Merge "MIPS: dspr2: Added optimization for function VP8LAddGreenToBlueAndRed_C" +55c75a25 dsp/mips: add some missing TSan annotations +2cb879f0 MIPS: dspr2: Added optimization for function VP8LAddGreenToBlueAndRed_C +e1556010 move some cost tables from enc/ to dsp/ +c3a03168 Merge "picture_csp: fix build w/USE_GAMMA_COMPRESSION undefined" +39537d7c Merge "VP8LDspInitMIPSdspR2: add missing TSan annotation" +1dd419ce picture_csp: fix build w/USE_GAMMA_COMPRESSION undefined +43fd3543 VP8LDspInitMIPSdspR2: add missing TSan annotation +c7233dfc Merge "VP8LDspInit: remove memcpy" +0ec4da96 picture_csp::InitGammaTables*: add missing TSan annotations +35579a49 VP8LDspInit: remove memcpy +97f6aff8 VP8YUVInit: add missing TSan annotation +f9016d66 dsp/enc::InitTables: add missing TSan annotation +e3d9771a VP8EncDspCostInit*: add missing TSan annotations +d97c143d Merge "doc/webp-container-spec: cosmetics" +309b7908 MIPS: mips32: Added optimization for function SetResidualCoeffs +a987faed MIPS: dspr2: added optimization for function GetResidualCost +e7d3df23 doc/webp-container-spec: cosmetics +be6635e9 Merge "VP8TBufferClear: remove some misleading const's" +02971e72 Merge "VP8EmitTokens: remove unnecessary param void cast" +3b77e5a7 VP8TBufferClear: remove some misleading const's +aa139c8f VP8EmitTokens: remove unnecessary param void cast +c24d8f14 cosmetics: upsampling_sse2: add const to some casts +1829c42c cosmetics: lossless_sse2: add const to some casts +183168f3 cosmetics: enc_sse2: add const to some casts +860badca cosmetics: dec_sse2: add const to some casts +0254db97 cosmetics: argb_sse2: add const to some casts +1aadf856 cosmetics: alpha_processing_sse2: add const to some casts +1579de3c vwebp: clear canvas at the beginning of each loop +4b9fa5d0 Merge "webp-container-spec: clarify background clear on loop" +4c82284d Updated the near-lossless level mapping. +56039479 webp-container-spec: clarify background clear on loop +19f0ba0e Implement true-motion prediction in SSE2 +774d4cb7 make VP8PredLuma16[] array non-const +d7eabb80 Merge "MIPS: dspr2: Added optimization for function CollectHistogram" +fe42739c Use integers for kmin/kmax for simplicity. +b9df35f7 AnimEncode API: kmax=0 should imply all keyframes. +6ce296da MIPS: dspr2: Added optimization for function CollectHistogram +2c906c40 vwebp: remove unnecessary static Help() prototype +be0fd1d5 Merge "dec/vp8: clear 'dither_' on skipped blocks" +e96170fe Merge "vwebp/animation: display last frame on end-of-loop" +0f017b56 vwebp/animation: display last frame on end-of-loop +c86b40cc enc/near_lossless.c: fix alignment +66935fb9 dec/vp8: clear 'dither_' on skipped blocks +b7de7946 Merge "lossless_neon: enable subtract green for aarch64" +77724f70 SSE2 version of GradientUnfilter +416e1cea lossless_neon: enable subtract green for aarch64 +72831f6b Speedup AnalyzeAndInit for low effort compression. +a6597483 Speedup Analyze methods for lossless compression. +98c81386 Enable Near-lossless feature. +c6b24543 AnimEncoder API: Fix for kmax=1 and default kmin case. +022d2f88 add SSE2 variants for alpha filtering functions +2db15a95 Temporarily disable encoding of alpha plane with color cache. +1d575ccd Merge "Lossless decoding: Remove an unnecessary if condition." +cafa1d88 Merge "Simplify backward refs calculation for low-effort." +7afdaf84 Alpha coding: reorganize the filter/unfiltering code +4d6d7285 Simplify backward refs calculation for low-effort. +ec0d1be5 Cleaup Near-lossless code. +9814ddb6 Remove the post-transform near-lossless heuristic. +4509e32e Lossless decoding: Remove an unnecessary if condition. +f2ebc4a8 Merge "Regression fix for lossless decoding" +783a8cda Regression fix for lossless decoding +9a062b8e AnimEncoder: Bugfix for kmin = 1 and kmax = 2. +0f027a72 simplify smart RGB->YUV conversion code +0d5b334e BackwardReferencesHashChainFollowChosenPath: remove unused variable +f480d1a7 Fix to near lossless artefacts on palettized images. +d4615d08 Merge changes Ia1686828,I399fda40 +cb4a18a7 rename HashChainInit into HashChainReset +f079e487 use uint16_t for chosen_path[] +da091212 MIPS: dspr2: Added optimization for function FTransformWHT +b8c20135 Merge "wicdec: (msvs) quiet some /analyze warnings" +9b228b54 wicdec: (msvs) quiet some /analyze warnings +daeb276a Merge "MIPS: dspr2: Added optimization for MultARGBRow function" +cc087424 Merge "dsp/cpu: (msvs) add include for __cpuidex" +4a82aab5 Merge changes I87544e92,I0bb6cda5 +7a191398 dwebp/WritePNG: mark png variables volatile +775dfad2 dwebp: include setjmp.h w/WEBP_HAVE_PNG +47d26be7 dwebp: correct sign in format strings +f0e0677b VP8LEncodeStream: add an assert +c5f7747f VP8LColorCacheCopy: promote an int before shifting +0de5f33e dsp/cpu: (msvs) add include for __cpuidex +7d850f7b MIPS: dspr2: Added optimization for MultARGBRow function +54875293 MIPS: dspr2: added optimization for function QuantizeBlock +4fbe9cf2 dsp/cpu: (msvs) avoid immintrin.h on _M_ARM +3fd59039 simplify/reorganize arguments for CollectColorBlueTransforms +b9e356b9 Disable costly TraceBackwards for method=0. +a7e7caa4 MIPS: dspr2: added optimization for function TransformColorRed +2cb39180 Merge "MIPS: dspr2: added optimization for function TransformColorBlue" +279e6613 Merge "dsp/cpu: add include for _xgetbv() w/MSVS" +b6c0428e dsp/cpu: add include for _xgetbv() w/MSVS +d1c4ffae gif2webp: Move GIF decoding related code to a support library. +07c39559 Merge "AnimEncoder API: Add info in README.mux" +7b161973 MIPS: dspr2: added optimization for function TransformColorBlue +d7c4b02a cpu: fix AVX2 detection for gcc/clang targets +9d299469 AnimEncoder API: Add info in README.mux +d581ba40 follow-up: clean up WebPRescalerXXX dsp function +f8740f0d dsp: s/USE_INTRINSICS/WEBP_USE_INTRINSICS/ +ce73abe0 Merge "introduce a separate WebPRescalerDspInit to initialize pointers" +ab66beca introduce a separate WebPRescalerDspInit to initialize pointers +205c7f26 fix handling of zero-sized partition #0 corner case +cbcdd5ff Merge "move rescaler functions to rescaler* files in src/dsp/" +bf586e88 Merge changes I230b3532,Idf3057a7 +6dc79dc2 Merge "anim_encode: fix type conversion warnings" +11fce25a Merge "dec_neon: remove returns from void functions" +c4e63f99 Makefile.vc: add gif2webp target +4f43d38c enable NEON for Windows ARM builds +3f6615ac Makefile.vc: add rudimentary Windows ARM support +e7c5954c dec_neon: remove returns from void functions +f79c163b anim_encode: fix type conversion warnings +0f54f1ec Remove gif2webp_util which is no longer needed. +cbcbedd0 move rescaler functions to rescaler* files in src/dsp/ +ac79ed19 webpmux: remove experimental fragment handling +e8694d4d mux: remove experimental FRGM parsing +9e92b6ea AnimEncoder API: Optimize single-frame animated images +abbae279 Merge "Move over gif2webp to the new AnimEncoder API." +a28c4b36 MIPS: move WORK_AROUND_GCC define to appropriate place +012d2c60 MIPS: dspr2: added optimization for functions SSEAxB +67720c8b Move over gif2webp to the new AnimEncoder API. +9241ecf4 MIPS: dspr2: added optimization for function Average +9422211d Merge "Tune BackwardReferencesLz77 for low_effort (m=0)." +df40057b Merge "Speedup VP8LGetHistoImageSymbols for low effort (m=0) mode." +ea08466d Tune BackwardReferencesLz77 for low_effort (m=0). +b0b973c3 Speedup VP8LGetHistoImageSymbols for low effort (m=0) mode. +c6d32927 argb_sse2: cosmetics +67f601cd make the 'last_cpuinfo_used' variable names unique +b9489861 AnimEncoder API: Init method for default options. +856f8ec1 Merge "AnimEncoder API: Remove AnimEncoderFrameOptions." +c537514d Merge "AnimEncoder API: GenerateCandidates bugfix." +dc0ce039 Merge "AnimEncoder API: Compute change rectangle for first frame too." +f00b639b Merge "AnimEncoder API: In Assemble(), always set animation parameters." +29ed796c Merge "AnimEncoder lib cleanup: prev to prev canvas not needed." +9f0dd6e5 Merge "WebPAnimEncoder API: Header and implementation" +5e56bbe0 AnimEncoder API: Remove AnimEncoderFrameOptions. +b902c3ea AnimEncoder API: GenerateCandidates bugfix. +ef3c39bb AnimEncoder API: Compute change rectangle for first frame too. +eec423ab AnimEncoder API: In Assemble(), always set animation parameters. +ae1c046e AnimEncoder lib cleanup: prev to prev canvas not needed. +4b997ae4 WebPAnimEncoder API: Header and implementation +72208bec move argb_*.o build target to encoder list +95920538 Merge "multi-thread fix: lock each entry points with a static var" +4c1b300a Merge "SSE2 implementation of VP8PackARGB" +fbcc2004 Merge "add -Wformat-nonliteral and -Wformat-security" +80d950d9 add -Wformat-nonliteral and -Wformat-security +04c20e75 Merge "MIPS: dspr2: added optimization for function Intra4Preds" +a437694a multi-thread fix: lock each entry points with a static var +ca7f60db SSE2 implementation of VP8PackARGB +72d573f6 simplify the PackARGB signature +4e2589ff demux: restore strict fragment flag check +4ba8e074 Merge "webp-container-spec: remove references to fragments" +e752f0a6 Merge "demux: remove experimental FRGM parsing" +f8abb112 Merge changes I109ec4d9,I73fe7743 +ae2188a4 MIPS: dspr2: added optimization for function Intra4Preds +1f4b8642 move VP8EncDspARGBInit() call closer to where it's needed +14108d78 dec_neon: add DC8uvNoTop / DC8uvNoLeft +d8340da7 dec_neon: add DC8uv +a66e66c7 webp-container-spec: remove references to fragments +7ce8788b MIPS: dspr2: added optimization for function MakeARGB32 +012e623d demux: remove experimental FRGM parsing +87c3d531 method=0: Don't evaluate any predictor +6f4fcb98 Merge "MIPS: dspr2: added optimization for function ImportRow" +24284459 replace unneeded calls to HistogramCopy() by swaps +bdf7b40c MIPS: dspr2: added optimization for function ImportRow +e66a9225 Merge "MIPS: dspr2: added optimization for function ExportRowC" +c279fec1 MIPS: dspr2: added optimization for function ExportRowC +31a9cf64 Speedup WebP lossless compression for low effort (m=0) mode with following: - Disable Cross-Color transform. - Evaluate predictors #11 (paeth), #12 and #13 only. +9275d91c MIPS: dspr2: added optimization for function TrueMotion +26106d66 Merge "enc_neon: fix building with non-Xcode clang (iOS)" +1c4e3efe unroll the kBands[] indirection to remove a dereference in GetCoeffs() +a3946b89 enc_neon: fix building with non-Xcode clang (iOS) +8ed9c00d Merge "simplify the Histogram struct, to only store max_value and last_nz" +bad77571 simplify the Histogram struct, to only store max_value and last_nz +3cca0dc7 MIPS: dspr2: Added optimization for DCMode function +37e395fd MIPS: fix functions to use generic BPS istead of hardcoded value +9475bef4 PickBestUV: fix VP8Copy16x8 invocation +441f273f Merge changes I55f8da52,Id73a1e96 +4a279a68 cosmetics: add some missing != NULL comparisons +66ad3725 factorize BPS definition in dsp.h and add VP8Copy16x8 +432e5b55 make ALIGN_xxx naming consistent +57606047 encoder: switch BPS to 32 instead of 16 +1b66bbe9 MIPS: dspr2: added optimization for function TransformColor_C +c6d0f9e7 histogram: cosmetics +f399d307 Merge changes I6eac17e5,I32d2b514 +9de9074c dec_neon: add TM8uv +8e517eca bit_reader/kVP8NewRange: range_t -> uint8_t +e1857139 dsp: initialize VP8PredChroma8 in VP8DspInit() +e0c809ad Move Entropy methods to lossless.c +a96ccf8f iosbuild: add x64_64 simulator support +a0df5510 Remove handling for WEBP_HINT_GRAPH +413dfc0c Move static method definition before its usage. +0f235665 Update BackwardRefsWithLocalCache. +d69e36ec Remove TODOs from lossless encoder code. +fdaac8e0 Optmize VP8LGetBackwardReferences LZ77 references. +2f0e2ba8 MIPS: dspr2: added optimization for function Select +a3e79a46 Merge "WebPEncode: Support encoding same pic twice (even if modified)" +e4f4dddb WebPEncode: Support encoding same pic twice (even if modified) +cbc3fbb4 Merge "Updated VP8LGetBackwardReferences and color cache." +95a9bd85 Updated VP8LGetBackwardReferences and color cache. +54f2c14c MIPS: dspr2: added optimization for function FTransform +aa42f423 MIPS: dspr2: Added optimization for function VP8LSubtractGreenFromBlueAndRed +11a25f75 Merge "FlattenSimilarBlocks should only be tried when blending is possible." +5cccdadf FlattenSimilarBlocks should only be tried when blending is possible. +95ca44a7 MIPS: dspr2: added optimization for Disto4x4 +4171b672 backward_references.c: reindent after c8581b0 +c8581b06 Optimize BackwardReferences for RLE encoding. +5798eee6 MIPS: dspr2: unfilters bugfix (Ie7b7387478a6b5c3f08691628ae00f059cf6d899) +4167a3f5 Optimize backwardreferences +d18554c3 Merge "webp/types.h: use inline for clang++/-std=c++11" +7489b0e7 gif2webp: Add '-min-size' option to get best compression. +77bdddf0 Speed up BackwardReferences +6638710b webp/types.h: use inline for clang++/-std=c++11 +abf04205 Enable entropy based merge histo for (q<100) +572022a3 filters_mips_dsp_r2.c: disable unfilters +a28e21b1 MIPS: dspr2: Added optimization for function ClampedAddSubtractFull +18d5a1ef MIPS: dspr2: added optimization for function ClampedAddSubtractHalf +829a8c19 MIPS: dspr2: added optimization for ITransform +c94ed49e gif2webp: Use the default hint instead of WEBP_HINT_GRAPH. +653ace55 Increase the MAX_COLOR_CACHE_BITS from 9 to 10. +919220c7 Change the logic adjusting the Histogram bits. +53b096c0 Merge "Fix bug in VP8LCalculateEstimateForCacheSize." +e912bd55 Fix bug in VP8LCalculateEstimateForCacheSize. +541d7839 Merge "dec_neon: add RD4 intra predictor" +f8cd0672 Merge "Makefile.vc: add a 'legacy' RTLIBCFG option" +22881c99 dec_neon: add RD4 intra predictor +613d281e update NEWS +1304eb34 Merge "dec_neon: DC4: use pair-wise adds for top row" +34c20c06 Makefile.vc: add a 'legacy' RTLIBCFG option +7083006b Merge "dsp/dec_{neon,sse2}: VE4: normalize variable names" +0db9031c dsp/dec_{neon,sse2}: VE4: normalize variable names +b5bc1530 dec_neon: DC4: use pair-wise adds for top row +5b90d8fe Unify the API between VP8BitWriter and VP8LBitWriter +f7ada560 Merge changes I2e06907b,Ia9ed4ca6,I782282ff +5beb6bf0 Merge "dec_neon: add VE4 intra predictor" +eba6ce06 dec_neon: add DC4 intra predictor +79abfbd9 dec_neon: add TM4 intra predictor +fe395f0e dec_neon: add LD4 intra predictor +32de385e dec_neon: add VE4 intra predictor +72395ba9 Merge "Modify CostModel to allocate optimal memory." +65e5eb8a gif2webp: Support GIF_DISPOSE_RESTORE_PREVIOUS +e4c829ef gif2webp: Handle frames with odd offsets + disposal to background. +c2b5a039 Modify CostModel to allocate optimal memory. +b7a33d7e implement VE4/HE4/RD4/... in SSE2 +97c76f1f make VP8PredLuma4[] non-const and initialize array in VP8DspInit() +0ea8c6c2 Merge "PrintReg: output to stderr" +d7ff2f97 Merge "stopwatch.h: fix includes" +f85ec712 PrintReg: output to stderr +54edbf65 stopwatch.h: fix includes +139142e4 Optimize BackwardReferenceHashChainFollowPath. +5f36b68d enc/backward_references.c: fix indent +e0e9960d Merge "sync version numbers to 0.4.2 release" +64ac5144 sync version numbers to 0.4.2 release +c24f8954 Simplify and speedup Backward refs computation. +d1c359ef fix shared object build with -fvisibility=hidden +a4c3a31b WEBP_TSAN_IGNORE_FUNCTION: fix gcc compat warning +f358eeb8 add code for testing random incremental decoding in dwebp +80247291 mark some init function as being safe for thread_sanitizer. +79b5bdbf bit_reader.h: cosmetics: fix a typo +6c673681 Improved near-lossless mode. +0ce27e71 enc_mips32: workaround gcc-4.9 bug +aca1b98f enc/vp8l.c: fix indent +ca005027 Evaluate non-palette compression for palette image +c8a87bb6 AssignSegments: quiet -Warray-bounds warning +32f67e30 Merge "enc_neon: initialize vectors w/vdup_n_u32" +fabc65da 1-3% faster encoding optimizing SSE_NxN functions +7534d716 enc_neon: initialize vectors w/vdup_n_u32 +5f813912 Merge "Fix return code of EncodeImageInternal()" +e321abe4 Fix return code of EncodeImageInternal() +f82cb06a optimize palette ordering +f545feee don't set the alpha value for histogram index image +2d9b0a44 add WebPDispatchAlphaToGreen() to dsp +1bd4c2ad Merge "Change Entropy based Histogram Combine heuristic." +e295b8f1 Merge "iosbuild: cleanup" +1be4e760 Merge "iosbuild: output autoconf req. on failure" +d5e498d4 Change Entropy based Histogram Combine heuristic. +47a2d8e1 fix MSVC float->int conversion warning +041956f6 iosbuild: cleanup +767eb402 iosbuild: output autoconf req. on failure +35ad48b8 HistoHeapInit: correct positions allocation size +45d9635f lossless: entropy clustering for high qualities. +dc37df8c fix type warning for VS9_x64 +9f7d9e6d iosbuild: make iOS 6 the minimum requirement +fdd6528b Remove unused VP8LDecoder member variable +ea3bba5a Merge "rewrite Disto4x4 in enc_neon.c with intrinsic" +f060dfc4 add lossless incremental decoding support +ab70794d rewrite Disto4x4 in enc_neon.c with intrinsic +d4471637 MIPS: dspr2: added optimization for function FilterLoop24 +2aef54d4 Merge "prepare VP8LDecodeImage for incremental decode" +aed0f5a2 Merge "MIPS: dspr2: added optimization for function FilterLoop26" +28630685 prepare VP8LDecodeImage for incremental decode +248f3aed remove br->error_ field +49e15044 MIPS: dspr2: added optimization for function FilterLoop26 +38128cb9 iobuild.sh: only install .h files in Headers +c792d412 Premultiply with alpha during U/V downsampling +0cc811d7 gif2webp: Background color correction +d7167ff7 Amend the lossless spec according to issue #205, #206 and #224 +b901416b Record the lossless size stats. +cddd3340 Add a WebPExtractAlpha function to dsp +0716a98e fix indent after I0204949917836f74c0eb4ba5a7f4052a4797833b +f9ced95a Optimize lossless decoding for trivial(ARB) codes. +924fcfd9 Merge "webpmux: simplify InitializeConfig()" +c0a462ca webpmux: simplify InitializeConfig() +6986bb5e webpmux: fix indent +f89e1690 webpmux: fix exit status on numeric value parse error +2172cb62 Merge "webpmux: fix loop_count range check" +e3b343ec Merge "examples: warn on invalid numeric parameters" +0e23c487 webpmux: fix loop_count range check +6208338a Merge "fix loop bug in DispatchAlpha()" +d51f3e40 gif2webp: Handle frames with missing graphic control extension +690b491a fix loop bug in DispatchAlpha() +96d43a87 examples: warn on invalid numeric parameters +3101f537 MIPS: dspr2: added optimization for TransformOne +a6bb9b17 SSE2 for inverse Mult(ARGB)Row and ApplyAlphaMultiply +d84a8ffd Remove default initialization of decoder status. +be70b86c configure: simplify libpng-config invocation +e0a99321 Rectify bug in lossless incremental decoding. +e2502a97 MIPS: dspr2: added optimization for TransformAC3 +24e1072a MIPS: dspr2: added optimization for TransformDC +c0e84df8 Merge "Slightly faster lossless decoding (1%)" +8dd28bb5 Slightly faster lossless decoding (1%) +f0103595 MIPS: dspr2: added optimization for ColorIndexInverseTransforms +d3242aee make VP8LSetBitPos() set br->eos_ flag +a9decb55 Lossless decoding: fix eos_ flag condition +3fea6a28 fix erroneous dec->status_ setting +80b8099f MIPS: dspr2: add some specific mips code to commit I2c3f2b12f8df15b785fad5a9c56316e954ae0c53 +e5640625 Merge "further refine the COPY_PATTERN optim for DecodeAlpha" +854509fe enc/histogram.c: reindent after f4059d0 +34421964 Merge "~3-5% faster encoding optimizing PickBestIntra*()" +865069c1 further refine the COPY_PATTERN optim for DecodeAlpha +a5956228 added C-level optimization for DecodeAlphaData function +187d379d add a fallback to ALPHA_NO_COMPRESSION +a48a2d76 ~3-5% faster encoding optimizing PickBestIntra*() +a6140194 ExUtilReadFromStdin: (windows) open stdin in bin mode +e80eab1f webpmux: (windows) open stdout in binary mode +e9bfb116 cwebp: (windows) open stdout in binary mode +5927e15b example_util: add ExUtilSetBinaryMode +30f3b75b webpmux man page: Clarify some title, descriptions and examples +77d4c7e3 address cosmetic comments from patch #71380 +f75dfbf2 Speed up Huffman decoding for lossless +637b3888 dsp/lossless: workaround gcc-4.9 bug on arm +8323a903 dsp.h: collect gcc/clang version test macros +e6c4b52f move static initialization of WebPYUV444Converters[] to the Init function. +49911d4d Merge "fix indentation" +f4059d0c Code cleanup for HistogramRemap. +e632b092 fix indentation +f5c04d64 Merge "add a DispatchAlpha() for SSE2 that handles 8 pixels at a time" +fc98edd9 add a DispatchAlpha() for SSE2 that handles 8 pixels at a time +73d361dd introduce VP8EncQuantize2Blocks to quantize two blocks at a time +0b21c30b MIPS: dspr2: added optimization for EmitAlphaRGB +953acd56 enc_neon: enable QuantizeBlock for aarch64 +f4ae1437 MIPS: mips32: code rebase +56977154 MIPS: dspr2: added optimizations for VP8YuvTo* +2523aa73 SmartRGBYUV: fix odd-width problem with pixel replication +ee52dc4e fix some MSVC64 warning about float conversion +3fca851a cpu: check for _MSC_VER before using msvc inline asm +e2a83d71 faster RGB->YUV conversion function (~7% speedup) +de2d03e1 Merge "Add smart RGB->YUV conversion option -pre 4" +3fc4c539 Add smart RGB->YUV conversion option -pre 4 +b4dc4069 MIPS: dspr2: added optimization for (un)filters +137e6090 Merge "configure: add work around for gcc-4.9 aarch64 bug" +b61c9cec MIPS: dspr2: Optimization of some simple point-sampling functions +e2b8cec0 configure: add work around for gcc-4.9 aarch64 bug +98c54107 MIPS: mips32r2: added optimization for BSwap32 +dab702b3 Update PATENTS to reflect s/VP8/WebM/g +b564f7c7 Merge "MIPS: detect mips32r6 and disable mips32r1 code" +b7e5a5c4 MIPS: detect mips32r6 and disable mips32r1 code +63c2fc02 Correctly use the AC_CANONICAL_* macros +bb07022b Merge "cosmetics" +e300c9d8 cosmetics +0e519eea Merge "cosmetics: remove some extraneous 'extern's" +3ef0f08a Merge "vp8enci.h: cosmetics: fix '*' placement" +4c6dde37 bit_writer: cosmetics: rename kFlush() -> Flush() +f7b4c48b cosmetics: remove some extraneous 'extern's +b47fb00a vp8enci.h: cosmetics: fix '*' placement +b5a36cc9 add -near_lossless [0..100] experimental option +0524d9e5 dsp: detect mips64 & disable mips32 code +d3485d96 cwebp.1: fix quality description placement +29a9fe22 Merge tag 'v0.4.1' +8af27718 update ChangeLog (tag: v0.4.1, origin/0.4.1) +e09e9ff6 Record & log the image pre-processing time. +f59c0b4b iosbuild.sh: specify optimization flags +8d34ea3e update ChangeLog (tag: v0.4.1-rc1) +dbc3da66 makefile.unix: add vwebp.1 to the dist target +89a7c83c update ChangeLog +ffe67ee9 Merge "update NEWS for the next release" into 0.4.1 +2def1fe6 gif2webp: dust up the help message +fb668d78 remove -noalphadither option from README/vwebp.1 +e49f693b update NEWS for the next release +cd013580 Merge "update AUTHORS" into 0.4.1 +268d01eb update AUTHORS +85213b9b bump version to 0.4.1 +695f80ae Merge "restore mux API compatibility" into 0.4.1 +862d296c restore mux API compatibility +8f6f8c5d remove the !WEBP_REFERENCE_IMPLEMENTATION tweak in Put8x8uv +d713a696 Merge changes If4debc15,I437a5d5f into 0.4.1 +c2fc52e4 restore encode API compatibility +793368e8 restore decode API compatibility +b8984f31 gif2webp: fix compile with giflib 5.1.0 +222f9b1a gif2webp: simplify giflib version checking +d2cc61b7 Extend MakeARGB32() to accept Alpha channel. +4595b62b Merge "use explicit size of kErrorMessages[] arrays" +157de015 Merge "Actuate memory stats for PRINT_MEMORY_INFO" +fbda2f49 JPEG decoder: delay conversion to YUV to WebPEncode() call +0b747b1b use explicit size of kErrorMessages[] arrays +3398d81a Actuate memory stats for PRINT_MEMORY_INFO +6f3202be Merge "move WebPPictureInit to picture.c" +6c347bbb move WebPPictureInit to picture.c +fb3acf19 fix configure message for multi-thread +40b086f7 configure: check for _beginthreadex +1549d620 reorder the YUVA->ARGB and ARGB->YUVA functions correctly +c6461bfd Merge "extract colorspace code from picture.c into picture_csp.c" +736f2a17 extract colorspace code from picture.c into picture_csp.c +645daa03 Merge "configure: check for -Wformat-security" +abafed86 configure: check for -Wformat-security +fbadb480 split monolithic picture.c into picture_{tools,psnr,rescale}.c +c76f07ec dec_neon/TransformAC3: initialize vector w/vcreate +bb4fc051 gif2webp: Allow single-frame animations +46fd44c1 thread: remove harmless race on status_ in End() +5a1a7264 Merge "configure: check for __builtin_bswapXX()" +6781423b configure: check for __builtin_bswapXX() +6450c48d configure: fix iOS builds +6422e683 VP8LFillBitWindow: enable fast path for 32-bit builds +4f7f52b2 VP8LFillBitWindow: respect WEBP_FORCE_ALIGNED +e458badc endian_inl.h: implement htoleXX with BSwapXX +f2664d1a endian_inl.h: add BSwap16 +6fbf5345 Merge "configure: add --enable-aligned" +dc0f479d configure: add --enable-aligned +9cc69e2b Merge "configure: support WIC + OpenGL under mingw64" +257adfb0 remove experimental YUV444 YUV422 and YUV400 code +10f4257c configure: support WIC + OpenGL under mingw64 +380cca4f configure.ac: add AC_C_BIGENDIAN +ee70a901 endian_inl.h: add BSwap64 +47779d46 endian_inl.h: add BSwap32 +d5104b1f utils: add endian_inl.h +58ab6224 Merge "make alpha-detection loop in IsKeyFrame() in good x/y order" +9d562902 make alpha-detection loop in IsKeyFrame() in good x/y order +516971b1 lossless: Remove unaligned read warning +b8b596f6 Merge "configure.ac: add an autoconf version prerequisite" +34b02f8c configure.ac: add an autoconf version prerequisite +e59f5360 neon: normalize vdup_n_* usage +6ee7160d Merge changes I0da7b3d3,Idad2f278,I4accc305 +abc02f24 Merge "fix (uncompiled) typo" +bc03670f neon: add INIT_VECTOR4 +6c1c632b neon: add INIT_VECTOR3 +dc7687e5 neon: add INIT_VECTOR2 +4536e7c4 add WebPMuxSetCanvasSize() to the mux API +824eab10 fix (uncompiled) typo +1f3e5f1e remove unused 'shift' argument and QFIX2 define +8e867051 Merge "VP8LoadNewBytes: use __builtin_bswap32 if available" +1b6a2635 Merge "Fix handling of weird GIF with canvas dimension 0x0" +1da3d461 VP8LoadNewBytes: use __builtin_bswap32 if available +1582e402 Fix handling of weird GIF with canvas dimension 0x0 +b8811dac Merge "rename interface -> winterface" +db8b8b5f Fix logic in the GIF LOOP-detection parsing +25aaddc8 rename interface -> winterface +5584d9d2 make WebPSetWorkerInterface() check its arguments +a9ef7ef9 Merge "cosmetics: update thread.h comments" +c6af9991 Merge "dust up the help message" +0a8b8863 dust up the help message +a9cf3191 cosmetics: update thread.h comments +27bfeee4 QuantizeBlock SSE2 Optimization: +2bc0dc3e Merge "webpmux: warn when odd frame offsets are used" +3114ebe4 Merge changes Id8edd3c1,Id418eb96,Ide05e3be +c0726634 webpmux: warn when odd frame offsets are used +c5c6b408 Merge "add alpha dithering for lossy" +d5146784 examples/Android.mk: add cwebp +ca0fa7c7 Android.mk: move dwebp to examples/Android.mk +73d8fca0 Android.mk: add ENABLE_SHARED flag +6e93317f muxread: fix out of bounds read +8b0f6a48 Makefile.vc: fix CFLAGS assignment w/HAVE_AVX2=1 +bbe32df1 add alpha dithering for lossy +79020767 Merge "make error-code reporting consistent upon malloc failure" +77bf4410 make error-code reporting consistent upon malloc failure +7a93c000 **/Makefile.am: remove unused AM_CPPFLAGS +24e30805 Add an interface abstraction to the WebP worker thread implementation +d6cd6358 Merge "fix orig_rect==NULL case" +2bfd1ffa fix orig_rect==NULL case +059e21c1 Merge "configure: move config.h to src/webp/config.h" +f05fe006 properly report back encoding error code in WebPFrameCacheAddFrame() +32b31379 configure: move config.h to src/webp/config.h +90090d99 Merge changes I7c675e51,I84f7d785 +ae7661b3 makefiles: define WEBP_HAVE_AVX2 when appropriate +69fce2ea remove the special casing for res->first in VP8SetResidualCoeffs +6e61a3a9 configure: test for -msse2 +b9d2efc6 rename upsampling_mips32.c to yuv_mips32.c +bdfeebaa dsp/yuv: move sse2 functions to yuv_sse2.c +46b32e86 Merge "configure: set WEBP_HAVE_AVX2 when available" +88305db4 Merge "VP8RandomBits2: prevent signed int overflow" +73fee88c VP8RandomBits2: prevent signed int overflow +db4860b3 enc_sse2: prevent signed int overflow +3fdaf4d2 Merge "real fix for longjmp warning" +385e3340 real fix for longjmp warning +230a0555 configure: set WEBP_HAVE_AVX2 when available +a2ac8a42 restore original value_/range_ field order +5e2ee56f Merge "remove libwebpdspdecode dep on libwebpdsp_avx2" +61362db5 remove libwebpdspdecode dep on libwebpdsp_avx2 +42c447ae Merge "lossy bit-reader clean-up:" +479ffd8b Merge "remove unused #include's" +9754d39a Merge "strong filtering speed-up (~2-3% x86, ~1-2% for NEON)" +158aff9b remove unused #include's +09545eea lossy bit-reader clean-up: +ea8b0a17 strong filtering speed-up (~2-3% x86, ~1-2% for NEON) +6679f899 Optimize VP8SetResidualCoeffs. +ac591cf2 fix for gcc-4.9 warnings about longjmp + local variables +4dfa86b2 dsp/cpu: NaCl has no support for xgetbv +4c398699 Merge "cwebp: fallback to native webp decode in WIC builds" +33aa497e Merge "cwebp: add some missing newlines in longhelp output" +c9b340a2 fix missing WebPInitAlphaProcessing call for premultiplied colorspace output +57897bae Merge "lossless_neon: use vcreate_*() where appropriate" +6aa4777b Merge "(enc|dec)_neon: use vcreate_*() where appropriate" +0d346e41 Always reinit VP8TransformWHT instead of hard-coding +7d039fc3 cwebp: fallback to native webp decode in WIC builds +d471f424 cwebp: add some missing newlines in longhelp output +bf0e0030 lossless_neon: use vcreate_*() where appropriate +9251c2f6 (enc|dec)_neon: use vcreate_*() where appropriate +399b916d lossy decoding: correct alpha-rescaling for YUVA format +78c12ed8 Merge "Makefile.vc: add rudimentary avx2 support" +dc5b122f try to remove the spurious warning for static analysis +ddfefd62 Makefile.vc: add rudimentary avx2 support +a8911643 Merge "simplify VP8LInitBitReader()" +fdbcd44d simplify VP8LInitBitReader() +7c004287 makefile.unix: add rudimentary avx2 support +515e35cf Merge "add stub dsp/enc_avx2.c" +a05dc140 SSE2: yuv->rgb speed-up for point-sampling +178e9a69 add stub dsp/enc_avx2.c +1b99c09c Merge "configure: add a test for -mavx2" +fe728071 configure: add a test for -mavx2 +e46a247c cpu: fix check for __cpuidex availability +176fda26 fix the bit-writer for lossless in 32bit mode +541784c7 dsp.h: add a check for AVX2 / define WEBP_USE_AVX2 +bdb151ee dsp/cpu: add AVX2 detection +ab9f2f86 Merge "revamp the point-sampling functions by processing a full plane" +a2f8b289 revamp the point-sampling functions by processing a full plane +ef076026 use decoder's DSP functions for autofilter +2b5cb326 Merge "dsp/cpu: add AVX detection" +df08e67e dsp/cpu: add AVX detection +e2f405c9 Merge "clean-up and slight speed-up in-loop filtering SSE2" +f60957bf clean-up and slight speed-up in-loop filtering SSE2 +9fc3ae46 .gitattributes: treat .ppm as binary +3da924b5 Merge "dsp/WEBP_USE_NEON: test for __aarch64__" +c7164490 Android.mk: always include *_neon.c in the build +a577b23a dsp/WEBP_USE_NEON: test for __aarch64__ +54bfffca move RemapBitReader() from idec.c to bit_reader code +34168ecb Merge "remove all unused layer code" +f1e77173 remove all unused layer code +b0757db7 Code cleanup for VP8LGetHistoImageSymbols. +5fe628d3 make the token page size be variable instead of fixed 8192 +f948d08c memory debug: allow setting pre-defined malloc failure points +ca3d746e use block-based allocation for backward refs storage, and free-lists +1ba61b09 enable NEON intrinsics in aarch64 builds +b9d2bb67 dsp/neon.h: coalesce intrinsics-related defines +b5c75258 iosbuild: add support for iOSv7/aarch64 +9383afd5 Reduce number of memory allocations while decoding lossless. +888e63ed Merge "dsp/lossless: prevent signed int overflow in left shift ops" +8137f3ed Merge "instrument memory allocation routines for debugging" +2aa18736 instrument memory allocation routines for debugging +d3bcf72b Don't allocate VP8LHashChain, but treat like automatic object +bd6b8619 dsp/lossless: prevent signed int overflow in left shift ops +b7f19b83 Merge "dec/vp8l: prevent signed int overflow in left shift ops" +29059d51 Merge "remove some uint64_t casts and use." +e69a1df4 dec/vp8l: prevent signed int overflow in left shift ops +cf5eb8ad remove some uint64_t casts and use. +38e2db3e MIPS: MIPS32r1: Added optimization for HistogramAdd. +e0609ade dwebp: fix exit code on webp load failure +bbd358a8 Merge "example_util.h: avoid forward declaring enums" +8955da21 example_util.h: avoid forward declaring enums +6d6865f0 Added SSE2 variants for Average2/3/4 +b3a616b3 make HistogramAdd() a pointer in dsp +c8bbb636 dec_neon: relocate some inline-asm defines +4e393bb9 dec_neon: enable intrinsics-only functions +ba99a922 dec_neon: use positive tests for USE_INTRINSICS +69058ff8 Merge "example_util: add ExUtilDecodeWebPIncremental" +a7828e8b dec_neon: make WORK_AROUND_GCC conditional on version +3f3d717a Merge "enc_neon: enable intrinsics-only functions" +de3cb6c8 Merge "move LOCAL_GCC_VERSION def to dsp.h" +1b2fe14d example_util: add ExUtilDecodeWebPIncremental +ca49e7ad Merge "enc_neon: move Transpose4x4 to dsp/neon.h" +ad900abd Merge "fix warning about size_t -> int conversion" +4825b436 fix warning about size_t -> int conversion +42b35e08 enc_neon: enable intrinsics-only functions +f937e012 move LOCAL_GCC_VERSION def to dsp.h +5e1a17ef enc_neon: move Transpose4x4 to dsp/neon.h +c7b92a5a dec_neon: (WORK_AROUND_GCC) delete unused Load4x8 +8e5f90b0 Merge "make ExUtilLoadWebP() accept NULL bitstream param." +05d4c1b7 Merge "cwebp: add webpdec" +ddeb6ac8 cwebp: add webpdec +35d7d095 Merge "Reduce memory footprint for encoding WebP lossless." +0b896101 Reduce memory footprint for encoding WebP lossless. +f0b65c9a make ExUtilLoadWebP() accept NULL bitstream param. +9c0a60cc Merge "dwebp: move webp decoding to example_util" +1d62acf6 MIPS: MIPS32r1: Added optimization for HuffmanCost functions. +4a0e7390 dwebp: move webp decoding to example_util +c0220460 Merge "Bugfix: Incremental decode of lossy-alpha" +8c7cd722 Bugfix: Incremental decode of lossy-alpha +7955152d MIPS: fix error with number of registers. +b1dabe37 Merge "Move the HuffmanCost() function to dsp lib" +75b12006 Move the HuffmanCost() function to dsp lib +2772b8bd MIPS: fix assembler error revealed by clang's debug build +6653b601 enc_mips32: fix unused symbol warning in debug +8dec1209 enc_mips32: disable ITransform(One) in debug builds +98519dd5 enc_neon: convert Disto4x4 to intrinsics +fe9317c9 cosmetics: +953b0746 enc_neon: cosmetics +a9fc697c Merge "WIP: extract the float-calculation of HuffmanCost from loop" +3f84b521 Merge "replace some mult-long (vmull_u8) with mult-long-accumulate (vmlal_u8)" +4ae0533f MIPS: MIPS32r1: Added optimizations for ExtraCost functions. +b30a04cf WIP: extract the float-calculation of HuffmanCost from loop +a8fe8ce2 Merge "NEON intrinsics version of CollectHistogram" +95203d2d NEON intrinsics version of CollectHistogram +7ca2e74b replace some mult-long (vmull_u8) with mult-long-accumulate (vmlal_u8) +41c6efbd fix lossless_neon.c +8ff96a02 NEON intrinsics version of FTransform +0214f4a9 Merge "MIPS: MIPS32r1: Added optimizations for FastLog2" +baabf1ea MIPS: MIPS32r1: Added optimizations for FastLog2 +3d49871d NEON functions for lossless coding +3fe02915 MIPS: MIPS32r1: Added optimizations for SSE functions. +c503b485 Merge "fix the gcc-4.6.0 bug by implementing alternative method" +abe6f487 fix the gcc-4.6.0 bug by implementing alternative method +5598bdec enc_mips32.c: fix file mode +2b1b4d5a MIPS: MIPS32r1: Add optimization for GetResidualCost +f0a1f3cd Merge "MIPS: MIPS32r1: Added optimization for FTransform" +7231f610 MIPS: MIPS32r1: Added optimization for FTransform +869eaf6c ~30% encoding speedup: use NEON for QuantizeBlock() +f758af6b enc_neon: convert FTransformWHT to intrinsics +7dad095b MIPS: MIPS32r1: Added optimization for Disto4x4 (TTransform) +2298d5f3 MIPS: MIPS32r1: Added optimization for QuantizeBlock +e88150c9 Merge "MIPS: MIPS32r1: Add optimization for ITransform" +de693f25 lossless_neon: disable VP8LConvert* functions +4143332b NEON intrinsics for encoding +0ca2914b MIPS: MIPS32r1: Add optimization for ITransform +71bca5ec dec_neon: use vst_lane instead of vget_lane +bf061052 Intrinsics NEON version of TransformOne +19c6f1ba Merge "dec_neon: use vld?_lane instead of vset?_lane" +7a94c0cf upsampling_neon: drop NEON suffix from local functions +d14669c8 upsampling_sse2: drop SSE2 suffix from local functions +2ca42a4f enc_sse2: drop SSE2 suffix from local functions +d038e619 dec_sse2: drop SSE2 suffix from local functions +fa52d752 dec_neon: use vld?_lane instead of vset?_lane +c520e77d cosmetic: fix long line +4b0f2dae Merge "add intrinsics NEON code for chroma strong-filtering" +e351ec07 add intrinsics NEON code for chroma strong-filtering +aaf734b8 Merge "Add SSE2 version of forward cross-color transform" +c90a902e Add SSE2 version of forward cross-color transform +bc374ff3 Use histogram_bits to initalize transform_bits. +2132992d Merge "Add strong filtering intrinsics (inner and outer edges)" +5fbff3a6 Add strong filtering intrinsics (inner and outer edges) +d4813f0c Add SSE2 function for Inverse Cross-color Transform +26029568 dec_neon: add strong loopfilter intrinsics +cca7d7ef Merge "add intrinsics version of SimpleHFilter16NEON()" +1a05dfa7 windows: fix dll builds +d6c50d8a Merge "add some colorspace conversion functions in NEON" +4fd7c82e SSE2 variants of Subtract-Green: Rectify loop condition +97e5fac3 add some colorspace conversion functions in NEON +b9a7a45f add intrinsics version of SimpleHFilter16NEON() +daccbf40 add light filtering NEON intrinsics +af444608 fix typo in STORE_WHT +6af6b8e1 Tune HistogramCombineBin for large images. +af93bdd6 use WebPSafe[CM]alloc/WebPSafeFree instead of [cm]alloc/free +51f406a5 lossless_sse2: relocate VP8LDspInitSSE2 proto +0f4f721b separate SSE2 lossless functions into its own file +514fc251 VP8LConvertFromBGRA: use conversion function pointers +6d2f3527 dsp/dec: TransformDCUV: use VP8TransformDC +defc8e1b Merge "fix out-of-bound read during alpha-plane decoding" +fbed3643 Merge "dsp: reuse wht transform from dec in encoder" +d8467084 Merge "Add SSE2 version of ARGB -> BGR/RGB/... conversion functions" +207d03b4 fix out-of-bound read during alpha-plane decoding +d1b33ad5 2-5% faster trellis with clang/MacOS (and ~2-3% on ARM) +369c26dd Add SSE2 version of ARGB -> BGR/RGB/... conversion functions +df230f27 dsp: reuse wht transform from dec in encoder +80e218d4 Android.mk: fix build with APP_ABI=armeabi-v7a-hard +59daf083 Merge "cosmetics:" +53622008 cosmetics: +3e7f34a3 AssignSegments: quiet array-bounds warning +3c2ebf58 Merge "UpdateHistogramCost: avoid implicit double->float" +cf821c82 UpdateHistogramCost: avoid implicit double->float +312e638f Extend the search space for GetBestGreenRedToBlue +1c58526f Fix few nits +fef22704 Optimize and re-structure VP8LGetHistoImageSymbols +068b14ac Optimize lossless decoding. +5f0cfa80 Do a binary search to get the optimum cache bits. +24ca3678 Merge "allow 'cwebp -o -' to emit output to stdout" +e12f874e allow 'cwebp -o -' to emit output to stdout +2bcad89b allow some more stdin/stout I/O +84ed4b3a fix cwebp.1 typos after patch #69199 +65b99f1c add a -z option to cwebp, and WebPConfigLosslessPreset() function +30176619 4-5% faster trellis by removing some unneeded calculations. +687a58ec histogram.c: reindent after b33e8a0 +06d456f6 Merge "~3-4% faster lossless encoding" +c60de260 ~3-4% faster lossless encoding +42eb06fc Merge "few cosmetics after patch #69079" +82af8264 few cosmetics after patch #69079 +b33e8a05 Refactor code for HistogramCombine. +ca1bfff5 Merge "5-10% encoding speedup with faster trellis (-m 6)" +5aeeb087 5-10% encoding speedup with faster trellis (-m 6) +82ae1bf2 cosmetics: normalize VP8GetCPUInfo checks +e3dd9243 Merge "Refactor GetBestPredictorForTile for future tuning." +206cc1be Refactor GetBestPredictorForTile for future tuning. +3cb84062 Merge "speed-up trellis quant (~5-10% overall speed-up)" +b66f2227 Merge "lossy encoding: ~3% speed-up" +4287d0d4 speed-up trellis quant (~5-10% overall speed-up) +390c8b31 lossy encoding: ~3% speed-up +9a463c4a Merge "dec_neon: convert TransformWHT to intrinsics" +e8605e96 Merge "dec_neon: add ConvertU8ToS16" +4aa3e412 MIPS: MIPS32r1: rescaler bugfix +c16cd99a Speed up lossless encoder. +9d6b5ff1 dec_neon: convert TransformWHT to intrinsics +2ff0aae2 dec_neon: add ConvertU8ToS16 +77a8f919 fix compilation with USE_YUVj flag +4acbec1b Merge changes I3b240ffb,Ia9370283,Ia2d28728 +2719bb7e dec_neon: TransformAC3: work on packed vectors +b7b60ca1 dec_neon: add SaturateAndStore4x4 +b7685d73 Rescale: let ImportRow / ExportRow be pointer-to-function +e02f16ef dec_neon.c: convert TransformDC to intrinsics +9cba963f add missing file +8992ddb7 use static clipping tables +0235d5e4 1-2% faster quantization in SSE2 +b2fbc36c fix VC12-x64 warning +6e37cb94 Merge "cosmetics: backward_references.c: reindent after a7d2ee3" +a42ea974 cosmetics: backward_references.c: reindent after a7d2ee3 +6c327442 Merge "fix missing __BIG_ENDIAN__ definition on some platform" +a8b6aad1 fix missing __BIG_ENDIAN__ definition on some platform +fde2904b Increase initial buffer size for VP8L Bit Writer. +a7d2ee39 Optimize cache estimate logic. +7fb6095b Merge "dec_neon.c: add TransformAC3" +bf182e83 VP8LBitWriter: use a bit-accumulator +3f40b4a5 Merge "MIPS: MIPS32r1: clang macro warning resolved" +1684f4ee WebP Decoder: Mark some truncated bitstreams as invalid +acbedac4 MIPS: MIPS32r1: clang macro warning resolved +228e4877 dec_neon.c: add TransformAC3 +393f89b7 Android.mk: avoid gcc-specific flags with clang +32aeaf11 revamp VP8LColorSpaceTransform() a bit +0c7cc4ca Merge "Don't dereference NULL, ensure HashChain fully initialized" +391316fe Don't dereference NULL, ensure HashChain fully initialized +926ff402 WEBP_SWAP_16BIT_CSP: remove code dup +1d1cd3bb Fix decode bug for rgbA_4444/RGBA_4444 color-modes. +939e70e7 update AUTHORS file +8934a622 cosmetics: *_mips32.c +dd438c9a MIPS: MIPS32r1: Optimization of some simple point-sampling functions. PATCH [6/6] +53520911 Added support for calling sampling functions via pointers. +d16c6974 MIPS: MIPS32r1: Optimization of filter functions. PATCH [5/6] +04336fc7 MIPS: MIPS32r1: Optimization of function TransformOne. PATCH [4/6] +92d8fc7d MIPS: MIPS32r1: Optimization of function WebPRescalerImportRow. PATCH [3/6] +bbc23ff3 parse one row of intra modes altogether +a2f608f9 Merge "MIPS: MIPS32r1: Optimization of function WebPRescalerExportRow. [2/6]" +88230854 MIPS: MIPS32r1: Optimization of function WebPRescalerExportRow. [2/6] +c5a5b028 decode mt+incremental: fix segfault in debug builds +9882b2f9 always use fast-analysis for all methods. +000adac0 Merge "autoconf: update ax_pthread.m4" +2d2fc37d update .gitignore +5bf4255a Merge "Make it possible to avoid automagic dependencies" +c1cb1933 disable NEON for arm64 platform +73a304e9 Make it possible to avoid automagic dependencies +4d493f8d MIPS: MIPS32r1: Decoder bit reader function optimized. PATCH [1/6] +c741183c make WebPCleanupTransparentArea work with argb picture +5da18552 add a decoding option to flip image vertically +00c3c4e1 Merge "add man/vwebp.1" +2c6bb428 add man/vwebp.1 +ea59a8e9 Merge "Merge tag 'v0.4.0'" +7574bed4 fix comments related to array sizes +0b5a90fd dwebp.1: fix option formatting +effcb0fd Merge tag 'v0.4.0' +7c76255d autoconf: update ax_pthread.m4 +fff2a11b make -short work with -print_ssim, -print_psnr, etc. +68e7901d update ChangeLog (tag: v0.4.0-rc1, tag: v0.4.0, origin/0.4.0) +256e4333 update NEWS description with new general features +29625340 Merge "gif2webp: don't use C99 %zu" into 0.4.0 +3b9f9dd0 gif2webp: don't use C99 %zu +b5b2e3c7 cwebp: fix metadata output w/lossy+alpha +ad26df1a makefile.unix: clean up libgif2webp_util.a +c3b45570 update Changelog +ca841121 Merge "bump version to 0.4.0" into 0.4.0 +8c524db8 bump version to 0.4.0 +eec2398c update AUTHORS & .mailmap +b9bbf6a1 update NEWS for 0.4.0 +c72e0811 Merge "dec/webp.c: don't wait for data before reporting w/h" +5ad65314 dec/frame.c: fix formatting +f7fc4bc8 dec/webp.c: don't wait for data before reporting w/h +66a32af5 Merge "NEON speed up" +26d842eb NEON speed up +f307f98b Merge "webpmux: let -- stop parameter parsing" +fe051da7 Merge "README: add a section on gif2webp" +6fd2bd62 Merge "manpage pedantry" +4af19007 README: add a section on gif2webp +6f36ade9 manpage pedantry +f9016cb9 README: update dwebp options +b4fa0a47 webpmux: let -- stop parameter parsing +a9a20acf gif2webp: Add a multi-threaded encode option +495bef41 fix bug in TrellisQuantize +605a7127 simplify __cplusplus ifdef +33109f99 Merge "drop: ifdef __cplusplus checks from C files" +7f9de0b9 Merge changes I994a5587,I8467bb71,I13b50688,I1e2c9c7b +5459030b gif2webp: let -- stop parameter parsing +a4b0aa06 vwebp: let -- stop parameter parsing +98af68fe cwebp: let -- stop parameter parsing +a33831e2 dwebp: let -- stop parameter parsing +36301249 add some checks on error paths +ce4c7139 Merge "autoconf: add --disable-wic" +5227d991 drop: ifdef __cplusplus checks from C files +f6453559 dwebp.1: fix typo +f91034f2 Merge "cwebp: print metadata stats when no output file is given" +d4934553 gif2webp: Backward compatibility for giflib version <= 4.1.3 +4c617d32 gif2webp: Disable output of ICC profile by default +73b731fb introduce a special quantization function for WHT +41c0cc4b Make Forward WHT transform use 32bit fixed-point calculation +a3359f5d Only compute quantization params once +70490437 cwebp: print metadata stats when no output file is given +d513bb62 * fix off-by-one zthresh calculation * remove the sharpening for non luma-AC coeffs * adjust the bias a little bit to compensate for this +ad9dec0c Merge "cosmetics: dwebp: fix local function name format" +f737f037 Merge "dwebp: remove a dead store" +3c3a70da Merge "makefile.unix: install binaries in $(DESTDIR)/bin/" +150b655f Merge "Android.mk: add some release compile flags" +dbebd33b cosmetics: dwebp: fix local function name format +27749951 dwebp: remove a dead store +a01e04fe autoconf: add --disable-wic +5009b227 makefile.unix: install binaries in $(DESTDIR)/bin/ +bab30fca Merge "fix -print_psnr / ssim options" +ebef7fb3 fix -print_psnr / ssim options +cb637855 Merge "fix bug due to overzealous check in WebPPictureYUVAToARGB()" +8189885b Merge "EstimateBestFilter: use an int to iterate WEBP_FILTER_TYPE" +4ad7d335 Android.mk: add some release compile flags +c12e2369 cosmetics: fix a few typos +6f104034 fix bug due to overzealous check in WebPPictureYUVAToARGB() +3f6c35c6 EstimateBestFilter: use an int to iterate WEBP_FILTER_TYPE +cc55790e Merge changes I8bb7a4dc,I2c180051,I021a014f,I8a224a62 +c536afb5 Merge "cosmetics: fix some typos" +cbdd3e6e add a -dither dithering option to the decoder +e8124012 Updated iosbuild.sh for XCode 5.x +4931c329 cosmetics: fix some typos +05aacf77 mux: add some missing casts +617d9348 enc/vp8l: add a missing cast +46db2865 idec: add some missing casts +b524e336 ErrorStatusLossless: correct return type +cb261f79 fix a descaling bug for vertical/horizontal U/V interpolation +bcb3955c Merge changes I48968468,I181bc736 +73f52133 gif2webp: Add a mixed compression mode +6198715e demux: split chunk parsing from ParseVP8X +d2e3f4e6 demux: add a tail pointer for chunks +87cffcc3 demux: cosmetics: s/has_frames/is_animation/ +e18e6677 demux: strictly enforce the animation flag +c4f39f4a demux: cosmetics: remove a useless break +61cb884d demux: (non-exp) fail if the fragmented flag is set +ff379db3 few % speedup of lossless encoding +df3649a2 remove all disabled code related to P-frames +6d0cb3de Merge "gif2webp: kmin = 0 should suppress key-frame addition." +36555983 gif2webp: kmin = 0 should suppress key-frame addition. +7708e609 Merge "detect flatness in blocks and favor DC prediction" +06b1503e Merge "add comment about the kLevelsFromDelta[][] LUT generation" +5935259c add comment about the kLevelsFromDelta[][] LUT generation +e3312ea6 detect flatness in blocks and favor DC prediction +ebc9b1ee Merge "VPLBitReader bugfix: Catch error if bit_pos > LBITS too." +96ad0e0a VPLBitReader bugfix: Catch error if bit_pos > LBITS too. +a014e9c9 tune quantization biases toward higher precision +1e898619 add helpful PrintBlockInfo() function +596a6d73 make use of 'extern' consistent in function declarations +c8d48c6e Merge "extract random utils to their own file util/random.[ch]" +98aa33cf extract random utils to their own file util/random.[ch] +432a723e Merge "swig: add basic go bindings" +fab618b5 Merge "rename libwebp.i -> libwebp.swig" +e4e7fcd6 swig: add basic go bindings +d3408720 Merge "fast auto-determined filtering strength" +f8bfd5cd fast auto-determined filtering strength +ac0bf951 small clean-up in ExpandMatrix() +1939607e rename libwebp.i -> libwebp.swig +43148b6c filtering: precompute ilimit and hev_threshold +18f992ec simplify f_inner calculation a little +241d11f1 add missing const +86c0031e add a 'format' field to WebPBitstreamFeatures +dde91fde Demux: Correct the extended format validation +5d6c5bd2 add entry for '-resize' option in cwebp's man +7c098d18 Use some gamma-curve range compression when computing U/V average +0b2b0504 Use deterministic random-dithering during RGB->YUV conversion +8a2fa099 Add a second multi-thread method +7d6f2da0 Merge "up to 20% faster multi-threaded decoding" +266f63ea Merge "libwebp.jar: build w/Java 1.6 for Android compat" +0532149c up to 20% faster multi-threaded decoding +38efdc2e Simplify the gif2webp tool: move the optimization details to util +de899516 libwebp.jar: build w/Java 1.6 for Android compat +cb221552 Decode a full row of bitstream before reconstructing +dca8a4d3 Merge "NEON/simple loopfilter: avoid q4-q7 registers" +9e84d901 Merge "NEON/TransformWHT: avoid q4-q7 registers" +fc10249b NEON/simple loopfilter: avoid q4-q7 registers +2f09d63e NEON/TransformWHT: avoid q4-q7 registers +77585a2b Merge "use a macrofunc for setting NzCoeffs bits" +d155507c Merge "use HINT_GRAPH as image_hint for gif source" +9c561646 Merge "only print GIF_DISPOSE_WARNING once" +05879865 use HINT_GRAPH as image_hint for gif source +0b28d7ab use a macrofunc for setting NzCoeffs bits +f9bbc2a0 Special-case sparse transform +00125196 gif2webp: detect and flatten uniformly similar blocks +0deaf0fa only print GIF_DISPOSE_WARNING once +6a8c0eb7 Merge "small optimization in segment-smoothing loop" +f7146bc1 small optimization in segment-smoothing loop +5a7533ce small gif2webp fix +4df0c89e Merge changes Ic697660c,I27285521 +5b2e6bd3 Android.mk: add a dwebp target +f910a84e Android.mk: update build flags +63f9aba4 special-case WHT transform when there's only DC +80911aef Merge "7-8% faster decoding by rewriting GetCoeffs()" +606c4304 gif2webp: Improved compression for lossy animated WebP +fb887f7f gif2webp: Different kmin/kmax defaults for lossy and lossless +2a981366 7-8% faster decoding by rewriting GetCoeffs() +92d47e4c improve VP8L signature detection by checking the version bits too +5cd43e43 Add -incremental option to dwebp +54b8e3f6 webpmux: DisplayInfo(): remove unnecessary error checks. +40ae3520 fix memleak in WebPIDelete() +d9662658 mux.h doc: WebPMuxGetFrame() can return WEBP_MUX_MEMORY_ERROR too. +0e6747f8 webpmux -info: display dimensions and has_alpha per frame +d78a82c4 Sanity check for underflow +8498f4bf Merge "remove -Wshadow warnings" +e89c6fc8 Avoid a potential memleak +3ebe1757 Merge "break down the proba 4D-array into some handy structs" +6a44550a break down the proba 4D-array into some handy structs +2f5e8934 remove -Wshadow warnings +bf3a29b3 Merge "add proper WEBP_HAVE_GIF and WEBP_HAVE_GL flags" +2b0a7593 Merge "fix some warnings from static analysis" +22dd07ce mux.h: Some doc corrections +79ff0346 add proper WEBP_HAVE_GIF and WEBP_HAVE_GL flags +d51f45f0 fix some warnings from static analysis +d134307b fix conversion warning on MSVC +d538cea8 gif2webp: Support a 'min' and 'max' key frame interval +80b54e1c allow search with token buffer loop and fix PARTITION0 problem +b7d4e042 add VP8EstimateTokenSize() +10fddf53 enc/quant.c: silence a warning +399cd456 Merge "fix compile error on ARM/gcc" +9f24519e encoder: misc rate-related fixes +c663bb21 Merge "simplify VP8IteratorSaveBoundary() arg passing" +fa46b312 Demux.h: Correct a method name reference +f8398c9d fix compile error on ARM/gcc +f691f0e4 simplify VP8IteratorSaveBoundary() arg passing +42542be8 up to 6% faster encoding with clang compiler +93402f02 multi-threaded segment analysis +7e2d6595 Merge "remove the PACK() bit-packing tricks" +c13fecf9 remove the PACK() bit-packing tricks +2fd091c9 Merge "use NULL for lf_stats_ testing, not bool" +b11c9d62 dwebp: use default dct_method +4bb8465f Merge "(de)mux.h: wrap pseudo-code in /* */" +cfb56b17 make -pass option work with token buffers +5416aab4 (de)mux.h: wrap pseudo-code in /* */ +35dba337 use NULL for lf_stats_ testing, not bool +733a7faa enc->Iterator memory cleanup +e81fac86 Add support for "no blend" in webpmux binary +3b80bc48 gif2webp: Separate out each step into a method +bef7e9cc Add doc precision about demux object keeping pointers to data. +61405a14 dwebp: enable stdout output with WIC +6eabb886 Merge "Animated WebP: add "do no blend" option to spec" +be20decb fix compilation for BITS 24 +e58cc137 Merge "dwebp: s/unsigned char/uint8_t/" +72501d43 dwebp: s/unsigned char/uint8_t/ +2c9633e8 Merge "gif2webp: Insert independent frames at regular intervals." +f0d6a14b gif2webp: Insert independent frames at regular intervals. +b25a6fbf yuv.h: fix indent +ede3602e Merge "cosmetics: fix indent" +3a65122a dwebp: fix stdout related output +388a7249 cosmetics: fix indent +4c7322c8 Merge "dsp: msvc compatibility" +d50c7e32 Merge "5-7% faster SSE2 versions of YUV->RGB conversion functions" +b8ab7847 Merge "simplify upsampler calls: only allow 'bottom' to be NULL" +df6cebfa 5-7% faster SSE2 versions of YUV->RGB conversion functions +ad6ac32d simplify upsampler calls: only allow 'bottom' to be NULL +a5e8afaf output to stdout if file name is "-" +f358450f dsp: msvc compatibility +43a7c8eb Merge "cosmetics" +4c5f19c1 Merge "bit_reader.h: cosmetics" +f72fab70 cosmetics +14dd5e78 fix const-ness +b20aec49 Merge "Support for 'do not blend' option in vwebp" +dcf65222 Support for 'do not blend' option in vwebp +d5bad033 Animated WebP: add "do no blend" option to spec +a2f5f73d Merge "Support for "Do not blend" in mux and demux libraries" +e081f2f3 Pack code & extra_bits to Struct (VP8LPrefixCode). +6284854b Support for "Do not blend" in mux and demux libraries +f486aaa9 Merge "slightly faster ParseIntraMode" +d1718632 slightly faster ParseIntraMode +3ceca8ad bit_reader.h: cosmetics +69257f70 Create LUT for PrefixEncode. +988b7084 add WebPWorkerExecute() for convenient bypass +06e24987 Merge "VP8EncIterator clean-up" +de4d4ad5 VP8EncIterator clean-up +7bbe9529 Merge "cosmetics: thread.c: drop a redundant comment" +da411485 cosmetics: thread.c: drop a redundant comment +feb4b6e6 thread.h: #ifdef when checking WEBP_USE_THREAD +8924a3a7 thread.c: drop WebPWorker prefix from static funcs +1aed8f2a Merge "fix indent" +4038ed15 fix indent +1693fd9b Demux: A new state WEBP_DEMUX_PARSE_ERROR +8dcae8b3 fix rescaling-with-alpha inaccuracy +11249abf Merge changes I9b4dc36c,I4e0eef4d +52508a1f Mux: support parsing unknown chunks within a frame/fragment. +05db0572 WebPMuxSetChunk: remove unused variable +8ba1bf61 Stricter check for presence of alpha when writing lossless images +a03c3516 Demux: WebPIterator now also denotes if the frame has alpha. +6df743a3 Decoder: handle fragments case correctly too. +faa4b07e Support for unknown chunks in mux library +7d60bbc6 Speed up HashChainFindCopy function. +66740140 Speedup Alpha plane encoding. +b7346a1e 0.1 % speedup to decoding +c606182e webp-container-spec: Tighten language added by last +a34a5029 pngdec: output error messages from libpng +e84c625d Merge "Detect canvas and image size mismatch in decoder." +f626fe2e Detect canvas and image size mismatch in decoder. +f5fbdee0 demux: stricter image bounds check +30c8158a add extra assert in Huffman decode code +8967b9f3 SSE2 for lossless decoding (critical) functions. +699d80ea Jump-lookup for Huffman coding +c34307ab fix some VS9 warnings about type conversion +eeada35c pngdec: add missing include +54b65108 gif2webp: If aligning to even offsets, extra pixels should be transparent +0bcf5ce3 Merge "remove a malloc() in case we're using only FILTER_NONE for alpha" +2c07143b remove a malloc() in case we're using only FILTER_NONE for alpha +a4d5f59d Faster lossless decoding +fd53bb75 Merge "alternate LUT-base reverse-bits code" +d1c166ef Merge "Container spec: a clarification on background color." +fdb91779 Rename a method +5e967532 Container spec: a clarification on background color. +30e77d0f Merge branch '0.3.0' +1b631e29 alternate LUT-base reverse-bits code +24cc307a ~20% faster lossless decoding +313d853f Speedup for decoding lossless WebP photographs: +24ee098a change the bytes_per_pixels_ field into more evocative use_8b_decode +2a04b034 update ChangeLog (tag: v0.3.1-rc2, tag: v0.3.1) +7288950b Regression fix for alpha channels using color cache: +2e377b53 wicdec: silence a format warning +ad9e42a6 muxedit: silence some uninitialized warnings +3307c163 Don't set alpha-channel to 0xff for alpha->green uplift +5130770c Merge "wicdec: silence a format warning" +a37eff47 Regression fix for alpha channels using color cache: +241cf99b Merge "muxedit: silence some uninitialized warnings" +c8f9c84d Regression fix for alpha unfiltering: +14cd5c6c muxedit: silence some uninitialized warnings +a368db81 dec/vp8l: quiet vs9 x64 type conversion warning +ffae9f31 wicdec: silence a format warning +8cf0701e Alpha encoding: never filter in case of NO_COMPRESSION +825e73b1 update ChangeLog (tag: v0.3.1-rc1) +abf6f691 update NEWS +5a92c1a5 bump version to 0.3.1 +86daf77c store top Y/U/V samples in packed fashion +67bc353e Revert "add WebPBlendAlpha() function to blend colors against background" +068db59e Intertwined decoding of alpha and RGB +38cc0114 Simplify forward-WHT + SSE2 version +3fa595a5 Support decoding upto given row in DECODE_DATA_FUNC +520f005f DequantizeLevels(): Add 'row' and 'num_rows' args +47374b82 Alpha unfilter for given set of rows +f32097e0 probe input file and quick-check for WebP format. +a2aed1d0 configure: improve gl/glut library test +c7e89cbb update copyright text +a00380d2 configure: remove use of AS_VAR_APPEND +a94a88dd fix EXIF parsing in PNG +a71e5d84 add doc precision for WebPPictureCopy() and WebPPictureView() +8287012e remove datatype qualifier for vmnv +e1908430 fix a memory leak in gif2webp +0b18b9ee fix two minor memory leaks in webpmux +db5095d5 remove some cruft from swig/libwebp.jar +850e956f README: update swig notes +bddd9b0a swig/python: add minimal documentation +d573a8d5 swig: add python encode support +6b931875 swig/java: reduce wrapper function code duplication +6fe536f4 swig/java: rework uint8_t typemap +a2ea4643 Fix the bug in ApplyPalette. +7bb28d2a webp/lossless: fix big endian BGRA output +f036d4bf Speed up ApplyPalette for ARGB pixels. +8112c8cf remove some warnings: +cc128e0b Further reduce memory to decode lossy+alpha images +07db70d2 fix for big-endian +eda8a7de gif2webp: Fix signed/unsigned comparison mismatch +31f346fe Makefile.vc: fix libwebpdemux dll variable typo +6c76d28e swig: add python (decode) support +b4f5bb6c swig: cosmetics +498d4dd6 WebP-Lossless encoding improvements. +26e72442 swig: ifdef some Java specific code +8ecec686 configure: add warning related flags +e676b043 configure: add GLUT detection; build vwebp +b0ffc437 Alpha decoding: significantly reduce memory usage +20aa7a8d configure: add --enable-everything +b8307cc0 configure.ac: add some helper macros +980e7ae9 Remove the gcc compilation comments +7f25ff99 gif2webp: Fix ICC and XMP support +d8e53211 Add missing name to AUTHORS +11edf5e2 Demux: Fix a potential memleak +c7b92184 don't forward declare enums +7a650c6a prevent signed int overflow in left shift ops +31bea324 add precision about dynamic output reallocation with IDecoder +c22877f7 Add incremental support for extended format files +5051245f Makefile.vc: have 'all' target build everything +8191deca Makefile.vc: flags cleanup +b9d74735 Makefile.vc: drop /FD flag +5568dbcf update gitignore +f4c7b654 WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded. +1fb04bec pngdec: Avoid a double-free. +dcbb1ca5 add WebPBlendAlpha() function to blend colors against background +bc9f5fbe configure.ac: add AM_PROG_AR for automake >= 1.12 +bf867bf2 Tuned cross_color parameter (step) for lower qual +90e2ec5a Merge "probe input file and quick-check for WebP format." +7180d7ff Merge "update copyright text" +830f72b7 probe input file and quick-check for WebP format. +2ccf58d6 configure: improve gl/glut library test +d640614d update copyright text +c2113ad4 Merge "configure: remove use of AS_VAR_APPEND" +9326a56f configure: remove use of AS_VAR_APPEND +ea63d619 fix a type warning on VS9 x86 +bec11092 fix EXIF parsing in PNG +b6e65f3d Merge "fix warnings for vs9 x64" +438946dc fix warnings for vs9 x64 +f4710e3b collect macroblock reconstruction data in VP8MBData struct +23d28e21 add doc precision for WebPPictureCopy() and WebPPictureView() +518f2cd7 cosmetics: gif2webp: fix indent +af358e68 Merge "remove datatype qualifier for vmnv" +3fe91635 remove datatype qualifier for vmnv +764fdffa fix a memory leak in gif2webp +3e59a74d fix two minor memory leaks in webpmux +47b9862f Merge "README: update swig notes" +325d15ff remove some cruft from swig/libwebp.jar +4a7627c2 README: update swig notes +5da81e33 Merge "swig/python: add minimal documentation" +f39e08f2 Merge "swig: add python encode support" +6ca4a3e3 Merge "swig/java: reduce wrapper function code duplication" +8f8702b0 Merge "swig/java: rework uint8_t typemap" +91413be2 reduce memory for VP8MB and remove bitfields use +7413394e Fix the memory leak in ApplyFilters. +2053c2cf simplify the alpha-filter testing loop +825b64db swig/python: add minimal documentation +14677e11 swig: add python encode support +a5c297c8 swig/java: reduce wrapper function code duplication +ad4a367d swig/java: rework uint8_t typemap +0d25876b use uint8_t for inv_palette[] +afa3450c Fix the bug in ApplyPalette. +2d6ac422 Merge "webp/lossless: fix big endian BGRA output" +2ca83968 webp/lossless: fix big endian BGRA output +742110cc Speed up ApplyPalette for ARGB pixels. +2451e47d misc code cleanup +83db4043 Merge "swig: add python (decode) support" +eeeea8b5 Merge "swig: cosmetics" +d5f9b8f3 Merge "libwebp: fix vp8 encoder mem alloc offsetting" +d8edd835 libwebp: fix vp8 encoder mem alloc offsetting +8983b83e remove use of bit-fields in VP8FInfo +87a4fca2 remove some warnings: +ba8f74e2 Merge "fix for big-endian" +a65067fa Merge "Further reduce memory to decode lossy+alpha images" +64c84486 Further reduce memory to decode lossy+alpha images +332130b9 Mux: make a few methods static +44370617 fix for big-endian +5199eab5 Merge "add uncompressed TIFF output support" +a3aede97 add uncompressed TIFF output support +f975b67f Merge "gif2webp: Fix signed/unsigned comparison mismatch" +5fbc734b Merge "GetFeatures: Detect invalid VP8X/VP8/VP8L data" +d5060c87 Merge "mux.h: A comment fix + some consistency fixes" +352d0dee GetFeatures: Detect invalid VP8X/VP8/VP8L data +3ef79fef Cosmetic: "width * height" +043e1ae4 gif2webp: Fix signed/unsigned comparison mismatch +5818cff7 mux.h: A comment fix + some consistency fixes +1153f888 Merge "swig: ifdef some Java specific code" +3eeedae1 Makefile.vc: fix libwebpdemux dll variable typo +f980faf4 swig: add python (decode) support +7f5f42bb swig: cosmetics +8eae188a WebP-Lossless encoding improvements. +c7247c4c swig: ifdef some Java specific code +4cb234d5 Merge "Mux: make ValidateForSingleImage() method static" +ed6f5308 Merge "Add GetCanvasSize() method to mux" +1d530c9a Mux: make ValidateForSingleImage() method static +bba4c2b2 configure: add warning related flags +fffefd18 Add GetCanvasSize() method to mux +732da8d0 Merge "configure: add GLUT detection; build vwebp" +0e513f7a configure: add GLUT detection; build vwebp +55d1c150 Merge "Alpha decoding: significantly reduce memory usage" +13d99fb5 Merge "configure: add --enable-everything" +2bf698fe Merge "configure.ac: add some helper macros" +edccd194 Alpha decoding: significantly reduce memory usage +3cafcc9a configure: add --enable-everything +4ef14477 configure.ac: add some helper macros +a4e1cdbb Remove the gcc compilation comments +6393fe4b Cosmetic fixes +9c4ce971 Simplify forward-WHT + SSE2 version +878b9da5 fix missed optim +00046171 VP8GetInfo(): Check for zero width or height. +9bf31293 align VP8Encoder::nz_ allocation +5da165cf fix CheckMode() signature +0ece07dc Merge "explicitly pad bitfields to 32-bits" +9dbc9d19 explicitly pad bitfields to 32-bits +5369a80f Merge "prevent signed int overflow in left shift ops" +70e39712 Merge "cosmetics: remove unnecessary ';'s" +d3136ce2 Merge "don't forward declare enums" +b26e5ad5 gif2webp: Fix ICC and XMP support +46089b20 Add missing name to AUTHORS +94328d64 Demux: Fix a potential memleak +96e948d7 don't forward declare enums +f4f90880 prevent signed int overflow in left shift ops +0261545e cosmetics: remove unnecessary ';'s +7ebdf110 Merge "Fix few missing comparisons to NULL" +1579989e Fix few missing comparisons to NULL +ea1b21cf Cleaned up VP8GetHeaders() so that it parses only frame header +b66caee4 dwebp: add support for BMP output +ff885bfe add precision about dynamic output reallocation with IDecoder +79241d5a Merge "Makefile.vc: have 'all' target build everything" +ac1c729b Merge "Makefile.vc: flags cleanup" +118a055c Merge "Makefile.vc: drop /FD flag" +ecad0109 Merge "update gitignore" +a681b4f4 Rename PRE_VP8 state to WEBP_HEADER +ead4d478 Add incremental support for extended format files +69d0f926 Makefile.vc: have 'all' target build everything +52967498 Makefile.vc: flags cleanup +c61baf0c Makefile.vc: drop /FD flag +3a15125d update gitignore +5167ca47 Merge "WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded." +67708d67 WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded. +b68912af pngdec: Avoid a double-free. +82abbe12 Merge "configure.ac: add AM_PROG_AR for automake >= 1.12" +e7d9548c add WebPBlendAlpha() function to blend colors against background +ed4dc717 configure.ac: add AM_PROG_AR for automake >= 1.12 +df4a406d Merge branch '0.3.0' +1e0d4b8c Update ChangeLog (tag: v0.3.0-rc7, tag: v0.3.0) +d52b405d Cosmetic fixes +6cb4a618 misc style fix +68111ab0 add missing YUVA->ARGB automatic conversion in WebPEncode() +e9a7990b Cosmetic fixes +403bfe82 Container spec: Clarify frame disposal +2aaa423b Merge "add missing YUVA->ARGB automatic conversion in WebPEncode()" +07d87bda add missing YUVA->ARGB automatic conversion in WebPEncode() +142c4629 misc style fix +3e7a13a0 Merge "Container spec: clarify the background color field" into 0.3.0 +14af7745 container doc: add a note about the 'ANMF' payload +cc635efa Container spec: clarify the background color field +e3e33949 container doc: move RIFF description to own section +4299f398 libwebp/mux: fix double free +33f9a692 Merge "demux: keep a frame tail pointer; used in AddFrame" into 0.3.0 +a2a7b959 use WebPDataCopy() instead of re-coding it. +6f18f12f demux: keep a frame tail pointer; used in AddFrame +e5af49e9 add doc precision about WebPParseHeaders() return codes +db46daab Merge "Makefile.vc: fix dynamic builds" into 0.3.0 +53c77afc Merge "gif2webp: Bgcolor fix for a special case" into 0.3.0 +a5ebd143 gif2webp: Bgcolor fix for a special case +6378f238 Merge "vwebp/animation: fix background dispose" into 0.3.0 +3c8eb9a8 fix bad saturation order in QuantizeBlock +04c7a2ec vwebp/animation: fix background dispose +81a50695 Makefile.vc: fix dynamic builds +5f25c396 update ChangeLog (tag: v0.3.0-rc6) +14d42af2 examples: don't use C99 %zu +5ccf1fe5 update ChangeLog +2560c243 update NEWS +f43bafc3 Merge changes Iecccb09c,If5ee9fd2,I3e181ce4 into 0.3.0 +a788644f dwebp: warn when decoding animated webp's +302efcdb Decode: return more meaningful error for animation +ad452735 WebPBitstreamFeatures: add has_animation field +783dfa49 disable FRGM decoding for good in libwebpmux +4b956be0 Update ChangeLog +ad8b86d7 update NEWS +3e084f63 Merge "demux cosmetics: comments/rename internal function" into 0.3.0 +d3f8c621 Merge "move WebPFeatureFlags declaration" into 0.3.0 +7386fe50 Merge "libwebp{demux,mux}: install mux_types.h" into 0.3.0 +d6cd4e90 Merge "bump decode abi" into 0.3.0 +17f8da5c bump decode abi +97684ae2 Merge "add doc precision about WebPDemuxPartial()" into 0.3.0 +f933fd2a move WebPFeatureFlags declaration +289bc47b libwebp{demux,mux}: install mux_types.h +224e8d46 add doc precision about WebPDemuxPartial() +4c18e80c demux cosmetics: comments/rename internal function +7cfd1bf1 update AUTHORS +401f7b85 Merge "speed-up lossless (~3%) with ad-hoc histogram cost evaluation" into 0.3.0 +1fc8ffca Merge "makefile.unix: dist related changes" into 0.3.0 +8a89c6ed Merge changes I466c377f,Ib761ebd3,I694857fc into 0.3.0 +f4ffb2d5 speed-up lossless (~3%) with ad-hoc histogram cost evaluation +723847d5 gif2webp: only write error messages to stderr +701b9e2a makefile.unix: dist related changes +bb85b437 Merge "update NEWS" into 0.3.0 +59423a24 gif2webp: fix crash on open failure with libgif5 +9acb17de gif2webp: silence a unused param warning +7d9fdc23 Merge "README updates" into 0.3.0 +5621934e Merge "build: fix install race on shared headers" into 0.3.0 +70809d89 Merge "bump version to 0.3.0" into 0.3.0 +d851cd1d demux: make the parse a bit more strict +28bb4107 update NEWS +cef93882 bump version to 0.3.0 +9048494d build: fix install race on shared headers +1e67e8ef README updates +42b611a4 Merge "configure: drop experimental from mux/demux" into 0.3.0 +096a8e32 Merge "vwebp: add color profile support" into 0.3.0 +ddfee5dc vwebp: add color profile support +0d6927d3 Merge "Mark fragment options as experimental in webpmux" into 0.3.0 +5dbd4030 Mark fragment options as experimental in webpmux +a0a6648c configure: drop experimental from mux/demux +ee65bad8 Merge "add support for BITS > 32" into 0.3.0 +744930db add support for BITS > 32 +7dd288f0 cwebp: fix build +19a8dd01 Merge "Makefile.vc: add vwebp.exe target" into 0.3.0 +50eeddad Merge "examples: normalize icc related program arguments" into 0.3.0 +757f637f Merge "Makefile.vc: add libwebpdecoder target" into 0.3.0 +b65c4b7c Makefile.vc: add libwebpdecoder target +f8db7b4a Merge "vwebp: replace doubles w/floats where appropriate" into 0.3.0 +d99aa56f Makefile.vc: add vwebp.exe target +013023e7 vwebp: replace doubles w/floats where appropriate +9b3db894 README.mux: add version reference +7b6a26cf Merge "cwebp: output metadata statistics" into 0.3.0 +d8dc72a0 examples: normalize icc related program arguments +7bfc9056 Merge "make alpha unfilter work in-place" into 0.3.0 +0037b2d2 Merge "add LUT-free reference code for YUV->RGB conversion." into 0.3.0 +166bf744 Merge "demux: disable fragment parsing" into 0.3.0 +126974b4 add LUT-free reference code for YUV->RGB conversion. +0aef3ebd make alpha unfilter work in-place +14ef5005 Merge "Remove 'status: experimental' from container spec" into 0.3.0 +d40c98e1 Merge "webpmux binary: tiny style fix" into 0.3.0 +0bc42689 cwebp: output metadata statistics +bc039803 Merge "autoconf: normalize experimental define" into 0.3.0 +d1e21b13 Remove 'status: experimental' from container spec +7681bb96 webpmux binary: tiny style fix +a3dd3d0f avoid installing example_util.h +252320e2 demux: disable fragment parsing +537bde05 autoconf: normalize experimental define +5e338e0b Merge changes I33e8a613,I8e8a7b44 into 0.3.0 +d9d0ea1b Merge changes If21e3ec7,I991fc30b into 0.3.0 +627f5ca6 automake: add reference to libwebp for mux/demux +eef73d07 don't consolidate proba stats too often +05ec4cc2 libwebp{,decoder}.pc: add pthread flags +1bfcf5bf add libwebpmux.pc +26ca843d add libwebpdemux.pc +69e25906 Merge "Tune Lossless compression for lower qualities." +0478b5d2 Tune Lossless compression for lower qualities. +39f7586f add a mention of parallel alpha encoding in the NEWS +5a21d967 Merge "1.5x-2x faster encoding for method 3 and up" +9bfbdd14 1.5x-2x faster encoding for method 3 and up +27dc741b Correct frame options order in README.mux +be2fd173 Mux: fix a scenario with bad ANMF/FRGM size +19eb012c Merge "Demux: Add option to get frame count using GetI()" +7368b8cb Merge "WebPGetFeatures() out of if condition for clarity." +f604c9a4 Merge "fix windows build" +153f94e8 fix windows build +847b4924 Merge "vwebp: use magenta for 'i'nfo display" +25ea46bd Merge "vwebp: add keyboard shortcuts to help output" +bea7ccaf vwebp: use magenta for 'i'nfo display +8fab161a webpmux: correct -frame param order in help output +03cc23d6 vwebp: add keyboard shortcuts to help output +068eba8d Demux: Add option to get frame count using GetI() +988b8f56 WebPGetFeatures() out of if condition for clarity. +6933d910 Merge "gif2webp: Be lenient about background color index." +4d0f7c55 Merge "WebPGetFeatures() behavior change:" +fdeeb01d gif2webp: Be lenient about background color index. +ad250320 Merge "multi-threaded alpha encoding for lossy" +4e32d3e1 Merge "fix compilation of token.c" +f817930a multi-threaded alpha encoding for lossy +88050351 fix compilation of token.c +fc816219 code using the actual values for num_parts_, not the ones from config +72655350 Merge "move the config check from .c to .h" +dd9e76f7 move the config check from .c to .h +956b217a WebPGetFeatures() behavior change: +df02e4ce WebPDemuxGetI behavior change: +633c004d Merge "rebalance method tools (-m) for methods [0..4]" +58ca6f65 rebalance method tools (-m) for methods [0..4] +7648c3cc Merge "describe rd-opt levels introduce VP8RDLevel enum" +67fb1003 Merge "autoconf: enable silent-rules by default" +a5042a32 GetVersion() methods for mux and demux +5189957e describe rd-opt levels introduce VP8RDLevel enum +4e094ace autoconf: enable silent-rules by default +b7eaa85d inline VP8LFastLog2() and VP8LFastSLog2 for small values +5cf7792e split quant_levels.c into decoder and encoder version +e5d3ffe2 Merge "Update code example in README.mux" +ac5a9156 Update code example in README.mux +38a91e99 Add example code snippet for demux API +5f557f3c README.mux: add info about Demux API and vwebp +c0ba0903 backward_references: avoid signed integer overflow +943386db disable SSE2 for now +9479fb7d lossless encoding speedup +ec2030a8 merge two lines together +b67956c0 Merge "Remove ReadOneBit() and ReadSymbolUnsafe()" +1667bded Remove ReadOneBit() and ReadSymbolUnsafe() +3151669b wicdec + dwebp cosmetics: normalize formatting +92668da6 change default filtering parameters: * type is now 'strong' * strength is now '60' +b7490f85 introduce WEBP_REFERENCE_IMPLEMENTATION compile option +33838857 faster decoding (3%-6%) +5c3e381b Merge "add a -jpeg_like option" +c2311046 remove unused declaration of VP8Zigzag +36152957 Merge "wicdec: add alpha support for paletted formats" +c9f16490 wicdec: add alpha support for paletted formats +1262f81e Merge "wicdec: silence some warnings" +e7ea61eb wicdec: silence some warnings +23c0f354 fix missing intptr_t->int cast for MSVC +e895059a add a -jpeg_like option +1f803f64 Merge "Tune alpha quality mapping to more reasonable values." +1267d498 Tune alpha quality mapping to more reasonable values. +043076e2 Merge "speed-up lossless in BackwardTrace" +f3a44dcd remove one malloc from TraceBackwards() +0fc1a3a0 speed-up lossless in BackwardTrace +7c732e59 cwebp: centralize WebPCleanupTransparentArea() +7381254e Merge "wicdec: add ICC profile extraction" +e83ff7de wicdec: add ICC profile extraction +146c6e3b Merge "cosmetics: pngdec: normalize default label location" +a8f549d7 Merge "manpages: italicize option parameters" +e118db83 Merge "encode.h: note the need to free() WebPMemoryWriter" +1dfee6db cosmetics: pngdec: normalize default label location +14c38200 manpages: italicize option parameters +7defbfad encode.h: note the need to free() WebPMemoryWriter +88d382a0 cwebp: cleanup after memory_writer +12d6cecf fix extra space in dwebp.1 man +b01681a9 Fix for demuxer frame iteration: +56c12aa6 Demuxer creation fix: +66c810bc add a -yuv option to dwebp (very similar to -pgm) +841a3ba5 Merge "Remove -Wshadow warnings." +8fd02527 Merge "upsampling_neon.c: fix build" +6efed268 Remove -Wshadow warnings. +60904aa6 Merge "allow WebPINewRGB/YUVA to be passed a NULL output buffer." +b7adf376 allow WebPINewRGB/YUVA to be passed a NULL output buffer. +27f8f742 upsampling_neon.c: fix build +06b9cdf1 gitignore: add IOS related directories +f112221e Merge "Fix more comments for iobuild.sh" +fe4d25dd Fix more comments for iobuild.sh +1de3e252 Merge "NEON optimised yuv to rgb conversion" +090b708a NEON optimised yuv to rgb conversion +daa06476 Merge "Add ios build script for building iOS library." +79fe39e2 Add ios build script for building iOS library. +126c035f remove some more -Wshadow warnings +522e9d61 Merge "cwebp: enable '-metadata'" +76ec5fa1 cwebp: enable '-metadata' +aeb91a9d Merge "cosmetics: break a few long lines" +be7c96b0 cosmetics: break a few long lines +cff8ddb6 Merge "add libwebpdecoder.pc" +93148ab8 Merge "libwebp.pc.in: detab" +6477f955 Merge "Makefile.vc: normalize path separator" +bed1ed7c add libwebpdecoder.pc +46168b2d libwebp.pc.in: detab +a941a346 Fixed few nits in the build files. +dd7a49b2 Makefile.vc: normalize path separator +9161be86 Merge "cwebp: extract WIC decoding to its own module" +08e7c58e Merge "Provide an option to build decoder library." +0aeba528 Provide an option to build decoder library. +757ebcb1 catch malloc(0)/calloc(0) with an assert +152ec3d2 Merge "handle malloc(0) and calloc(0) uniformly on all platforms" +a452a555 cwebp: extract WIC decoding to its own module +2b252a53 Merge "Provide option to swap bytes for 16 bit colormodes" +94a48b4b Provide option to swap bytes for 16 bit colormodes +42f8f934 handle malloc(0) and calloc(0) uniformly on all platforms +8b2152c5 Merge "add an extra assert to check memory bounds" +0d19fbff remove some -Wshadow warnings +cd22f655 add an extra assert to check memory bounds +8189feda Merge "Add details and reference about the YUV->RGB conversion" +1d2702b1 Merge "Formatting fixes in lossless bitstream spec" +8425aaee Formatting fixes in lossless bitstream spec +a556cb1a Add details and reference about the YUV->RGB conversion +d8f21e0b add link to SSIM description on Wikipedia +18e9167e Merge "WebP-lossless spec clarifications:" +98e25b9b Merge "cwebp: add -metadata option" +f01c2a53 WebP-lossless spec clarifications: +f4a97970 Merge "Disto4x4 and Disto16x16 in NEON" +47b7b0ba Disto4x4 and Disto16x16 in NEON +7eaee9f1 cwebp: add -metadata option +36c52c2c tiffdec: use toff_t for exif ifd offset +7c8111e4 Merge "cwebp/tiffdec: add TIFF metadata extraction" +e6409adc Remove redundant include from dsp/lossless code. +1ab5b3a7 Merge "configure: fix --with-gifincludedir" +03c749eb configure: fix --with-gifincludedir +8b650635 multiple libgif versions support for gif2webp +476e293f gif2webp: Use DGifOpenFileName() +b50f277b tiffdec: correct format string +2b9048e3 Merge "tiffdec: check error returns for width/height" +a1b5a9a3 Merge "cwebp/tiff: use the first image directory" +079423f5 tiffdec: check error returns for width/height +d62824af Merge "cwebp/jpegdec: add JPEG metadata extraction" +03afaca4 Merge "cwebp: add PNG metadata extraction" +2c724968 cwebp/jpegdec: add JPEG metadata extraction +dba64d91 cwebp: add PNG metadata extraction +1f075f89 Lossless spec corrections/rewording/clarifications +2914ecfd cwebp/tiffdec: add TIFF metadata extraction +d82a3e33 More corrections/clarifications in lossless spec: +bd002557 cwebp/tiff: use the first image directory +df7aa076 Merge "Cleanup around jpegdec" +0f57dcc3 decoding speed-up (~1%) +bcec339b Lossless bitstream clarification: +6bf20874 add examples/metadata.c +207f89c0 Merge "configure: add libwebpdemux status to summary" +1bd287a6 Cleanup around jpegdec +91455679 Merge "cosmetics: use '== 0' in size checks" +d6b88b76 cosmetics: use '== 0' in size checks +d3dace2f cosmetics: jpegdec +2f69af73 configure: add libwebpdemux status to summary +1c1c5646 cwebp: extract tiff decoding to its own module +6a871d66 cwebp: extract jpeg decoding to its own module +2ee228f9 cwebp: extract png decoding to its own module +4679db00 Merge "cwebp: add metadata framework" +63aba3ae cwebp: add metadata framework +931bd516 lossless bitstream: block size bits correction +e4fc4c1c lossless bitstream: block size bits correction +d65ec678 fix build, move token.c to src/enc/ +657f5c91 move token buffer to its own file (token.c) +c34a3758 introduce GetLargeValue() to slim-fast GetCoeffs(). +d5838cd5 faster non-transposing SSE2 4x4 FTransform +f76191f9 speed up GetResidualCost() +ba2aa0fd Add support for BITS=24 case +2e7f6e8e makefile.unix: Dependency on libraries +dca84219 Merge "Separate out mux and demux code and libraries:" +23782f95 Separate out mux and demux code and libraries: +bd56a01f configure: add summary output +90e5e319 dwebp manual: point to webpmux, gif2webp. +540790ca gif2webp.c: add a note about prerequisites +d1edf697 cwebp man page: meaning of '-q' for lossy/lossless +79efa1d0 Add man page for gif2webp utility +2243e40c Merge "gif2webp build support with autoconf tools" +c40efca1 gif2webp build support with autoconf tools +6523e2d4 WebP Container: +4da788da Merge "simplify the fwd transform" +42c3b550 simplify the fwd transform +41a6ced9 user GLfloat instead of float +b5426119 fix indentation +68f282f7 * handle offset in anim viewer 'vwebp' * fix gif2webp to handle disposal method and odd offset correctly +118cb312 Merge "add SSE2 version of Sum of Square error for 16x16, 16x8 and 8x8 case" +8a7c3cc8 Merge "Change the order of -frame argument to be more natural" +99e0a707 Merge "Simplify the texture evaluation Disto4x4()" +0f923c3f make the bundling work in a tmp buffer +e5c3b3f5 Simplify the texture evaluation Disto4x4() +48600084 Change the order of -frame argument to be more natural +35bfd4c0 add SSE2 version of Sum of Square error for 16x16, 16x8 and 8x8 case +a7305c2e Clarification for unknown chunks +4c4398e2 Refine WebP Container Spec wrt unknown chunks. +2ca642e0 Rectify WebPMuxGetFeatures: +7caab1d8 Some cosmetic/comment fixes. +60b2651a Merge "Write a GIF to WebP converter based on libgif." +c7127a4d Merge "Add NEON version of FTransformWHT" +11b27212 Write a GIF to WebP converter based on libgif. +e9a15a37 ExUtilWriteFile() to write memory segment to file +74356eb5 Add a simple cleanup step in mux assembly: +51bb1e5d mux.h: correct WebPDemuxSelectFragment() prototype +22a0fd9d Add NEON version of FTransformWHT +fa30c863 Update mux code to match the spec wrt animation +d9c5fbef by-pass Analysis pass in case segments=1 +d2ad4450 Merge changes Ibeccffc3,Id1585b16 +5c8be251 Merge "Chunk fourCCs for XMP/EXIF" +a00a3daf Use 'frgm' instead of 'tile' in webpmux parameters +81b8a741 Design change in ANMF and FRGM chunks: +f903cbab Chunk fourCCs for XMP/EXIF +812933d6 Tune performance of HistogramCombine +52ad1979 Animation specification in container spec +001b9302 Image fragment specification in container spec +391f9db9 Ordering of description of bits in container spec +d5735776 Metadata specification in container spec +1c4609b1 Merge commit 'v0.2.1' +0ca584cb Merge "Color profile specification in container spec" +e8b41ad1 add NEON asm version for WHT inverse transform +af6f0db2 Color profile specification in container spec +a61a824b Merge "Add NULL check in chunk APIs" +0e8b7eed fix WebPPictureView() unassigned strides +75e5f17e ARM/NEON: 30% encoding speed-up +02b43568 Add NULL check in chunk APIs +a0770727 mux struct naming +6c66dde8 Merge "Tune Lossless encoder" +ab5ea217 Tune Lossless encoder +74fefc8c Update ChangeLog (tag: v0.2.1, origin/0.2.0) +92f8059c Rename some chunks: +3bb4bbeb Merge "Mux API change:" +d0c79f05 Mux API change: +abc06044 Merge "update NEWS" into 0.2.0 +57cf313b update NEWS +25f585c4 bump version to 0.2.1 +fed7c048 libwebp: validate chunk size in ParseOptionalChunks +552cd9bc cwebp (windows): fix alpha image import on XP +b14fea99 autoconf/libwebp: enable dll builds for mingw +4a8fb272 [cd]webp: always output windows errors +d6621580 fix double to float conversion warning +72b96a69 cwebp: fix jpg encodes on XP +734f762a VP8LAllocateHistogramSet: fix overflow in size calculation +f9cb58fb GetHistoBits: fix integer overflow +b30add20 EncodeImageInternal: fix uninitialized free +3de58d77 fix the -g/O3 discrepancy for 32bit compile +77aa7d50 fix the BITS=8 case +e5970bda Make *InitSSE2() functions be empty on non-SSE2 platform +ef5cc47e make *InitSSE2() functions be empty on non-SSE2 platform +c4ea259d make VP8DspInitNEON() public +8344eadf Merge "libwebp: validate chunk size in ParseOptionalChunks" +4828bb93 Merge "cwebp (windows): fix alpha image import on XP" +30763333 libwebp: validate chunk size in ParseOptionalChunks +70481898 AccumulateLSIM: fix double -> float warnings +eda8ee4b cwebp (windows): fix alpha image import on XP +c6e98658 Merge "add EXPERIMENTAL code for YUV-JPEG colorspace" +f0360b4f add EXPERIMENTAL code for YUV-JPEG colorspace +f86e6abe add LSIM metric to WebPPictureDistortion() +c3aa215a Speed up HistogramCombine for lower qualities. +1765cb1c Merge "autoconf/libwebp: enable dll builds for mingw" +a13562e8 autoconf/libwebp: enable dll builds for mingw +9f469b57 typo: no_fancy -> no_fancy_upsampling +1a27f2f8 Merge "fix double to float conversion warning" +cf1e90de Merge "cwebp: fix jpg encodes on XP" +f2b5d19b [cd]webp: always output windows errors +e855208c fix double to float conversion warning +ecd66f77 cwebp: fix jpg encodes on XP +7b3eb372 Tune lossless compression to get better gains. +ce8bff45 Merge "VP8LAllocateHistogramSet: fix overflow in size calculation" +ab5b67a1 Merge "EncodeImageInternal: fix uninitialized free" +7fee5d12 Merge "GetHistoBits: fix integer overflow" +a6ae04d4 VP8LAllocateHistogramSet: fix overflow in size calculation +80237c43 GetHistoBits: fix integer overflow +8a997235 EncodeImageInternal: fix uninitialized free +0b9e6829 minor cosmetics +a792b913 fix the -g/O3 discrepancy for 32bit compile +73ba4357 Merge "detect and merge similar segments" +fee66275 detect and merge similar segments +0c44f415 src/webp/*.h: don't forward declare enums in C++ +d7a5ac86 vwebp: use demux interface +931e0ea1 Merge "replace 'typedef struct {} X;" by "typedef struct X X; struct X {};"" +8f216f7e remove cases of equal comparison for qsort() +28d25c82 replace 'typedef struct {} X;" by "typedef struct X X; struct X {};" +2afee60a speed up for ARM using 8bit for boolean decoder +5725caba new segmentation algorithm +2cf1f815 Merge "fix the BITS=8 case" +12f78aec fix the BITS=8 case +6920c71f fix MSVC warnings regarding implicit uint64 to uint32 conversions +f6c096aa webpmux binary: Rename 'xmp' option to 'meta' +ddfe871a webpmux help correction +b7c55442 Merge "Make *InitSSE2() functions be empty on non-SSE2 platform" +1c04a0d4 Common APIs for chunks metadata and color profile. +2a3117a1 Merge "Create WebPMuxFrameInfo struct for Mux APIs" +5c3a7231 Make *InitSSE2() functions be empty on non-SSE2 platform +7c6e60f4 make *InitSSE2() functions be empty on non-SSE2 platform +c7eb4576 make VP8DspInitNEON() public +ab3234ae Create WebPMuxFrameInfo struct for Mux APIs +e3990fd8 Alignment fixes +e55fbd6d Merge branch '0.2.0' +4238bc0a Update ChangeLog (tag: v0.2.0) +c655380c dec/io.c: cosmetics +fe1958f1 RGBA4444: harmonize lossless/lossy alpha values +681cb30a fix RGBA4444 output w/fancy upsampling +f06c1d8f Merge "Alignment fix" into 0.2.0 +f56e98fd Alignment fix +6fe843ba avoid rgb-premultiply if there's only trivial alpha values +528a11af fix the ARGB4444 premultiply arithmetic +a0a48855 Lossless decoder fix for a special transform order +62dd9bb2 Update encoding heuristic w.r.t palette colors. +6f4272b0 remove unused ApplyInverseTransform() +93bf0faa Update ChangeLog (tag: v0.2.0-rc1) +5934fc59 update AUTHORS +014a711d update NEWS +43b0d610 add support for ARGB -> YUVA conversion for lossless decoder +33705ca0 bump version to 0.2.0 +c40d7ef1 fix alpha-plane check + add extra checks +a06f8023 MODE_YUVA: set alpha to opaque if the image has none +52a87dd7 Merge "silence one more warning" into 0.2.0 +3b023093 silence one more warning +f94b04f0 move some RGB->YUV functions to yuv.h +4b71ba03 README: sync [cd]webp help output +c9ae57f5 man/dwebp.1: add links to output file format details +292ec5cc quiet a few 'uninitialized' warnings +4af3f6c4 fix indentation +9b261bf5 remove the last NOT_HAVE_LOG2 instances +323dc4d9 remove use of log2(). Use VP8LFastLog2() instead. +8c515d54 Merge "harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc" into 0.2.0 +d4b4bb02 Merge changes I46090628,I1a41b2ce into 0.2.0 +bff34ac1 harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc +a3c063c7 Merge "extra size check for security" into 0.2.0 +5e796300 Merge "WebPEncode: clear stats at the start of encode" into 0.2.0 +f1edf62f Merge "rationalize use of color-cache" into 0.2.0 +c1933317 extra size check for security +906be657 rationalize use of color-cache +dd1c3873 Add image-hint for low-color images. +4eb7aa64 Merge "WebPCheckMalloc() and WebPCheckCalloc():" into 0.2.0 +80cc7303 WebPCheckMalloc() and WebPCheckCalloc(): +183cba83 check VP8LBitWriterInit return +cbfa9eec lossless: fix crash on user abort +256afefa cwebp: exit immediately on version mismatch +475d87d7 WebPEncode: clear stats at the start of encode +a7cc7291 fix type and conversion warnings +7d853d79 add stats for lossless +d39177b7 make QuantizeLevels() store the sum of squared error +5955cf5e replace x*155/100 by x*101581>>16 +7d732f90 make QuantizeLevels() store the sum of squared error +e45a446a replace x*155/100 by x*101581>>16 +159b75d3 cwebp output size consistency: +cbee59eb Merge commit 'v0.1.99' +1889e9b6 dwebp: report -alpha option +3bc3f7c0 Merge "dwebp: add PAM output support" into 0.2.0 +d919ed06 dwebp: add PAM output support +85e215d3 README/manpages/configure: update website link +c3a207b9 Update ChangeLog (tag: v0.1.99) +d1fd7826 Merge "add extra precision about default values and behaviour" into 0.2.0 +efc826e0 add extra precision about default values and behaviour +9f29635d header/doc clean up +ff9fd1ba Makefile.vc: fix webpmux.exe *-dynamic builds +8aacc7b0 remove INAM, ICOP, ... chunks from the test webp file. +2fc13015 harmonize authors as "Name (mail@address)" +4a9f37b7 Merge "update NEWS" into 0.2.0 +7415ae13 makefile.unix: provide examples/webpmux target +ce82cedc update NEWS +641e28e8 Merge "man/cwebp.1: wording, change the date" into 0.2.0 +c37c23e5 README: cosmetics +3976dcd5 man/cwebp.1: wording, change the date +3e5bbe1c Merge "rename 'use_argb_input' to 'use_argb'" into 0.2.0 +ce90847a Merge "add some padding bytes areas for later use" into 0.2.0 +2390dabc Merge "fixing the findings by Frederic Kayser to the bitstream spec" into 0.2.0 +02751591 add a very crude progress report for lossless +a4b9b1c6 Remove some unused enum values. +dd108176 rename 'use_argb_input' to 'use_argb' +90516ae8 add some padding bytes areas for later use +d03b2503 fixing the findings by Frederic Kayser to the bitstream spec +ce156afc add missing ABI compatibility checks +9d45416a Merge "Doc: container spec text tweaks" into 0.2.0 +4e2e0a8c Doc: container spec text tweaks +f7f16a29 add ABI compatibility check +2a775570 Merge "swig: add WebPEncodeLossless* wrappers" into 0.2.0 +a3ec6225 mux.h: remove '* const' from function parameters +31426eba encode.h: remove '* const' from function parameters +9838e5d5 decode.h: remove '* const' from function parameters +4972302d swig: add WebPEncodeLossless* wrappers +9ff00cae bump encoder/decoder versions +c2416c9b add lossless quick encoding functions to the public API +4c1f5d64 Merge "NEWS: mention decode_vp8.h is no longer installed" into 0.2.0 +6cb2277d NEWS: mention decode_vp8.h is no longer installed +d5e5ad63 move decode_vp8.h from webp/ to dec/ +8d3b04a2 Merge "header clean-up" into 0.2.0 +02201c35 Merge "remove one malloc() by making color_cache non dynamic" into 0.2.0 +d708ec14 Merge "move MIN/MAX_HISTO_BITS to format_constants.h" into 0.2.0 +ab2da3e9 Merge "add a malloc() check" into 0.2.0 +2d571bd8 add a malloc() check +7f0c178e remove one malloc() by making color_cache non dynamic +6569cd7c Merge "VP8LFillBitWindow: use 64-bit path for msvc x64 builds" into 0.2.0 +23d34f31 header clean-up +2a3ab6f9 move MIN/MAX_HISTO_BITS to format_constants.h +985d3da6 Merge "shuffle variables in HashChainFindCopy" into 0.2.0 +cdf885c6 shuffle variables in HashChainFindCopy +c3b014db Android.mk: add missing lossless files +8c1cc6b5 makefile.unix dist: explicitly name installed includes +7f4647ee Merge "clarify the colorspace naming and byte ordering of decoded samples" into 0.2.0 +cbf69724 clarify the colorspace naming and byte ordering of decoded samples +857650c8 Mux: Add WebPDataInit() and remove WebPImageInfo +ff771e77 don't install webp/decode_vp8.h +596dff78 VP8LFillBitWindow: use 64-bit path for msvc x64 builds +3ca7ce98 Merge "doc: remove non-finalized chunk references" into 0.2.0 +1efaa5a3 Merge "bump versions" into 0.2.0 +51fa13e1 Merge "README: update cwebp help output" into 0.2.0 +12f9aede README: update cwebp help output +f0b5defb bump versions +4c42a61b update AUTHORS +6431a1ce doc: remove non-finalized chunk references +8130c4cc Merge "build: remove libwebpmux from default targets/config" +23b44438 Merge "configure: broaden test for libpng-config" +85bff2cd Merge "doc: correct lossless prefix coding table & code" +05108f6e Merge "More spec/code matching in mux:" +6808e69d More spec/code matching in mux: +bd2b46f5 Merge "doc/webp-container-spec: light cosmetics" +20ead329 doc/webp-container-spec: light cosmetics +1d40a8bc configure: add pthread detection +b5e9067a fix some int <-> size_t mix for buffer sizes +e41a7596 build: remove libwebpmux from default targets/config +0fc2baae configure: broaden test for libpng-config +45b8272c Merge "restore authorship to lossless bitstream doc" +06ba0590 restore authorship to lossless bitstream doc +44a09a3c add missing description of the alpha filtering methods +63db87dd Merge "vwebp: add checkboard background for alpha display" +a73b8978 vwebp: add checkboard background for alpha display +939158ce Merge "vwebp: fix info display" +b35c07d9 vwebp: fix info display +48b39eb1 fix underflow for very short bitstreams +7e622984 cosmetics: param alignment, manpage wording +1bd7dd50 Merge changes I7b0afb0d,I7ecc9708 +ac69e63e Merge "Updated cwebp man's help for Alpha & Lossless." +c0e8859d Get rid of image_info_ from WebPChunk struct. +135ca69e WebP Container Spec: +eb6f9b8a Updated cwebp man's help for Alpha & Lossless. +0fa844fb cosmetic fixes on assert and 'const' where applicable +7f22bd25 check limit of width * height is 32 bits +16c46e83 autoconf/make: cosmetics: break long lines +ab22a07a configure: add helper macro to define --with-* +c17699b3 configure: add libtiff test +0e09732c Merge "cwebp: fix crash with yuv input + lossless" +88a510ff Merge "fix big-endian VP8LWriteBits" +da99e3bf Merge "Makefile.vc: split mux into separate lib" +7bda392b cwebp: fix crash with yuv input + lossless +f56a369a fix big-endian VP8LWriteBits +54169d6c Merge "cwebp: name InputFileFormat members consistently" +e2feefa9 Makefile.vc: split mux into separate lib +27caa5aa Merge "cwebp: add basic TIFF support" +d8921dd4 cwebp: name InputFileFormat members consistently +6f76d246 cwebp: add basic TIFF support +4691407b Merge changes If39ab7f5,I3658b5ae +cca7c7b8 Fixed nit: 10 -> 10.f +5d09a244 WebPMuxCreate() error handling: +777341c3 Fix a memleak in WebPMuxCreate() +61c9d161 doc: correct lossless prefix coding table & code +4c397579 Merge "mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN" +e4e36cc6 Merge "Mux: Allow only some frames/tiles to have alpha." +ad2aad3c Merge "WebP Decoding error handling:" +97649c8f Mux: Allow only some frames/tiles to have alpha. +f864be3b Lower the quality settings for Alpha encoding. +3ba81bbe WebP Decoding error handling: +fcc69923 add automatic YUVA/ARGB conversion during WebPEncode() +802e012a fix compilation in non-FANCY_UPSAMPLING mode +e012dfd9 make width/height coding match the spec +228d96a5 mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN +637a314f remove the now unused *KeepA variants +d11f6fcc webpmux returns error strings rather than numbers +fcec0593 makefile.unix: cwebp: fix OSX link +6b811f1b Merge "doc: remove lossless pdf" +c9634821 doc: remove lossless pdf +b9ae4f0d cosmetics after mux changes b74ed6e, b494ad5 +b494ad50 Mux: only allow adding frame/tiles at the end. +2c341b0e Merge "Added image characteristic hint for the codec." +d373076a Added image characteristic hint for the codec. +2ed2adb5 Merge "msvc: add intrinsic based BitsLog2Floor" +e595e7c5 Merge "add demux.c to the makefiles" +da47b5bd Merge "demux: add {Next,Prev}Chunk" +e5f46742 add demux.c to the makefiles +4708393c demux: add {Next,Prev}Chunk +e8a0a821 demux: quiet msvc warnings +7f8472a6 Update the WebP Container Spec. +31b68fe6 cleanup WebPPicture struct and API +9144a186 add overflow check before calling malloc() +81720c91 consistency cosmetics +2ebe8394 Merge "Add kramdown version information to README" +71443084 enc/vp8l.c: fix build +b7ac19fe Add kramdown version information to README +efdcb667 Merge "Edit for consistency, usage and grammar." +08220102 Enable alpha in vvwebp +8de9a084 Merge "Mux API change:" +b74ed6e7 Mux API change: +233a589e take picture->argb_stride into account for lossless coding +04e33f17 Edit for consistency, usage and grammar. +a575b4bc Merge "cosmetics: add missing const" +8d99b0f4 Merge "cosmetics: remove unimplemented function proto" +69d02217 cosmetics: add missing const +5b08318b cosmetics: remove unimplemented function proto +b7fb0ed5 Log warning for unsupported options for lossless. +e1f769fe msvc: add intrinsic based BitsLog2Floor +8a69c7d8 Bug-fix: Clamp backward dist to 1. +b5b6ac97 Merge "Bring the special writer 'WebPMemoryWriter' to public API" +a6a1909f Merge "Fix floating point exception with cwebp -progress" +f2cee067 Fix floating point exception with cwebp -progress +91b7a8c7 Bring the special writer 'WebPMemoryWriter' to public API +310e2972 support resize and crop for RGBA input +a89835d3 Merge changes Ice662960,Ie8d7aa90,I2d996d5e,I01c04772 +ce614c0c Merge "dec/vp8: avoid setting decoder status twice" +900285da dec/vp8: avoid setting decoder status twice +8227adc8 Merge changes I6f02b0d0,I5cbc9c0a,I9dd9d4ed,Id684d2a1 +dcda59c1 Merge "demux: rename SetTile to SelectTile" +622ef12e demux: rename SetTile to SelectTile +81ebd375 Merge "demux: add {Next,Prev}Frame" +02dd37a2 demux: add {Next,Prev}Frame +4b79fa59 Merge "Limit the maximum size of huffman Image to 16MB." +9aa34b34 Manually number "chapters," as chapter numbers are used in the narrative. +2a4c6c29 Re-wrap at <= 72 columns +a45adc19 Apply inline emphasis and monospacing, per gdoc / PDF +91011206 Incorporate gdoc changes through 2012-06-08 +7a182487 Removed CodeRay syntax declarations ... +b3ec18c5 Provide for code-block syntax highlighting. +709d7702 Replace high ASCII artifacts (curly quotes, etc.). +930e8abb Lossless WebP doc largely ported to markdown text. +18cae37b msvc: silence some build warnings +b3923084 Limit the maximum size of huffman Image to 16MB. +f180df2a Merge "libwebp/demux: add Frame/Chunk iteration" +2bbe1c9a Merge "Enable lossless encoder code" +d0601b01 Merge changes I1d97a633,I81c59093 +78f3e345 Enable lossless encoder code +d974a9cc Merge "libwebp/demux: add simple format parsing" +26bf2232 Merge "libwebp: add WebPDemux stub functions" +2f666688 Merge "modify WebPParseHeaders to allow reuse by GetFeatures" +b402b1fb libwebp/demux: add Frame/Chunk iteration +ad9ada3b libwebp/demux: add WebPDemuxGetI +2f2d4d58 libwebp/demux: add extended format parsing +962dcef6 libwebp/demux: add simple format parsing +f8f94081 libwebp: add WebPDemux stub functions +fb47bb5c Merge "NumNamedElements() should take an enum param." +7c689805 Fix asserts in Palette and BackwardReference code. +fbdcb7ea NumNamedElements() should take an enum param. +fb4943bd modify WebPParseHeaders to allow reuse by GetFeatures +3697b5ce write an ad-hoc EncodeImageInternal variant +eaee9e79 Bug-Fix: Decode small (less than 32 bytes) images. +0bceae48 Merge "cwebp: fix alpha reporting in stats output" +0424b1ef Rebase default encoding settings. +c71ff9e3 cwebp: fix alpha reporting in stats output +e2ffe446 Merge "Stop indefinite recursion for Huffman Image." +70eb2bd6 Stop indefinite recursion for Huffman Image. +f3bab8eb Update vwebp +6d5c797c Remove support for partial files in Mux. +f1df5587 WebPMuxAssemble() returns WebPData*. +814a0639 Rename 'Add' APIs to 'Set'. +bbb0218f Update Mux psuedo-code examples. +4fc4a47f Use WebPData in MUX set APIs +c67bc979 Merge "add WebPPictureImportRGBX() and WebPPictureImportBGRX()" +27519bc2 add WebPPictureImportRGBX() and WebPPictureImportBGRX() +f80cd27e factorize code in Import() +9b715026 histogram: add log2 wrapper +8c34378f Merge "fix some implicit type conversion warnings" +42f6df9d fix some implicit type conversion warnings +250c16e3 Merge "doc: update lossless pdf" +9d9daba4 Merge "add a PDF of the lossless spec" +8fbb9188 prefer webp/types.h over stdint.h +0ca170c2 doc: update lossless pdf +0862ac6e add a PDF of the lossless spec +437999fb introduce a generic WebPPictureHasTransparency() function +d2b6c6c0 cosmetic fixes after Idaba281a +b4e6645c Merge "add colorspace for premultiplied alpha" +48f82757 add colorspace for premultiplied alpha +069f903a Change in lossless bit-stream. +5f7bb3f5 Merge "WebPReportProgress: use non-encoder specific params" +f18281ff WebPReportProgress: use non-encoder specific params +9ef32283 Add support for raw lossless bitstream in decoder. +7cbee29a Fix bug: InitIo reseting fancy_upsampling flag. +880fd98c vwebp: fix exit w/freeglut +1875d926 trap two unchecked error conditions +87b4a908 no need to have mux.h as noinst clause in enc/ +88f41ec6 doc: fix bit alignment in VP8X chunk +52f5a4ef Merge "fix bug with lossy-alpha output stride" +3bde22d7 fix bug with lossy-alpha output stride +42d61b6d update the spec for the lossy-alpha compression methods. +e75dc805 Move some more defines to format_constants.h +c13f6632 Move consts to internal header format_constants.h +7f2dfc92 use a bit-set transforms_seen_ instead of looping +18da1f53 modulate alpha-compression effort according to config.method +f5f2fff6 Merge "Alpha flag fix for lossless." +c975c44e Alpha flag fix for lossless. +4f067fb2 Merge "Android: only build dec_neon with NEON support" +255c66b4 Android: only build dec_neon with NEON support +8f9117a9 cosmetics: signature fixes +39bf5d64 use header-less lossless bitstream for alpha channel +75d7f3b2 Merge "make input data be 'const' for VP8LInverseTransform()" +9a721c6d make input data be 'const' for VP8LInverseTransform() +9fc64edc Disallow re-use of same transformation. +98ec717f use a function pointer for ProcessRows() +f7ae5e37 cosmetics: join line +140b89a3 factor out buffer alloc in AllocateARGBBuffers() +a107dfa8 Rectify WebPParseOptionalChunks(). +237eab67 Add two more color-spaces for lossless decoding. +27f417ab fix orthographic typo +489ec335 add VP8LEncodeStream() to compress lossless image stream +fa8bc3db make WebPEncodingSetError() take a const picture +638528cd bitstream update for lossy alpha compression +d73e63a7 add DequantizeLevels() placeholder +ec122e09 remove arch-dependent rand() +d40e7653 fix alignment +1dd6a8b6 Merge "remove tcoder, switch alpha-plane compression to lossless" +3e863dda remove tcoder, switch alpha-plane compression to lossless +8d77dc29 Add support for lossless in mux: +831bd131 Make tile size a function of encoding method. +778c5228 Merge "remove some variable shadowing" +817c9dce Few more HuffmanTreeToken conversions. +37a77a6b remove some variable shadowing +89c07c96 Merge "normalize example header includes" +4aff411f Merge "add example_util.[hc]" +00b29e28 normalize example header includes +061263a7 add example_util.[hc] +c6882c49 merge all tree processing into a single VP8LProcessTree() +9c7a3cf5 fix VP8LHistogramNumCodes to handle the case palette_code_bits == 0 +b5551d2e Merge "Added HuffmanTreeCode Struct for tree codes." +8b85d01c Added HuffmanTreeCode Struct for tree codes. +093f76d8 Merge "Allocate single memory in GetHuffBitLengthsAndCodes." +41d80494 Allocate single memory in GetHuffBitLengthsAndCodes. +1b04f6d2 Correct size in VP8L header. +2924a5ae Makefile.vc: split object lists based on directory +c8f24165 Merge "add assert(tokens)" +43239947 add assert(tokens) +9f547450 Catch an error in DecodeImageData(). +ac8e5e42 minor typo and style fix +9f566d1d clean-up around Huffman-encode +c579a710 Introduce CHUNK_SIZE_BYTES in muxi.h. +14757f8a Make sure huffman trees always have valid symbols +41050618 makefile.unix: add support for building vwebp +48b37721 Merge "fixed signed/unsigned comparison warning" +57f696da Merge "EncodeImageInternal: fix potential leak" +d972cdf2 EncodeImageInternal: fix potential leak +5cd12c3d fixed signed/unsigned comparison warning +cdca30d0 Merge "cosmetics: shorten long line" +e025fb55 cosmetics: shorten long line +22671ed6 Merge "enc/vp8l: fix double free on error" +e1b9b052 Merge "cosmetics: VP8LCreateHuffmanTree: fix indent" +a8e725f8 enc/vp8l: fix double free on error +27541fbd cosmetics: VP8LCreateHuffmanTree: fix indent +1d38b258 cwebp/windows: use MAKE_REFGUID where appropriate +817ef6e9 Merge "cwebp: fix WIC/Microsoft SDK compatibility issue" +902d3e3b cwebp: fix WIC/Microsoft SDK compatibility issue +89d803c4 Merge "Fix a crash due to wrong pointer-integer arithmetic." +cb1bd741 Merge "Fix a crash in lossless decoder." +de2fe202 Merge "Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool' outside the loop. - Some renames/tweaks for readability." +ce69177a Fix a crash due to wrong pointer-integer arithmetic. +e40a3684 Fix a crash in lossless decoder. +3927ff3a remove unneeded error condition for WebPMuxNumNamedElements() +2c140e11 Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool' outside the loop. - Some renames/tweaks for readability. +861a5b7b add support for animation +eb5c16cc Merge "Set correct encode size in encoder's stats." +4abe04a2 fix the return value and handle missing input file case. +2fafb855 Set correct encode size in encoder's stats. +e7167a2b Provide one entry point for backward references. +c4ccab64 Print relevant lossless encoding stats in cwebp. +e3302cfd GetHuffBitLengthsAndCodes: reduce level of indirection +b5f2a9ed enc/vp8l: fix uninitialized variable warning +7885f8b2 makefile.unix: add lossless encoder files +1261a4c8 Merge "cosmetics" +3926b5be Merge "dsp/cpu.c: Android: fix crash on non-neon arm builds" +834f937f dsp/cpu.c: Android: fix crash on non-neon arm builds +126e1606 cosmetics +e38602d2 Merge branch 'lossless_encoder' +e8d3d6a0 split StoreHuffmanCode() into smaller functions +d0d88990 more consolidation: introduce VP8LHistogramSet +1a210ef1 big code clean-up and refactoring and optimization +41b5c8ff Some cosmetics in histogram.c +ada6ff77 Approximate FastLog between value range [256, 8192] +ec123ca3 Forgot to update out_bit_costs to symbol_bit_costs at one instance. +cf33ccd1 Evaluate output cluster's bit_costs once in HistogramRefine. +781c01f4 Simple Huffman code changes. +a2849bc5 Lossless decoder: remove an unneeded param in ReadHuffmanCodeLengths(). +b39e7487 Reducing emerging palette size from 11 to 9 bits. +bfc73db4 Move GetHistImageSymbols to histogram.c +889a5786 Improve predict vs no-predict heuristic. +01f50663 code-moving and clean-up +31035f3b reduce memory usage by allocating only one histo +fbb501b8 Restrict histo_bits to ensure histo_image size is under 32MB +8415ddf3 further simplification for the meta-Huffman coding +e4917299 A quick pass of cleanup in backward reference code +83332b3c Make transform bits a function of encode method (-m). +72920caa introduce -lossless option, protected by USE_LOSSLESS_ENCODER +c6ac4dfb Run TraceBackwards for higher qualities. +412222c8 Make histo_bits and transform_bits function of quality. +149b5098 Update lossless encoder strategy: +0e6fa065 cache_bits passed to EncodeImageInternal() +e38b40a9 Factorize code for clearing HtreeGroup. +6f4a16ea Removing the indirection of meta-huffman tables. +3d33ecd1 Some renaming/comments related to palette in lossless encoder. +4d02d586 Lossless encoder: correction in Palette storage +4a636235 fix a memleak in EncodeImageInternal() +0993a611 Full and final fix for prediction transform +afd2102f Fix cross-color transform in lossless encoder +b96d8740 Need to write a '0' bit at the end of transforms. +54dad7e5 Color cache size should be counted as 0 when cache bits = 0 +4f0c5caf Fix prediction transform in lossless encoder. +36dabdad Fix memory leak in method EncodeImageInternal for histogram_image. +352a4f49 Get rid of PackLiteralBitLengths() +d673b6b9 Change the predictor function to pass left pixel +b2f99465 Fix CopyTileWithPrediction() +84547f54 Add EncodeImageInternal() method. +6b38378a Guard the lossless encoder (in flux) under a flag +09f7532c Fix few nits (const qualifiers) +648be393 Added implementation for various lossless functions +32714ce3 Add VP8L prefix to backward ref & histogram methods. +fcba7be2 Fixed header file tag (WEBP_UTILS_HUFFMAN_ENCODE_H_) +bc703746 Add backward_ref, histogram & huffman encode modules from lossless. +fdccaadd Fixing nits +227110c4 libwebp interface changes for lossless encoding. +50679acf minor style fixes +b38dfccf remove unneeded reference to NUM_LITERAL_CODES +8979675b harmonize header description +c04eb7be tcoder.c: define NOT_HAVE_LOG2 for MSVC builds +9a214fa1 Merge "VP8[L]GetInfo: check input pointers" +5c5be8ba VP8[L]GetInfo: check input pointers +0c188fec Merge changes I431acdfe,I713659b7 +b3515c62 mux: drop 'chunk' from ChunkInfo member names +aea7923c muxi.h: remove some unused defines +01422492 update NEWS file for next release +29e3f7ec Merge "dec: remove deprecated WebPINew()" +4718e449 Merge "muxedit: a few more size_t changes" +82654f96 Merge "muxedit: remove a few redundant NULL checks" +02f27fbd dec: remove deprecated WebPINew() +ccddb3fc muxedit: remove a few redundant NULL checks +a6cdf710 muxedit: a few more size_t changes +a3846892 Merge "mux: remove unused LIST_ID" +11ae46ae alpha.c: quiet some size_t -> int conversion warnings +dee46692 mux: remove unused LIST_ID +03f1f493 mux: add version checked entry points +6a0abdaa Merge "doc: tile/alpha corrections" +c8139fbe Merge "few cosmetics" +68338737 Merge "lossless: remove some size_t -> int conversions" +5249e94a doc: tile/alpha corrections +d96e722b huffman: quiet int64 -> int conversion warning +532020f2 lossless: remove some size_t -> int conversions +23be6edf few cosmetics +1349edad Merge "configure: AC_ARG_* use AS_HELP_STRING" +bfbcc60a configure: AC_ARG_* use AS_HELP_STRING +1427ca8e Merge "Makefile.am: header file maintenance" +087332e3 Merge "remove unused parameter 'round' from CalcProba()" +9630e168 remove unused parameter 'round' from CalcProba() +92092eaa Merge "bit_reader.h: correct include" +a87fc3f6 Merge "mux: ensure # images = # tiles" +53af99b1 Merge "mux: use size_t consistently" +39a57dae Makefile.am: header file maintenance +1bd0bd0d bit_reader.h: correct include +326a3c6b mux: ensure # images = # tiles +95667b8d mux: use size_t consistently +231ec1fb Removing the indirection of meta-huffman tables. +15ebcbaa check return pointer from MuxImageGetListFromId +b0d6c4a7 Merge "configure: remove test for zlib.h" +8cccac50 Merge "dsp/lossless: silence some build warnings" +b08819a6 dsp/lossless: silence some build warnings +7ae22521 Android.mk: SSE2 & NEON updates +0a49e3f3 Merge "makefile.unix add missing header files" +2e75a9a1 Merge "decode.h: use size_t consistently" +fa13035e configure: remove test for zlib.h +d3adc81d makefile.unix add missing header files +262fe01b Merge "makefile.unix & Android.mk: cosmetics" +4cce137e Merge "enc_sse2 add missing stdlib.h include" +80256b85 enc_sse2 add missing stdlib.h include +9b3d1f3a decode.h: use size_t consistently +64083d3c Merge "Makefile.am: cosmetics" +dceb8b4d Merge changes If1331d3c,I86fe3847 +0e33d7bf Merge "webp/decode.h: fix prototypes" +fac0f12e rename BitReader to VP8LBitReader +fbd82b5a types.h: centralize use of stddef.h +2154835f Makefile.am: cosmetics +1c92bd37 vp8io: use size_t for buffer size +90ead710 fix some more uint32_t -> size_t typing +cbe705c7 webp/decode.h: fix prototypes +3f8ec1c2 makefile.unix & Android.mk: cosmetics +217ec7f4 Remove tabs in configure.ac +b3d35fc1 Merge "Android.mk & Makefile.vc: add new files" +0df04b9e Android.mk & Makefile.vc: add new files +e4f20c5b Merge "automake: replace 'silent-rules' w/AM_SILENT_RULES" +8d254a09 cosmetics +6860c2ea fix some uint32_t -> size_t typing +4af1858a Fix a crash due to max symbol in a tree >= alphabet size +6f01b830 split the VP8 and VP8L decoding properly +f2623dbe enable lossless decoder +b96efd7d add dec/vp8i.h changes from experimental +19f6398e add dec/vp8l{i.h,.c} from experimental +c4ae53c8 add utils/bit_reader.[hc] changes from experimental +514d0089 add dsp/lossless.[hc] from experimental +9c67291d add utils/huffman.[hc] from experimental +337914a0 add utils/color_cache.[hc] from experimental +b3bf8fe7 the read-overflow code-path wasn't reporting as an error +1db888ba take colorspace into account when cropping +61c2d51f move the rescaling code into its own file and make enc/ and dec/ use it. +efc2016a Make rescaler methods generic +3eacee81 Move rescaler methods out of io.c. +a69b893d automake: replace 'silent-rules' w/AM_SILENT_RULES +6f7bf645 issue 111: fix little-endian problem in bit-reader +ed278e22 Removed unnecessary lookup +cd8c3ba7 fix some warnings: down-cast and possibly-uninitialized variable +0a7102ba ~1% improvement of alpha compression +3bc1b141 Merge "Reformat container doc" +dc17abdc mux: cosmetics +cb5810df Merge "WebPMuxGetImage: allow image param to be NULL" +506a4af2 mux: cosmetics +135e8b19 WebPMuxGetImage: allow image param to be NULL +de556b68 Merge "README.mux: reword some descriptions" +0ee2aeb9 Makefile.vc: use batch mode rules +d9acddc0 msvc: move {i,p}db creation to object directory +237c9aa7 Merge "expose WebPFree function for DLL builds" +b3e4054f silence msvc debug build warning +45feb55d expose WebPFree function for DLL builds +11316d84 README.mux: reword some descriptions +4be52f4a factorize WebPMuxValidate +14f6b9f6 mux: light cleanup +5e96a5db add more param checks to WebPPictureDistortion() +8abaf820 Merge "silence some type size related warnings" +1601a39b silence some type size related warnings +f3abe520 Merge "idec: simplify buffer size calculation" +a9c5cd4c idec: simplify buffer size calculation +7b06bd7f Merge "configure/automake: add silent-rules option" +e9a7d145 Reformat container doc +d4e5c7f3 configure/automake: add silent-rules option +5081db78 configure/automake: no -version-info for convenience libs +85b6ff68 Merge "idec: fix WebPIUpdate failure" +7bb6a9cc idec: fix internal state corruption +89cd1bb8 idec: fix WebPIUpdate failure +01b63806 4-5% faster decoding, optimized byte loads in arithmetic decoder. +631117ea Merge "cosmetics & warnings" +a0b2736d cosmetics & warnings +f73947f4 use 32bit for storing dequant coeffs, instead of 16b. +b9600308 Merge "store prediction mode array as uint8_t[16], not int[16]." +7b67881a store prediction mode array as uint8_t[16], not int[16]. +cab8d4dc Merge "NEON TransformOne" +ba503fda NEON TransformOne +9f740e3b Merge "gcc warning fix: remove the 'const' qualifier." +f76d3587 gcc warning fix: remove the 'const' qualifier. +e78478d6 Merge "webpmux: make more use of WebPData" +f85bba3d Merge "manpages: add BUGS section" +48a43bbf Merge "makefile.unix: variable cosmetics" +c274dc96 makefile.unix: variable cosmetics +1f7b8595 re-organize the error-handling in the main loop a bit +1336fa71 Only recompute level_cost_[] when needed +771ee449 manpages: add BUGS section +0f7820e6 webpmux: make more use of WebPData +974aaff3 examples: logging updates +6c14aadd Merge "better token buffer code" +f4054250 better token buffer code +18d959fa Merge "mux: add WebPData type" +eec4b877 mux: add WebPData type +0de3096b use 16bit counters for recording proba counts +7f23678d fix for LevelCost + little speed-up +7107d544 further speed-up/cleanup of RecordCoeffs() and GetResidualCost() +fd221040 Introduce Token buffer (unused for now) +5fa148f4 Merge "speed-up GetResidualCost()" +28a9d9b4 speed-up GetResidualCost() +11e7dadd Merge "misc cosmetics" +378086bd misc cosmetics +d61479f9 add -print_psnr and -print_ssim options to cwebp. +2e3e8b2e add a WebPCleanupTransparentArea() method +552c1217 Merge "mux: plug some memory leaks on error" +a2a81f7d Merge "fix Mach-O shared library build" +b3482c43 Merge "fix gcc-4.0 apple 32-bit build" +e4e3ec19 fix gcc-4.0 apple 32-bit build +b0d2fecf mux: plug some memory leaks on error +f0d2c7a7 pass of cosmetics +b309a6f9 fix Mach-O shared library build +241ddd38 doc: delete mux container pdf +8b1ba272 doc: update VP8 decode guide link +7e4371c5 WebPMuxCreate: fix unchecked malloc +eb425586 Merge "have makefile.unix clean up src/webp/*~ too" +a85c3631 Merge "correct EncodeAlpha documentation" +a33842fd Merge "Update webp container spec with alpha filter options." +8d6490da Incremental support for some of the mux APIs. +b8375abd have makefile.unix clean up src/webp/*~ too +b5855fc7 correct EncodeAlpha documentation +dba37fea Update webp container spec with alpha filter options. +2e74ec8b fix compile under MINGW +716d1d7f fix suboptimal MAX_LEN cut-off limit +57cab7b8 Harmonize the alpha-filter predictions at boundary +3a989534 Merge "Fix bug for Alpha in RGBA_4444 color-mode." +8ca2076d Introduce a 'fast' alpha mode +221a06bb Fix bug for Alpha in RGBA_4444 color-mode. +ad1e163a cosmetics: normalize copyright headers +c77424d7 cosmetics: light include cleanup +9d0e17c9 fix msvc build breakage after 252028a +7c4c177c Some readability fixes for mux library +d8a47e66 Merge "Add predictive filtering option for Alpha." +252028aa Add predictive filtering option for Alpha. +9b69be1c Merge "Simplify mux library code" +a056170e Simplify mux library code +992187a3 improve log2 test +e852f832 update Android.mk file list +a90cb2be reduce number of copies and mallocs in alpha plane enc/dec +b1662b05 fix some more type conversion warnings w/MSVC +223d8c60 fix some uint64_t -> int conversion warnings with MSC +c1a0437b Merge "simplify checks for enabling SSE2 code" +f06817aa simplify checks for enabling SSE2 code +948d4fe9 silence a msvc build warning +91179549 vwebp: msvc build tweaks +7937b409 simple WebP viewer, based on OpenGL +6aac1df1 add a bunch of missing 'extern "C"' +421eb99d Merge "Remove assigned-but-not-used variable "br"" +91e27f45 better fitting names for upsampling functions +a5d7ed5c Remove assigned-but-not-used variable "br" +f62d2c94 remove unused 'has_alpha' from VP8GetInfo() signature +08e86582 trap alpha-decoding error +b361eca1 add cut-off to arith coder probability update. +8666a93a Some bug-fixes for images with alpha. +273a12a0 fix off-by-1 diff in case cropping and simple filtering +2f741d1e webpmux: ReadImage: fix ptr free in error case +721f3f48 fix alpha decode +60942c8c fix the has_alpha_ order +30971c9e Implement progress report (and user abort) +eda520a9 cosmetics after 9523f2a +38bd5bb5 Merge "Better alpha support in webpmux binary" +ccbaebfe Merge "Updated the includes to relative paths." +d71fbdcc fix small typo in error message array +cdf97aa2 Better alpha support in webpmux binary +885f25bc Updated the includes to relative paths. +a0ec9aac Update WebP encoder (cwebp) to support Alpha. +667b769a Fixed the include for types.h within mux.h +9523f2a5 Add Alpha Encode support from WebPEncode. +16612ddd Merge "Add Alpha Decode support from WebPDecode." +d117a940 Add Alpha Decode support from WebPDecode. +67228734 cosmetics after e1947a9 +e1947a92 Add Alpha encode/decode code. +afc4c5d6 simplify code by introducing a CopyPlane() helper func +113b3128 Merge "MUX API Updates" +c398f595 MUX API Updates +5acf04ef remove orphan source file +059f03ef Merge "dec: validate colorspace before using as array index" +70a03989 Merge "factorize some code" +9b243b3d factorize some code +372e2b46 Correct a bug in ReadPNG() with GRAY_ALPHA images +469d6eb9 Merge "Makefile.am: remove redundant noinst_HEADERS" +9fe3372f dec: validate colorspace before using as array index +8962030f remove orphan source file +ced3e3f4 Makefile.am: remove redundant noinst_HEADERS +964387ed use WEBP_INLINE for inline function declarations +90880a11 Merge "manpages: break long lines" +b5910895 Merge "manpages: minor formatting updates" +4c451e4a Merge "Rectify the Chunk parsing logic." +04e84cf1 examples: slight cleanup +099717ce manpages: break long lines +1daf39bb manpages: minor formatting updates +abd030b5 fix missing "(void)" in function signature +f6a7d758 remove useless test +f07b2138 Rectify the Chunk parsing logic. +b8634f7d webpmux: fix lib link order +42c2e682 Fix missing coma (on uncompiled code) +d8329d41 Android.mk: add missing source files +13a54df5 Merge "More aggressive copy-edit; add TODO; validate HTML5" +868b96ae More aggressive copy-edit; add TODO; validate HTML5 +767afea2 configure: check for a symbol contained in libpng +408b8918 Merge "Linewrap at 72 cols. Casual copy-edit." +3ae318c7 Merge "Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.)" +918eb2d8 Merge "Basic container doc source clean-up; fix lists and pseudocode blocks." +03bec9e0 Linewrap at 72 cols. Casual copy-edit. +2678d819 Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.) +428674da Basic container doc source clean-up; fix lists and pseudocode blocks. +6a77d928 Merge "Makefile.vc: cosmetics" +28c38e8c Merge "Makefile.vc: condense directory creation rules" +55be2cf8 Initial import of container spec document, from pdftotext transform. +a82a788b Makefile.vc: cosmetics +c8f41ce5 Makefile.vc: condense directory creation rules +2b877cd0 Some fixes to Makefile.vc to support the src\mux directory. +3eb969b3 Merge "Add Makefile.vc for Mux library & binary." +e78e971e Add Makefile.vc for Mux library & binary. +6aedde58 Add manual for WebPMux tool. +8a360d0a Merge "Added WebPMux Binary." +a4f32cae Added WebPMux Binary. +f3bf4c76 Added Mux Container Spec & README for MUX-API. +9f761cfa Changed function signature for WebPMuxCreate +5f31b5ec Merge "Add Mux library for manipulating WebP container." +2315785f Add Mux library for manipulating WebP container. +7e198abb update ChangeLog (tag: v0.1.3) +dfc9c1ea Harmonize the dates +28ad70c5 Fix PNG decoding bug +846e93c5 Update AUTHORS & add .mailmap +563e52d6 cosmetics after '76036f5 Refactor decoder library' +76036f54 Refactor decoder library +377ef43c configure.ac: update AC_INIT params +7a8d8762 use a user-visible MACRO for max width/height. +d4e9f559 NEON decode support in WebP +0ee683b5 update libtool version-info +fdbe02c5 windows: match _cond_destroy logic w/return variable name +206b686b README: correct advanced decode api pseudo-code +6a32a0f5 make VP8BitReader a typedef, for better re-use +b112e836 create a libwebputils under src/utils +ee697d9f harmonize the include guards and #endif comments +a1ec07a6 Fixing compiler error in non x86 arch. +dcfa509a Fixed recursive inclusion of bit_writer.h and vp8enci.h. +e06ac088 create a separate libwebpdsp under src/dsp +ebeb412a use unsigned int for bitfields +341cc56a make kNewRange a static array +227a91e5 README: minor wording update +05bd8e6a add man pages to dist +812dfa1a bump up versions in preparations for 0.1.3 +a5b78c81 wrap alpha-related options under WEBP_EXPERIMENTAL_FEATURES flag +34dc7907 regen ChangeLog for 0.1.3-rc2 +7c436630 Silence some (more) Visual Studio warnings. +60306e8c add top-level gitattributes +2aa6b80e Slience some Visual Studio warnings. +4cbbb290 Merge "bump up version for next freeze" +a3291674 bump up version for next freeze +c7e86aba cosmetics: fix comment line lengths +c9e037ab makefile.unix: add simple dist target +87d58ce9 makefile.unix: rule maintenance +d477de77 mend +fac15ec7 Update NEWS & README for next release V0.1.3 +6215595c Merge "add a -partition_limit option to limit the number of bits used by intra4x4" +3814b76c Merge "reorganize chunk-parsing code" +900286e0 add a -partition_limit option to limit the number of bits used by intra4x4 +cd12b4b0 add the missing cost for I4/I16 mode selection +dfcc2136 reorganize chunk-parsing code +3cf20306 initialize pointers to function within VP8DspInit() +d21b4795 Merge "windows: add decode threading support" +473ae953 fix hang on thread creation failure +fccca420 windows: add decode threading support +a31f843a Use the exact PNG_INCLUDES/PNG_LIBS when testing for -lpng +ad9b45f1 Merge "Makefile.vc: rule maintenance" +565a2cab Makefile.vc: rule maintenance +2d0da681 makefile.unix: disable Wvla by default +fc7815d6 multi-thread decoding: ~25-30% faster +acd8ba42 io->teardown() was not always called upon error +c85527b1 Merge "Makefile.vc: add DLL configs" +e1e9be35 cosmetics: spelling/grammar in README and lib headers +b4d0ef8f Makefile.vc: add DLL configs +998754a7 remove unused nb_i4_ and nb_i16_ fields. +9f01ce3a rename WebPDecBuffer::memory -> private_memory +fb5d659b fix an overflow bug in LUT calculation +d646d5c7 swig: add WebPDecodeARGB +78aeed40 add missing WebPDecodeARGBInto() and switch ARGB4444 to RGBA4444 as was intended +cd7c5292 explicitly mark library functions as extern +19db59f8 add support for RGB565, ARGB4444 and ARGB colorspace (decoder) +c915fb2a encoder speed-up: hardcode special level values +c558bdad Rename and improve the API to retrieve decoded area +bf599d74 Merge "makefile.unix: disable -Wvla by default" +c9ea03d7 SSE2 version of strong filtering +993af3e2 makefile.unix: disable -Wvla by default +3827e1bc Merge "examples: (windows/WIC) add alpha support" +e291fae0 SSE2 functions for the fancy upsampler. +a06bbe2e add WebPISetIOHooks() to set some custom hooks on the incremental decoder object. +7643a6f2 Merge "makefile.unix: use uname to detect OSX environment" +5142a0be export alpha channel (if present) when dumping to PGM format +14d5731c makefile.unix: use uname to detect OSX environment +08057062 examples: quiet warnings +3cfe0888 examples: (windows/WIC) add alpha support +13ed94b8 add compile warning for variable-length-array +5a18eb1a Merge "add Advanced Decoding Interface" +5c4f27f9 add missing \n +f4c4e416 80 cols fix +d2603105 add Advanced Decoding Interface +bd2f65f6 sse2 version of the complex filter +96ed9ce0 perform two idct transforms at a time when possible +01af7b69 use aligned stored +0e1d1fdf Merge "Makefile.vc: add experimental target" +2a1292a6 Makefile.vc: add experimental target +23bf351e Enable decode SSE2 for Visual Studio +131a4b7b dec/dsp_sse2: fix visual studio compile +00d9d680 swig: file reorganization +7fc7e0d9 Merge "swig/java: basic encode support" +3be57b16 fix MSVC compile for WEBP_EXPERIMENTAL_FEATURES +40a7e347 dec/dsp: disable sse2 for Visual Studio builds +e4d540c8 add SSE2 code for transform +54f2170a swig/java: basic encode support +c5d4584b call function pointers instead of C-version +ea43f045 Merge "configure: mingw32 targets: test for WIC support" +a11009d7 SSE2 version of simple in-loop filtering +42548da9 shave one unneeded filter-cache line +31f9dc6f configure: mingw32 targets: test for WIC support +19559699 Merge "split expression in two." +415dbe46 split expression in two. +e29072a8 configure: test for zlib only w/--enable-experimental +b2b0090b Simplify Visual Studio ifdefs +ca7a2fd6 Add error reporting from encoding failures. +6c9405db Merge "Makefile.vc: require CFG with clean target" +0424ecd9 Makefile.vc: require CFG with clean target +003417c7 Enable SSE2 for Visual Studio builds +af10db4a little speed up for VP8BitUpdate() +e71418f8 more MSVC files to ignore +46d90363 cosmetics +edf59ab3 typo fix +72229f5f Add support for x64 and SSE2 builds under Windows. +92e5c6e1 VP8GetInfo() + WebPResetDecParams() +416b7a6b raise the fixed-point precision for the rescaler +aa87e4e0 fix alignment +eb66670c disable WEBP_EXPERIMENTAL_FEATURES +c5ae7f65 typo fix: USE_ => WEBP_ +d041efae swig: add libwebp.jar/libwebp_java_wrap.c +f6fb3877 add swig interface +e9273902 align buffer for double too +842c009b fix -strong option +d0a70387 Merge "cosmetics" +fc0a02e5 fix the dichotomy loop +38369c03 cosmetics +8dfc4c6f factorize and unify GetAlpha() between the C and SSE2 version +6d0e66c2 prepare experimentation with yuv444 / 422 +79cc49f5 add a --enable-experimental option to './configure' +d7575238 sse2 version of CollectHistogram() +c1c728d6 add an extra #ifdef WEBP_EXPERIMENTAL_FEATURES to avoid 'unused variable' warning +60c61d2d always call VP*EncDeleteAlpha() unconditionnally, for simplicity +0f8c6384 simply don't call WriteExtensions() if WEBP_EXPERIMENTAL_FEATURES is not defined +47c661d5 rename swap -> swap_rb +10d55bbb move chunk[] declaration out of the for() loop +517cec21 fix indentation +f7d9e261 fix merge problems +8fd42b3a add a stride 'a_stride' for the alpha plane +b8dcbf2f fix alpha-plane copy and crop methods +cdef89de fix some 'unused variable' warning +fb29c262 SSE2 version of the fwd transform and the squared sum metric +2ab4b72f EXPERIMENTAL: add support for alpha channel +cfbf88a6 add SSE2 functions. ~2x faster encoding on average. +e7ff3f9a merge two ITransforms together when applicable and change the TTransform to return the sum directly. +ca554137 fix WebPIDecGetRGB() to accept any RGB(A) mode, not just MODE_RGB +8aa50efd fix some 'man' typos +d3f3bdda update ChangeLog (tag: v0.1.2) +d7e9a69c update contributor list +261abb8e add a 'superclean' section +276ae825 Remove files not mean to be in git, and update .gitignore +24868455 build: prepare libwebp.pc +14ceb6e8 add "-version" description to man pages +b247a3b2 Create the m4 directory, and also place .gitignore in there for libtool. +cdd734c9 Resolve automake warnings +c5fa726e build: add pkgconfig files +b20aaca2 build: just use autoreconf, avoid calling tools manually +4b0b0d66 cwebp: use modern functions +efbc6c41 update Android.mk +7777570b better version of ChangeLog +fa70d2b7 update version number in the DOC +f8db5d5d more C89-fixes +0de013b3 fix typos +650ffa3b add version getters for decoder and encoder +be4867d2 doc for incremental decoding +56732a1b add idec.obj in MSVC makefile +208afb5e add c++ guards +8bf76fe0 add incremental decoding +1f288328 'inline' isn't defined in strict ansi c89 +8b77c632 move the quantization function to dsp.c +b2c3575c add a 'last_y' field to WebPDecParams +2654c3da correctly pass along the exact same status returned from ParsePartitions +4704146a add missing precision in the man +6d978a6c add error messages +6463e6ab add some install instructions, and fix intel-mac flags +05fb7bfc Merge ".gitignore: initial version" +c33f0195 .gitignore: initial version +e532b9ab Makefile: allow out of tree builds +4c0da7aa enable sparse dc/ac transforms +07dbb8d5 clarify the return logic +5c69e1bb fix bigger-by-1 array +7c5267e3 fix a (harmless) typo: non_zero_ -> non_zero_ac_ +bc752135 fix missing free() +af3e2aaa remove trailing spaces +13e50da6 make the bitreader preload at least 8bits, instead of post-load them (this makes initialization easier and will be helpful for incremental decoding). Modify ParsePartitions() to accommodate for truncated input. +f4888f77 emit 9 - nb_bits trailing zeros instead of 8 +3db65255 separate block-parsing into a visible VP8DecodeMB() +a871de02 add missing extern "C" +b3ce8c52 remove a gcc warning about type pun by using a proper union'd type +e1863715 update after addition of webpi.h +3e856e2d Extract some useful functions around decoding buffer WebPDecParams. +d5bc05a4 make the filtering process match libvpx and ffvp8 +dd60138d add man pages for cwebp(1) and dwebp(1) +c4fa3644 fix header +5b70b378 * add an option to bypass_filtering in VP8Io. +b97a4003 simplify QuantizeBlock code a bit +84b58ebb add more checks around picture allocation +b65a3e10 remove absolute_delta_ field and syntax code +0744e842 Dont' open output file until we're sure the input file is valid +d5bd54c7 fix typo and buggy line +f7a9549d Add a simple top-level makefile.unix for quick & easy build. +5f36b944 update the doc for the -f option +f61d14aa a WebP encoder converts PNG & JPEG to WebP +81c96621 oops: forgotten call to Initialize() + move the error message to a more useful place +87ffa005 typo: fix a missing 'R', was confusing. +b04b857a * add decoding measurement using stopwatch.h (use -v option) * support PNG output through WIC on Win32 +746a4820 * make (*put)() hook return a bool for abort request. * add an enum for VP8Status() to make things clearer +73c973e6 * strengthen riff/chunk size checks * don't consider odd-sized chunks being an error +1dc4611a add support for PNG output (default) regularize include guards +860641df fix a typo: sizeof(kYModeProbaInter0) => sizeof(kUVModeProbaInter0) +3254fc52 fix some petty constness fix the ./configure file too +504d3393 fix eof_ mis-initialization +2bc0778f leftover Makefile.* from previous commit +d2cf04e4 move Makefile.am one level below, to src/dec fix typos here and there dwebp is now an installed program +ade92de8 typo: vp8.h -> decode_vp8.h +d7241241 forgot to declare types.h to be installed +6421a7a4 move the decoder sourcetree to a sub-location src/dec to make room for future libs sources +a9b3eab6 correct layout name is IMC4. +2330522c handle corner case of zero-dimensions +280c3658 make VP8Init() handle short buffers (< 2 bytes) correctly +b1c9e8b4 handle error cases more robustly +0e94935c Merge "table-less version of clip_8b()" +1e0a2d25 table-less version of clip_8b() +e12109ee dwebp: change -yuv option to -raw change the layout to IMC2 +d72180a4 speed-up fancy upscaler +9145f3bc reset eof_ at construction time +a7ee0559 simplify the logic of GetCoeffs() +f67b5939 lot of cosmetics +ea27d7c6 fix endian problem on PowerPC +beb0a1ba fix signature of VP8StoreBlock +b128c5e2 Merge "fancy chroma upscaling" +6a37a2aa fancy chroma upscaling +ff565edc fix two numeric typos +5a936a0a use uintptr_t for casting pointers to ints +e14a0301 for cross_compiling=yes to prevent executing any binary +83b545ee add vc9+ makefile +296f6914 fix output loop for small height +cbfbb5c3 convert to plain-C +f09f96ee Fix declaration after statement warning +5981ee55 Fix UV plane ac/dc quantizer transposition +c8d15efa convert to ANSI-C +c3f41cb4 Initial commit diff --git a/internal/libwebp-1.2.1/Makefile.am b/internal/libwebp-1.2.1/Makefile.am new file mode 100644 index 0000000..e415f6f --- /dev/null +++ b/internal/libwebp-1.2.1/Makefile.am @@ -0,0 +1,9 @@ +ACLOCAL_AMFLAGS = -I m4 +SUBDIRS = src imageio man +EXTRA_DIST = COPYING autogen.sh + +if BUILD_EXTRAS + SUBDIRS += extras +endif + +SUBDIRS += examples diff --git a/internal/libwebp-1.2.1/Makefile.vc b/internal/libwebp-1.2.1/Makefile.vc new file mode 100644 index 0000000..b9d631d --- /dev/null +++ b/internal/libwebp-1.2.1/Makefile.vc @@ -0,0 +1,523 @@ +# +# Stem for static libs and DLLs +# +LIBWEBPDECODER_BASENAME = libwebpdecoder +LIBWEBP_BASENAME = libwebp +LIBWEBPMUX_BASENAME = libwebpmux +LIBWEBPDEMUX_BASENAME = libwebpdemux + +!IFNDEF ARCH +!IF ! [ cl 2>&1 | find "x86" > NUL ] +ARCH = x86 +!ELSE IF ! [ cl 2>&1 | find "x64" > NUL ] +ARCH = x64 +!ELSE IF ! [ cl 2>&1 | find "ARM" > NUL ] +ARCH = ARM +!ELSE +!ERROR Unable to auto-detect toolchain architecture! \ +If cl.exe is in your PATH rerun nmake with ARCH=. +!ENDIF +!ENDIF + +!IF "$(ARCH)" == "x86" +PLATFORM_LDFLAGS = /SAFESEH +!ENDIF + +############################################################# +## Nothing more to do below this line! + +NOLOGO = /nologo +CCNODBG = cl.exe $(NOLOGO) /O2 /DNDEBUG +CCDEBUG = cl.exe $(NOLOGO) /Od /Zi /D_DEBUG /RTC1 +CFLAGS = /I. /Isrc $(NOLOGO) /W3 /EHsc /c +CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN +LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE +LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS) +LNKDLL = link.exe /DLL $(NOLOGO) +LNKEXE = link.exe $(NOLOGO) +LNKLIB = lib.exe $(NOLOGO) +MT = mt.exe $(NOLOGO) +RCNODBG = rc.exe $(NOLOGO) /l"0x0409" # 0x409 = U.S. English +RCDEBUG = $(RCNODBG) /D_DEBUG + +!IF "$(ARCH)" == "ARM" +CFLAGS = $(CFLAGS) /DWINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP /DWEBP_USE_THREAD +!ELSE +CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD +!ENDIF + +CFGSET = FALSE +!IF "$(OBJDIR)" == "" +OUTDIR = ..\obj\ +!ELSE +OUTDIR = $(OBJDIR) +!ENDIF + +############################################################## +# Runtime library configuration +!IF "$(RTLIBCFG)" == "static" +RTLIB = /MT +RTLIBD = /MTd +!ELSE IF "$(RTLIBCFG)" == "legacy" +RTLIBCFG = static +RTLIB = /MT +RTLIBD = /MTd +CFLAGS = $(CFLAGS) /GS- /arch:IA32 +!ELSE +RTLIB = /MD +RTLIBD = /MDd +!ENDIF +DIRBASE = $(OUTDIR)\$(CFG)\$(ARCH) +DIROBJ = $(DIRBASE)\obj +DIRLIB = $(DIRBASE)\lib +DIRINC = $(DIRBASE)\include +DIRBIN = $(DIRBASE)\bin +LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME).pdb +OUTPUT_DIRS = $(DIRBIN) $(DIRINC) $(DIRLIB) \ + $(DIROBJ)\dec \ + $(DIROBJ)\demux \ + $(DIROBJ)\dsp \ + $(DIROBJ)\enc \ + $(DIROBJ)\examples \ + $(DIROBJ)\extras \ + $(DIROBJ)\imageio \ + $(DIROBJ)\mux \ + $(DIROBJ)\utils \ + +# Target configuration +!IF "$(CFG)" == "release-static" +CC = $(CCNODBG) +STATICLIBBUILD = TRUE +!ELSE IF "$(CFG)" == "debug-static" +CC = $(CCDEBUG) +RTLIB = $(RTLIBD) +STATICLIBBUILD = TRUE +LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug +LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug +LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug +LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug +!ELSE IF "$(CFG)" == "release-dynamic" +CC = $(CCNODBG) +RC = $(RCNODBG) +DLLBUILD = TRUE +!ELSE IF "$(CFG)" == "debug-dynamic" +CC = $(CCDEBUG) +RC = $(RCDEBUG) +RTLIB = $(RTLIBD) +DLLBUILD = TRUE +LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug +LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug +LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug +LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug +!ENDIF + +!IF "$(STATICLIBBUILD)" == "TRUE" +CC = $(CC) $(RTLIB) +CFGSET = TRUE +LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME).lib +LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME).lib +LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib +LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME).lib +!ELSE IF "$(DLLBUILD)" == "TRUE" +DLLINC = webp_dll.h +CC = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(RTLIB) /DWEBP_DLL +LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME)_dll.lib +LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME)_dll.lib +LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME)_dll.lib +LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME)_dll.lib +LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME)_dll.pdb +CFGSET = TRUE +!ENDIF + +!IF "$(UNICODE)" == "1" +CFLAGS = $(CFLAGS) /D_UNICODE /DUNICODE +!ENDIF + +####################### +# Usage +# +!IF "$(CFGSET)" == "FALSE" +!MESSAGE Usage: nmake /f Makefile.vc [CFG=] +!MESSAGE . [OBJDIR=] [RTLIBCFG=] [UNICODE=1] [] +!MESSAGE +!MESSAGE where is one of: +!MESSAGE - release-static - release static library +!MESSAGE - debug-static - debug static library +!MESSAGE - release-dynamic - release dynamic link library (DLL) +!MESSAGE - debug-dynamic - debug dynamic link library (DLL) +!MESSAGE +!MESSAGE may be: +!MESSAGE - clean - perform a clean for CFG +!MESSAGE - experimental - build CFG with experimental +!MESSAGE . features enabled. +!MESSAGE - (empty) - build libwebp-based targets for CFG +!MESSAGE - all - build (de)mux-based targets for CFG +!MESSAGE - gif2webp - requires libgif & >= VS2013 +!MESSAGE - anim_diff - requires libgif & >= VS2013 +!MESSAGE - anim_dump +!MESSAGE +!MESSAGE RTLIBCFG controls the runtime library linkage - 'static' or 'dynamic'. +!MESSAGE 'legacy' will produce a Windows 2000 compatible library. +!MESSAGE OBJDIR is the path where you like to build (obj, bins, etc.), +!MESSAGE defaults to ..\obj + +!IF "$(CFG)" != "" +!MESSAGE +!ERROR please choose a valid configuration instead of "$(CFG)" +!ENDIF +!ENDIF + +####################### +# Rules +# +!IF "$(CFGSET)" == "TRUE" +# A config was provided, so the library can be built. +# + +DEC_OBJS = \ + $(DIROBJ)\dec\alpha_dec.obj \ + $(DIROBJ)\dec\buffer_dec.obj \ + $(DIROBJ)\dec\frame_dec.obj \ + $(DIROBJ)\dec\idec_dec.obj \ + $(DIROBJ)\dec\io_dec.obj \ + $(DIROBJ)\dec\quant_dec.obj \ + $(DIROBJ)\dec\tree_dec.obj \ + $(DIROBJ)\dec\vp8_dec.obj \ + $(DIROBJ)\dec\vp8l_dec.obj \ + $(DIROBJ)\dec\webp_dec.obj \ + +DEMUX_OBJS = \ + $(DIROBJ)\demux\anim_decode.obj \ + $(DIROBJ)\demux\demux.obj \ + +DSP_DEC_OBJS = \ + $(DIROBJ)\dsp\alpha_processing.obj \ + $(DIROBJ)\dsp\alpha_processing_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\alpha_processing_neon.obj \ + $(DIROBJ)\dsp\alpha_processing_sse2.obj \ + $(DIROBJ)\dsp\alpha_processing_sse41.obj \ + $(DIROBJ)\dsp\cpu.obj \ + $(DIROBJ)\dsp\dec.obj \ + $(DIROBJ)\dsp\dec_clip_tables.obj \ + $(DIROBJ)\dsp\dec_mips32.obj \ + $(DIROBJ)\dsp\dec_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\dec_msa.obj \ + $(DIROBJ)\dsp\dec_neon.obj \ + $(DIROBJ)\dsp\dec_sse2.obj \ + $(DIROBJ)\dsp\dec_sse41.obj \ + $(DIROBJ)\dsp\filters.obj \ + $(DIROBJ)\dsp\filters_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\filters_msa.obj \ + $(DIROBJ)\dsp\filters_neon.obj \ + $(DIROBJ)\dsp\filters_sse2.obj \ + $(DIROBJ)\dsp\lossless.obj \ + $(DIROBJ)\dsp\lossless_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\lossless_msa.obj \ + $(DIROBJ)\dsp\lossless_neon.obj \ + $(DIROBJ)\dsp\lossless_sse2.obj \ + $(DIROBJ)\dsp\lossless_sse41.obj \ + $(DIROBJ)\dsp\rescaler.obj \ + $(DIROBJ)\dsp\rescaler_mips32.obj \ + $(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\rescaler_msa.obj \ + $(DIROBJ)\dsp\rescaler_neon.obj \ + $(DIROBJ)\dsp\rescaler_sse2.obj \ + $(DIROBJ)\dsp\upsampling.obj \ + $(DIROBJ)\dsp\upsampling_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\upsampling_msa.obj \ + $(DIROBJ)\dsp\upsampling_neon.obj \ + $(DIROBJ)\dsp\upsampling_sse2.obj \ + $(DIROBJ)\dsp\upsampling_sse41.obj \ + $(DIROBJ)\dsp\yuv.obj \ + $(DIROBJ)\dsp\yuv_mips32.obj \ + $(DIROBJ)\dsp\yuv_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\yuv_neon.obj \ + $(DIROBJ)\dsp\yuv_sse2.obj \ + $(DIROBJ)\dsp\yuv_sse41.obj \ + +DSP_ENC_OBJS = \ + $(DIROBJ)\dsp\cost.obj \ + $(DIROBJ)\dsp\cost_mips32.obj \ + $(DIROBJ)\dsp\cost_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\cost_neon.obj \ + $(DIROBJ)\dsp\cost_sse2.obj \ + $(DIROBJ)\dsp\enc.obj \ + $(DIROBJ)\dsp\enc_mips32.obj \ + $(DIROBJ)\dsp\enc_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\enc_msa.obj \ + $(DIROBJ)\dsp\enc_neon.obj \ + $(DIROBJ)\dsp\enc_sse2.obj \ + $(DIROBJ)\dsp\enc_sse41.obj \ + $(DIROBJ)\dsp\lossless_enc.obj \ + $(DIROBJ)\dsp\lossless_enc_mips32.obj \ + $(DIROBJ)\dsp\lossless_enc_mips_dsp_r2.obj \ + $(DIROBJ)\dsp\lossless_enc_msa.obj \ + $(DIROBJ)\dsp\lossless_enc_neon.obj \ + $(DIROBJ)\dsp\lossless_enc_sse2.obj \ + $(DIROBJ)\dsp\lossless_enc_sse41.obj \ + $(DIROBJ)\dsp\ssim.obj \ + $(DIROBJ)\dsp\ssim_sse2.obj \ + +EX_ANIM_UTIL_OBJS = \ + $(DIROBJ)\examples\anim_util.obj \ + +IMAGEIO_DEC_OBJS = \ + $(DIROBJ)\imageio\image_dec.obj \ + $(DIROBJ)\imageio\jpegdec.obj \ + $(DIROBJ)\imageio\metadata.obj \ + $(DIROBJ)\imageio\pngdec.obj \ + $(DIROBJ)\imageio\pnmdec.obj \ + $(DIROBJ)\imageio\tiffdec.obj \ + $(DIROBJ)\imageio\webpdec.obj \ + $(DIROBJ)\imageio\wicdec.obj \ + +IMAGEIO_ENC_OBJS = \ + $(DIROBJ)\imageio\image_enc.obj \ + +EX_GIF_DEC_OBJS = \ + $(DIROBJ)\examples\gifdec.obj \ + +EX_UTIL_OBJS = \ + $(DIROBJ)\examples\example_util.obj \ + +ENC_OBJS = \ + $(DIROBJ)\enc\alpha_enc.obj \ + $(DIROBJ)\enc\analysis_enc.obj \ + $(DIROBJ)\enc\backward_references_cost_enc.obj \ + $(DIROBJ)\enc\backward_references_enc.obj \ + $(DIROBJ)\enc\config_enc.obj \ + $(DIROBJ)\enc\cost_enc.obj \ + $(DIROBJ)\enc\filter_enc.obj \ + $(DIROBJ)\enc\frame_enc.obj \ + $(DIROBJ)\enc\histogram_enc.obj \ + $(DIROBJ)\enc\iterator_enc.obj \ + $(DIROBJ)\enc\near_lossless_enc.obj \ + $(DIROBJ)\enc\picture_enc.obj \ + $(DIROBJ)\enc\picture_csp_enc.obj \ + $(DIROBJ)\enc\picture_psnr_enc.obj \ + $(DIROBJ)\enc\picture_rescale_enc.obj \ + $(DIROBJ)\enc\picture_tools_enc.obj \ + $(DIROBJ)\enc\predictor_enc.obj \ + $(DIROBJ)\enc\quant_enc.obj \ + $(DIROBJ)\enc\syntax_enc.obj \ + $(DIROBJ)\enc\token_enc.obj \ + $(DIROBJ)\enc\tree_enc.obj \ + $(DIROBJ)\enc\vp8l_enc.obj \ + $(DIROBJ)\enc\webp_enc.obj \ + +EXTRAS_OBJS = \ + $(DIROBJ)\extras\extras.obj \ + $(DIROBJ)\extras\quality_estimate.obj \ + +IMAGEIO_UTIL_OBJS = \ + $(DIROBJ)\imageio\imageio_util.obj \ + +MUX_OBJS = \ + $(DIROBJ)\mux\anim_encode.obj \ + $(DIROBJ)\mux\muxedit.obj \ + $(DIROBJ)\mux\muxinternal.obj \ + $(DIROBJ)\mux\muxread.obj \ + +UTILS_DEC_OBJS = \ + $(DIROBJ)\utils\bit_reader_utils.obj \ + $(DIROBJ)\utils\color_cache_utils.obj \ + $(DIROBJ)\utils\filters_utils.obj \ + $(DIROBJ)\utils\huffman_utils.obj \ + $(DIROBJ)\utils\quant_levels_dec_utils.obj \ + $(DIROBJ)\utils\rescaler_utils.obj \ + $(DIROBJ)\utils\random_utils.obj \ + $(DIROBJ)\utils\thread_utils.obj \ + $(DIROBJ)\utils\utils.obj \ + +UTILS_ENC_OBJS = \ + $(DIROBJ)\utils\bit_writer_utils.obj \ + $(DIROBJ)\utils\huffman_encode_utils.obj \ + $(DIROBJ)\utils\quant_levels_utils.obj \ + +LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS) +LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \ + $(UTILS_ENC_OBJS) $(DLL_OBJS) +LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS) +LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) $(LIBWEBPDEMUX_OBJS) + +OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP) +!IF "$(ARCH)" == "ARM" +ex: $(OUT_LIBS) +all: ex +!ELSE +OUT_EXAMPLES = $(DIRBIN)\cwebp.exe $(DIRBIN)\dwebp.exe +EXTRA_EXAMPLES = $(DIRBIN)\vwebp.exe $(DIRBIN)\webpmux.exe \ + $(DIRBIN)\img2webp.exe $(DIRBIN)\get_disto.exe \ + $(DIRBIN)\webp_quality.exe $(DIRBIN)\vwebp_sdl.exe \ + $(DIRBIN)\webpinfo.exe + +ex: $(OUT_LIBS) $(OUT_EXAMPLES) +all: ex $(EXTRA_EXAMPLES) +# NB: gif2webp.exe and anim_diff.exe are excluded from 'all' as libgif requires +# C99 support which is only available from VS2013 onward. +gif2webp: $(DIRBIN)\gif2webp.exe +anim_diff: $(DIRBIN)\anim_diff.exe +anim_dump: $(DIRBIN)\anim_dump.exe + +$(DIRBIN)\anim_diff.exe: $(DIROBJ)\examples\anim_diff.obj $(EX_ANIM_UTIL_OBJS) +$(DIRBIN)\anim_diff.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) +$(DIRBIN)\anim_diff.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP) +$(DIRBIN)\anim_dump.exe: $(DIROBJ)\examples\anim_dump.obj $(EX_ANIM_UTIL_OBJS) +$(DIRBIN)\anim_dump.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) +$(DIRBIN)\anim_dump.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP) +$(DIRBIN)\anim_dump.exe: $(IMAGEIO_ENC_OBJS) +$(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(IMAGEIO_DEC_OBJS) +$(DIRBIN)\cwebp.exe: $(IMAGEIO_UTIL_OBJS) +$(DIRBIN)\cwebp.exe: $(LIBWEBPDEMUX) +$(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj $(IMAGEIO_DEC_OBJS) +$(DIRBIN)\dwebp.exe: $(IMAGEIO_ENC_OBJS) +$(DIRBIN)\dwebp.exe: $(IMAGEIO_UTIL_OBJS) +$(DIRBIN)\dwebp.exe: $(LIBWEBPDEMUX) +$(DIRBIN)\gif2webp.exe: $(DIROBJ)\examples\gif2webp.obj $(EX_GIF_DEC_OBJS) +$(DIRBIN)\gif2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBPMUX) +$(DIRBIN)\gif2webp.exe: $(LIBWEBP) +$(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj $(EX_UTIL_OBJS) +$(DIRBIN)\vwebp.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP) +$(DIRBIN)\vwebp_sdl.exe: $(DIROBJ)\extras\vwebp_sdl.obj +$(DIRBIN)\vwebp_sdl.exe: $(DIROBJ)\extras\webp_to_sdl.obj +$(DIRBIN)\vwebp_sdl.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBP) +$(DIRBIN)\webpmux.exe: $(DIROBJ)\examples\webpmux.obj $(LIBWEBPMUX) +$(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP) +$(DIRBIN)\img2webp.exe: $(DIROBJ)\examples\img2webp.obj $(LIBWEBPMUX) +$(DIRBIN)\img2webp.exe: $(IMAGEIO_DEC_OBJS) +$(DIRBIN)\img2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) +$(DIRBIN)\img2webp.exe: $(LIBWEBPDEMUX) $(LIBWEBP) +$(DIRBIN)\get_disto.exe: $(DIROBJ)\extras\get_disto.obj +$(DIRBIN)\get_disto.exe: $(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS) +$(DIRBIN)\get_disto.exe: $(LIBWEBPDEMUX) $(LIBWEBP) +$(DIRBIN)\webp_quality.exe: $(DIROBJ)\extras\webp_quality.obj +$(DIRBIN)\webp_quality.exe: $(IMAGEIO_UTIL_OBJS) +$(DIRBIN)\webp_quality.exe: $(EXTRAS_OBJS) +# EXTRA_OBJS requires private symbols from dsp. Explicitly add those when +# building libwebp as a dll. +!IF "$(DLLBUILD)" == "TRUE" +$(DIRBIN)\webp_quality.exe: $(DSP_DEC_OBJS) +!ENDIF +$(DIRBIN)\webp_quality.exe: $(LIBWEBP) +$(DIRBIN)\webpinfo.exe: $(DIROBJ)\examples\webpinfo.obj +$(DIRBIN)\webpinfo.exe: $(IMAGEIO_DEC_OBJS) +$(DIRBIN)\webpinfo.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) +$(DIRBIN)\webpinfo.exe: $(LIBWEBPDEMUX) $(LIBWEBP) + +$(OUT_EXAMPLES): $(EX_UTIL_OBJS) $(LIBWEBP) +$(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS): $(OUTPUT_DIRS) +$(IMAGEIO_DEC_OBJS) $(IMAGEIO_ENC_OBJS) $(EXTRAS_OBJS): $(OUTPUT_DIRS) +!ENDIF # ARCH == ARM + +$(LIBWEBPDECODER): $(LIBWEBPDECODER_OBJS) +$(LIBWEBP): $(LIBWEBP_OBJS) +$(LIBWEBPMUX): $(LIBWEBPMUX_OBJS) +$(LIBWEBPDEMUX): $(LIBWEBPDEMUX_OBJS) + +$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): $(OUTPUT_DIRS) + +!IF "$(DLLBUILD)" == "TRUE" +$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): \ + $(DIROBJ)\$(DLLINC) + +{$(DIROBJ)}.c{$(DIROBJ)}.obj: + $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@ $< + +{src}.rc{$(DIROBJ)}.res: + $(RC) /fo$@ $< +{src\demux}.rc{$(DIROBJ)\demux}.res: + $(RC) /fo$@ $< +{src\mux}.rc{$(DIROBJ)\mux}.res: + $(RC) /fo$@ $< + +$(LIBWEBP): $(DIROBJ)\$(LIBWEBP_BASENAME:_debug=).res +$(LIBWEBPDECODER): $(DIROBJ)\$(LIBWEBPDECODER_BASENAME:_debug=).res +$(LIBWEBPMUX): $(LIBWEBP) $(DIROBJ)\mux\$(LIBWEBPMUX_BASENAME:_debug=).res +$(LIBWEBPDEMUX): $(LIBWEBP) $(DIROBJ)\demux\$(LIBWEBPDEMUX_BASENAME:_debug=).res + +$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX): + $(LNKDLL) /out:$(DIRBIN)\$(@B:_dll=.dll) /implib:$@ $(LFLAGS) $** + -xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y + +clean:: + @-erase /s $(DIROBJ)\$(DLLINC) 2> NUL +!ELSE +$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX): + $(LNKLIB) /out:$@ $** + -xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y +!ENDIF + +$(OUTPUT_DIRS): + @if not exist "$(@)" mkdir "$(@)" + +# generate a helper include to define WEBP_EXTERN suitable for the DLL build +$(DIROBJ)\$(DLLINC): + @echo #ifndef WEBP_DLL_H_ > $@ + @echo #define WEBP_DLL_H_ >> $@ + @echo #define WEBP_EXTERN __declspec(dllexport) >> $@ + @echo #endif /* WEBP_DLL_H_ */ >> $@ + +.SUFFIXES: .c .obj .res .exe +# File-specific flag builds. Note batch rules take precedence over wildcards, +# so for now name each file individually. +$(DIROBJ)\examples\anim_diff.obj: examples\anim_diff.c + $(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ + /Fo$(DIROBJ)\examples\ examples\$(@B).c +$(DIROBJ)\examples\anim_dump.obj: examples\anim_dump.c + $(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ + /Fo$(DIROBJ)\examples\ examples\$(@B).c +$(DIROBJ)\examples\anim_util.obj: examples\anim_util.c + $(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ + /Fo$(DIROBJ)\examples\ examples\$(@B).c +$(DIROBJ)\examples\gif2webp.obj: examples\gif2webp.c + $(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ + /Fo$(DIROBJ)\examples\ examples\$(@B).c +$(DIROBJ)\examples\gifdec.obj: examples\gifdec.c + $(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \ + /Fo$(DIROBJ)\examples\ examples\$(@B).c +# Batch rules +{examples}.c{$(DIROBJ)\examples}.obj:: + $(CC) $(CFLAGS) /Fd$(DIROBJ)\examples\ /Fo$(DIROBJ)\examples\ $< +{extras}.c{$(DIROBJ)\extras}.obj:: + $(CC) $(CFLAGS) /Fd$(DIROBJ)\extras\ /Fo$(DIROBJ)\extras\ $< +{imageio}.c{$(DIROBJ)\imageio}.obj:: + $(CC) $(CFLAGS) /Fd$(DIROBJ)\imageio\ /Fo$(DIROBJ)\imageio\ $< +{src\dec}.c{$(DIROBJ)\dec}.obj:: + $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $< +{src\demux}.c{$(DIROBJ)\demux}.obj:: + $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\demux\ $< +{src\dsp}.c{$(DIROBJ)\dsp}.obj:: + $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ $< +{src\enc}.c{$(DIROBJ)\enc}.obj:: + $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\enc\ $< +{src\mux}.c{$(DIROBJ)\mux}.obj:: + $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\mux\ $< +{src\utils}.c{$(DIROBJ)\utils}.obj:: + $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\utils\ $< + +LNKLIBS = ole32.lib windowscodecs.lib shlwapi.lib +!IF "$(UNICODE)" == "1" +LNKLIBS = $(LNKLIBS) Shell32.lib +!ENDIF + +{$(DIROBJ)\examples}.obj{$(DIRBIN)}.exe: + $(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS) + $(MT) -manifest $@.manifest -outputresource:$@;1 + del $@.manifest + +{$(DIROBJ)\extras}.obj{$(DIRBIN)}.exe: + $(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS) + $(MT) -manifest $@.manifest -outputresource:$@;1 + del $@.manifest + +clean:: + @-erase /s $(DIROBJ)\*.dll 2> NUL + @-erase /s $(DIROBJ)\*.exp 2> NUL + @-erase /s $(DIROBJ)\*.idb 2> NUL + @-erase /s $(DIROBJ)\*.lib 2> NUL + @-erase /s $(DIROBJ)\*.obj 2> NUL + @-erase /s $(DIROBJ)\*.pch 2> NUL + @-erase /s $(DIROBJ)\*.pdb 2> NUL + @-erase /s $(DIROBJ)\*.res 2> NUL + +!ENDIF # End of case where a config was provided. diff --git a/internal/libwebp-1.2.1/NEWS b/internal/libwebp-1.2.1/NEWS new file mode 100644 index 0000000..a691761 --- /dev/null +++ b/internal/libwebp-1.2.1/NEWS @@ -0,0 +1,266 @@ +- 7/20/2021: version 1.2.1 + This is a binary compatible release. + * minor lossless encoder improvements and x86 color conversion speed up + * add ARM64 simulator support to xcframeworkbuild.sh (#510) + * further security related hardening in libwebp & examples + (issues: #497, #508, #518) + (chromium: #1196480, #1196773, #1196775, #1196777, #1196778, #1196850) + (oss-fuzz: #28658, #28978) + * toolchain updates and bug fixes (#498, #501, #502, #504, #505, #506, #509, + #533) + * use more inclusive language within the source (#507) + +- 12/23/2020: version 1.2.0 + * API changes: + - libwebp: + encode.h: add a qmin / qmax range for quality factor (cwebp adds -qrange) + * lossless encoder improvements + * SIMD support for Wasm builds + * add xcframeworkbuild.sh, supports Mac Catalyst builds + * import fuzzers from oss-fuzz & chromium (#409) + * webpmux: add an '-set loop ' option (#494) + * toolchain updates and bug fixes (#449, #463, #470, #475, #477, #478, #479, + #488, #491) + +- 12/18/2019: version 1.1.0 + * API changes: + - libwebp: + WebPMalloc (issue #442) + - extras: + WebPUnmultiplyARGB + * alpha decode fix (issue #439) + * toolchain updates and bug fixes + (chromium: #1026858, #1027136, #1027409, #1028620, #1028716, #995200) + (oss-fuzz: #19430, #19447) + +- 7/4/2019: version 1.0.3 + This is a binary compatible release. + * resize fixes for Nx1 sizes and the addition of non-opaque alpha values for + odd sizes (issues #418, #434) + * lossless encode/decode performance improvements + * lossy compression performance improvement at low quality levels with flat + content (issue #432) + * python swig files updated to support python 3 + Tool updates: + vwebp will now preserve the aspect ratio of images that exceed monitor + resolution by scaling the image to fit (issue #433) + +- 1/14/2019: version 1.0.2 + This is a binary compatible release. + * (Windows) unicode file support in the tools (linux and mac already had + support, issue #398) + * lossless encoder speedups + * lossy encoder speedup on ARM + * lossless multi-threaded security fix (chromium:917029) + +- 11/2/2018: version 1.0.1 + This is a binary compatible release. + * lossless encoder speedups + * big-endian fix for alpha decoding (issue #393) + * gif2webp fix for loop count=65535 transcode (issue #382) + * further security related hardening in libwebp & libwebpmux + (issues #383, #385, #386, #387, #388, #391) + (oss-fuzz #9099, #9100, #9105, #9106, #9111, #9112, #9119, #9123, #9170, + #9178, #9179, #9183, #9186, #9191, #9364, #9417, #9496, #10349, + #10423, #10634, #10700, #10838, #10922, #11021, #11088, #11152) + * miscellaneous bug & build fixes (issues #381, #394, #396, #397, #400) + +- 4/2/2018: version 1.0.0 + This is a binary compatible release. + * lossy encoder improvements to avoid chroma shifts in various circumstances + (issues #308, #340) + * big-endian fixes for decode, RGBA import and WebPPictureDistortion + Tool updates: + gifwebp, anim_diff - default duration behavior (<= 10ms) changed to match + web browsers, transcoding tools (issue #379) + img2webp, webpmux - allow options to be passed in via a file (issue #355) + +- 11/24/2017: version 0.6.1 + This is a binary compatible release. + * lossless performance and compression improvements + a new 'cruncher' mode + (-m 6 -q 100) + * ARM performance improvements with clang (15-20% w/ndk r15c, issue #339) + * webp-js: emscripten/webassembly based javascript decoder + * miscellaneous bug & build fixes (issue #329, #332, #343, #353, #360, #361, + #363) + Tool updates / additions: + added webpinfo - prints file format information (issue #330) + gif2webp - loop behavior modified to match Chrome M63+ (crbug.com/649264); + '-loop_compatibility' can be used for the old behavior + +- 1/26/2017: version 0.6.0 + * lossless performance and compression improvements + * miscellaneous performance improvements (SSE2, NEON, MSA) + * webpmux gained a -duration option allowing for frame timing modification + * new img2webp utility allowing a sequence of images to be converted to + animated webp + * API changes: + - libwebp: + WebPPictureSharpARGBToYUVA + WebPPlaneDistortion + - libwebpmux / gif2webp: + WebPAnimEncoderOptions: kmax <= 0 now disables keyframes, kmax == 1 + forces all keyframes. See mux.h and the gif2webp + manpage for details. + +- 12/13/2016: version 0.5.2 + This is a binary compatible release. + This release covers CVE-2016-8888 and CVE-2016-9085. + * further security related hardening in the tools; fixes to + gif2webp/AnimEncoder (issues #310, #314, #316, #322), cwebp/libwebp (issue + #312) + * full libwebp (encoder & decoder) iOS framework; libwebpdecoder + WebP.framework renamed to WebPDecoder.framework (issue #307) + * CMake support for Android Studio (2.2) + * miscellaneous build related fixes (issue #306, #313) + * miscellaneous documentation improvements (issue #225) + * minor lossy encoder fixes and improvements + +- 6/14/2016: version 0.5.1 + This is a binary compatible release. + * miscellaneous bug fixes (issues #280, #289) + * reverted alpha plane encoding with color cache for compatibility with + libwebp 0.4.0->0.4.3 (issues #291, #298) + * lossless encoding performance improvements + * memory reduction in both lossless encoding and decoding + * force mux output to be in the extended format (VP8X) when undefined chunks + are present (issue #294) + * gradle, cmake build support + * workaround for compiler bug causing 64-bit decode failures on android + devices using clang-3.8 in the r11c NDK + * various WebPAnimEncoder improvements + +- 12/17/2015: version 0.5.0 + * miscellaneous bug & build fixes (issues #234, #258, #274, #275, #278) + * encoder & decoder speed-ups on x86/ARM/MIPS for lossy & lossless + - note! YUV->RGB conversion was sped-up, but the results will be slightly + different from previous releases + * various lossless encoder improvements + * gif2webp improvements, -min_size option added + * tools fully support input from stdin and output to stdout (issue #168) + * New WebPAnimEncoder API for creating animations + * New WebPAnimDecoder API for decoding animations + * other API changes: + - libwebp: + WebPPictureSmartARGBToYUVA() (-pre 4 in cwebp) + WebPConfig::exact (-exact in cwebp; -alpha_cleanup is now the default) + WebPConfig::near_lossless (-near_lossless in cwebp) + WebPFree() (free'ing webp allocated memory in other languages) + WebPConfigLosslessPreset() + WebPMemoryWriterClear() + - libwebpdemux: removed experimental fragment related fields and functions + - libwebpmux: WebPMuxSetCanvasSize() + * new libwebpextras library with some uncommon import functions: + WebPImportGray/WebPImportRGB565/WebPImportRGB4444 + +- 10/15/15: version 0.4.4 + This is a binary compatible release. + * rescaling out-of-bounds read fix (issue #254) + * various build fixes and improvements (issues #253, #259, #262, #267, #268) + * container documentation update + * gif2webp transparency fix (issue #245) + +- 3/3/15: version 0.4.3 + This is a binary compatible release. + * Android / gcc / iOS / MSVS build fixes and improvements + * lossless decode fix (issue #239 -- since 0.4.0) + * documentation / vwebp updates for animation + * multi-threading fix (issue #234) + +- 10/13/14: version 0.4.2 + This is a binary compatible release. + * Android / gcc build fixes + * (Windows) fix reading from stdin and writing to stdout + * gif2webp: miscellaneous fixes + * fix 'alpha-leak' with lossy compression (issue #220) + * the lossless bitstream spec has been amended to reflect the current code + +- 7/24/14: version 0.4.1 + This is a binary compatible release. + * AArch64 (arm64) & MIPS support/optimizations + * NEON assembly additions: + - ~25% faster lossy decode / encode (-m 4) + - ~10% faster lossless decode + - ~5-10% faster lossless encode (-m 3/4) + * dwebp/vwebp can read from stdin + * cwebp/gif2webp can write to stdout + * cwebp can read webp files; useful if storing sources as webp lossless + +- 12/19/13: version 0.4.0 + * improved gif2webp tool + * numerous fixes, compression improvement and speed-up + * dither option added to decoder (dwebp -dither 50 ...) + * improved multi-threaded modes (-mt option) + * improved filtering strength determination + * New function: WebPMuxGetCanvasSize + * BMP and TIFF format output added to 'dwebp' + * Significant memory reduction for decoding lossy images with alpha. + * Intertwined decoding of RGB and alpha for a shorter + time-to-first-decoded-pixel. + * WebPIterator has a new member 'has_alpha' denoting whether the frame + contains transparency. + * Container spec amended with new 'blending method' for animation. + +- 6/13/13: version 0.3.1 + This is a binary compatible release. + * Add incremental decoding support for images containing ALPH and ICCP chunks. + * Python bindings via swig for the simple encode/decode interfaces similar to + Java. + +- 3/20/13: version 0.3.0 + This is a binary compatible release. + * WebPINewRGB/WebPINewYUVA accept being passed a NULL output buffer + and will perform auto-allocation. + * default filter option is now '-strong -f 60' + * encoding speed-up for lossy methods 3 to 6 + * alpha encoding can be done in parallel to lossy using 'cwebp -mt ...' + * color profile, metadata (XMP/EXIF) and animation support finalized in the + container. + * various NEON assembly additions + Tool updates / additions: + * gif2webp added + * vwebp given color profile & animation support + * cwebp can preserve color profile / metadata with '-metadata' + +- 10/30/12: version 0.2.1 + * Various security related fixes + * cwebp.exe: fix import errors on Windows XP + * enable DLL builds for mingw targets + +- 8/3/12: version 0.2.0 + * Add support for ARGB -> YUVA conversion for lossless decoder + New functions: WebPINewYUVA, WebPIDecGetYUVA + * Add stats for lossless and alpha encoding + * Security related hardening: allocation and size checks + * Add PAM output support to dwebp + +- 7/19/12: version 0.1.99 + * This is a pre-release of 0.2.0, not an rc to allow for further + incompatible changes based on user feedback. + * Alpha channel encode/decode support. + * Lossless encoder/decoder. + * Add TIFF input support to cwebp. + Incompatible changes: + * The encode ABI has been modified to support alpha encoding. + * Deprecated function WebPINew() has been removed. + * Decode function signatures have changed to consistently use size_t over + int/uint32_t. + * decode_vp8.h is no longer installed system-wide. + * cwebp will encode the alpha channel if present. + +- 9/19/11: version 0.1.3 + * Advanced decoding APIs. + * On-the-fly cropping and rescaling of images. + * SSE2 instructions for decoding performance optimizations on x86 based + platforms. + * Support Multi-threaded decoding. + * 40% improvement in Decoding performance. + * Add support for RGB565, RGBA4444 & ARGB image colorspace. + * Better handling of large picture encoding. + +- 3/25/11: version 0.1.2 + * Incremental decoding: picture can be decoded byte-by-byte if needs be. + * lot of bug-fixes, consolidation and stabilization + +- 2/23/11: initial release of version 0.1, with the new encoder +- 9/30/10: initial release version with only the lightweight decoder diff --git a/internal/libwebp-1.2.1/PATENTS b/internal/libwebp-1.2.1/PATENTS new file mode 100644 index 0000000..caedf60 --- /dev/null +++ b/internal/libwebp-1.2.1/PATENTS @@ -0,0 +1,23 @@ +Additional IP Rights Grant (Patents) +------------------------------------ + +"These implementations" means the copyrightable works that implement the WebM +codecs distributed by Google as part of the WebM Project. + +Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, +royalty-free, irrevocable (except as stated in this section) patent license to +make, have made, use, offer to sell, sell, import, transfer, and otherwise +run, modify and propagate the contents of these implementations of WebM, where +such license applies only to those patent claims, both currently owned by +Google and acquired in the future, licensable by Google that are necessarily +infringed by these implementations of WebM. This grant does not include claims +that would be infringed only as a consequence of further modification of these +implementations. If you or your agent or exclusive licensee institute or order +or agree to the institution of patent litigation or any other patent +enforcement activity against any entity (including a cross-claim or +counterclaim in a lawsuit) alleging that any of these implementations of WebM +or any code incorporated within any of these implementations of WebM +constitute direct or contributory patent infringement, or inducement of +patent infringement, then any patent rights granted to you under this License +for these implementations of WebM shall terminate as of the date such +litigation is filed. diff --git a/internal/libwebp-1.2.1/PRESUBMIT.py b/internal/libwebp-1.2.1/PRESUBMIT.py new file mode 100644 index 0000000..df4c333 --- /dev/null +++ b/internal/libwebp-1.2.1/PRESUBMIT.py @@ -0,0 +1,183 @@ +# Copyright (c) 2021, Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of Google nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +"""Top-level presubmit script for libwebp. + +See https://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for +details on the presubmit API built into depot_tools. +""" + +import subprocess2 + +USE_PYTHON3 = True +_BASH_INDENTATION = "2" +_INCLUDE_BASH_FILES_ONLY = [r".*\.sh$"] +_INCLUDE_MAN_FILES_ONLY = [r"man/.+\.1$"] +_LIBWEBP_MAX_LINE_LENGTH = 80 + + +def _GetFilesToSkip(input_api): + return list(input_api.DEFAULT_FILES_TO_SKIP) + [ + r"swig/.*\.py$", + r"\.pylintrc$", + ] + + +def _RunManCmd(input_api, output_api, man_file): + """man command wrapper.""" + cmd = ["man", "--warnings", "-EUTF-8", "-l", "-Tutf8", "-Z", man_file] + name = "Check %s file." % man_file + start = input_api.time.time() + output, _ = subprocess2.communicate( + cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True) + duration = input_api.time.time() - start + if output[1]: + return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" % + (name, " ".join(cmd), duration, output[1])) + return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" % + (name, " ".join(cmd), duration)) + + +def _RunShellCheckCmd(input_api, output_api, bash_file): + """shellcheck command wrapper.""" + cmd = ["shellcheck", "-x", "-oall", "-sbash", bash_file] + name = "Check %s file." % bash_file + start = input_api.time.time() + output, rc = subprocess2.communicate( + cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True) + duration = input_api.time.time() - start + if rc == 0: + return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" % + (name, " ".join(cmd), duration)) + return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" % + (name, " ".join(cmd), duration, output[1])) + + +def _RunShfmtCheckCmd(input_api, output_api, bash_file): + """shfmt command wrapper.""" + cmd = [ + "shfmt", "-i", _BASH_INDENTATION, "-bn", "-ci", "-sr", "-kp", "-d", + bash_file + ] + name = "Check %s file." % bash_file + start = input_api.time.time() + output, rc = subprocess2.communicate( + cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True) + duration = input_api.time.time() - start + if rc == 0: + return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" % + (name, " ".join(cmd), duration)) + return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" % + (name, " ".join(cmd), duration, output[1])) + + +def _RunCmdOnCheckedFiles(input_api, output_api, run_cmd, files_to_check): + """Ensure that libwebp/ files are clean.""" + file_filter = lambda x: input_api.FilterSourceFile( + x, files_to_check=files_to_check, files_to_skip=None) + + affected_files = input_api.change.AffectedFiles(file_filter=file_filter) + results = [ + run_cmd(input_api, output_api, f.AbsoluteLocalPath()) + for f in affected_files + ] + return results + + +def _CommonChecks(input_api, output_api): + """Ensures this patch does not have trailing spaces, extra EOLs, + or long lines. + """ + results = [] + results.extend( + input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol( + input_api, output_api)) + results.extend( + input_api.canned_checks.CheckChangeHasNoTabs(input_api, output_api)) + results.extend( + input_api.canned_checks.CheckChangeHasNoStrayWhitespace( + input_api, output_api)) + + source_file_filter = lambda x: input_api.FilterSourceFile( + x, files_to_skip=_GetFilesToSkip(input_api)) + results.extend( + input_api.canned_checks.CheckLongLines( + input_api, + output_api, + maxlen=_LIBWEBP_MAX_LINE_LENGTH, + source_file_filter=source_file_filter)) + + results.extend( + input_api.canned_checks.CheckPatchFormatted( + input_api, + output_api, + check_clang_format=False, + check_python=True, + result_factory=output_api.PresubmitError)) + results.extend( + _RunCmdOnCheckedFiles(input_api, output_api, _RunManCmd, + _INCLUDE_MAN_FILES_ONLY)) + # Run pylint. + results.extend( + input_api.canned_checks.RunPylint( + input_api, + output_api, + files_to_skip=_GetFilesToSkip(input_api), + pylintrc=".pylintrc", + version="2.7")) + + # Binaries shellcheck and shfmt are not installed in depot_tools. + # Installation is needed + try: + subprocess2.communicate(["shellcheck", "--version"]) + results.extend( + _RunCmdOnCheckedFiles(input_api, output_api, _RunShellCheckCmd, + _INCLUDE_BASH_FILES_ONLY)) + print("shfmt") + subprocess2.communicate(["shfmt", "-version"]) + results.extend( + _RunCmdOnCheckedFiles(input_api, output_api, _RunShfmtCheckCmd, + _INCLUDE_BASH_FILES_ONLY)) + except OSError as os_error: + results.append( + output_api.PresubmitPromptWarning( + "%s\nPlease install missing binaries locally." % os_error.args[0])) + return results + + +def CheckChangeOnUpload(input_api, output_api): + results = [] + results.extend(_CommonChecks(input_api, output_api)) + return results + + +def CheckChangeOnCommit(input_api, output_api): + results = [] + results.extend(_CommonChecks(input_api, output_api)) + return results diff --git a/internal/libwebp-1.2.1/README b/internal/libwebp-1.2.1/README new file mode 100644 index 0000000..54c5175 --- /dev/null +++ b/internal/libwebp-1.2.1/README @@ -0,0 +1,795 @@ + __ __ ____ ____ ____ + / \\/ \/ _ \/ _ )/ _ \ + \ / __/ _ \ __/ + \__\__/\____/\_____/__/ ____ ___ + / _/ / \ \ / _ \/ _/ + / \_/ / / \ \ __/ \__ + \____/____/\_____/_____/____/v1.2.1 + +Description: +============ + +WebP codec: library to encode and decode images in WebP format. This package +contains the library that can be used in other programs to add WebP support, +as well as the command line tools 'cwebp' and 'dwebp'. + +See http://developers.google.com/speed/webp + +The latest source tree is available at +https://chromium.googlesource.com/webm/libwebp + +It is released under the same license as the WebM project. +See http://www.webmproject.org/license/software/ or the +"COPYING" file for details. An additional intellectual +property rights grant can be found in the file PATENTS. + +Building: +========= + +Windows build: +-------------- + +By running: + + nmake /f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output + +the directory output\release-static\(x64|x86)\bin will contain the tools +cwebp.exe and dwebp.exe. The directory output\release-static\(x64|x86)\lib will +contain the libwebp static library. +The target architecture (x86/x64) is detected by Makefile.vc from the Visual +Studio compiler (cl.exe) available in the system path. + +Unix build using makefile.unix: +------------------------------- + +On platforms with GNU tools installed (gcc and make), running + + make -f makefile.unix + +will build the binaries examples/cwebp and examples/dwebp, along +with the static library src/libwebp.a. No system-wide installation +is supplied, as this is a simple alternative to the full installation +system based on the autoconf tools (see below). +Please refer to makefile.unix for additional details and customizations. + +Using autoconf tools: +--------------------- +Prerequisites: +A compiler (e.g., gcc), make, autoconf, automake, libtool. +On a Debian-like system the following should install everything you need for a +minimal build: +$ sudo apt-get install gcc make autoconf automake libtool + +When building from git sources, you will need to run autogen.sh to generate the +configure script. + +./configure +make +make install + +should be all you need to have the following files + +/usr/local/include/webp/decode.h +/usr/local/include/webp/encode.h +/usr/local/include/webp/types.h +/usr/local/lib/libwebp.* +/usr/local/bin/cwebp +/usr/local/bin/dwebp + +installed. + +Note: A decode-only library, libwebpdecoder, is available using the +'--enable-libwebpdecoder' flag. The encode library is built separately and can +be installed independently using a minor modification in the corresponding +Makefile.am configure files (see comments there). See './configure --help' for +more options. + +Building for MIPS Linux: +------------------------ +MIPS Linux toolchain stable available releases can be found at: +https://community.imgtec.com/developers/mips/tools/codescape-mips-sdk/available-releases/ + +# Add toolchain to PATH +export PATH=$PATH:/path/to/toolchain/bin + +# 32-bit build for mips32r5 (p5600) +HOST=mips-mti-linux-gnu +MIPS_CFLAGS="-O3 -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 \ + -msched-weight -mload-store-pairs -fPIE" +MIPS_LDFLAGS="-mips32r5 -mabi=32 -mmsa -mfp64 -pie" + +# 64-bit build for mips64r6 (i6400) +HOST=mips-img-linux-gnu +MIPS_CFLAGS="-O3 -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 \ + -msched-weight -mload-store-pairs -fPIE" +MIPS_LDFLAGS="-mips64r6 -mabi=64 -mmsa -mfp64 -pie" + +./configure --host=${HOST} --build=`config.guess` \ + CC="${HOST}-gcc -EL" \ + CFLAGS="$MIPS_CFLAGS" \ + LDFLAGS="$MIPS_LDFLAGS" +make +make install + +CMake: +------ +With CMake, you can compile libwebp, cwebp, dwebp, gif2webp, img2webp, webpinfo +and the JS bindings. + +Prerequisites: +A compiler (e.g., gcc with autotools) and CMake. +On a Debian-like system the following should install everything you need for a +minimal build: +$ sudo apt-get install build-essential cmake + +When building from git sources, you will need to run cmake to generate the +makefiles. + +mkdir build && cd build && cmake ../ +make +make install + +If you also want any of the executables, you will need to enable them through +CMake, e.g.: + +cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../ + +or through your favorite interface (like ccmake or cmake-qt-gui). + +Use option -DWEBP_UNICODE=ON for Unicode support on Windows (with chcp 65001). + +Finally, once installed, you can also use WebP in your CMake project by doing: + +find_package(WebP) + +which will define the CMake variables WebP_INCLUDE_DIRS and WebP_LIBRARIES. + +Gradle: +------- +The support for Gradle is minimal: it only helps you compile libwebp, cwebp and +dwebp and webpmux_example. + +Prerequisites: +A compiler (e.g., gcc with autotools) and gradle. +On a Debian-like system the following should install everything you need for a +minimal build: +$ sudo apt-get install build-essential gradle + +When building from git sources, you will need to run the Gradle wrapper with the +appropriate target, e.g. : + +./gradlew buildAllExecutables + +SWIG bindings: +-------------- + +To generate language bindings from swig/libwebp.swig at least swig-1.3 +(http://www.swig.org) is required. + +Currently the following functions are mapped: +Decode: + WebPGetDecoderVersion + WebPGetInfo + WebPDecodeRGBA + WebPDecodeARGB + WebPDecodeBGRA + WebPDecodeBGR + WebPDecodeRGB + +Encode: + WebPGetEncoderVersion + WebPEncodeRGBA + WebPEncodeBGRA + WebPEncodeRGB + WebPEncodeBGR + WebPEncodeLosslessRGBA + WebPEncodeLosslessBGRA + WebPEncodeLosslessRGB + WebPEncodeLosslessBGR + +See swig/README for more detailed build instructions. + +Java bindings: + +To build the swig-generated JNI wrapper code at least JDK-1.5 (or equivalent) +is necessary for enum support. The output is intended to be a shared object / +DLL that can be loaded via System.loadLibrary("webp_jni"). + +Python bindings: + +To build the swig-generated Python extension code at least Python 2.6 is +required. Python < 2.6 may build with some minor changes to libwebp.swig or the +generated code, but is untested. + +Encoding tool: +============== + +The examples/ directory contains tools for encoding (cwebp) and +decoding (dwebp) images. + +The easiest use should look like: + cwebp input.png -q 80 -o output.webp +which will convert the input file to a WebP file using a quality factor of 80 +on a 0->100 scale (0 being the lowest quality, 100 being the best. Default +value is 75). +You might want to try the -lossless flag too, which will compress the source +(in RGBA format) without any loss. The -q quality parameter will in this case +control the amount of processing time spent trying to make the output file as +small as possible. + +A longer list of options is available using the -longhelp command line flag: + +> cwebp -longhelp +Usage: + cwebp [-preset <...>] [options] in_file [-o out_file] + +If input size (-s) for an image is not specified, it is +assumed to be a PNG, JPEG, TIFF or WebP file. +Note: Animated PNG and WebP files are not supported. + +Options: + -h / -help ............. short help + -H / -longhelp ......... long help + -q ............. quality factor (0:small..100:big), default=75 + -alpha_q ......... transparency-compression quality (0..100), + default=100 + -preset ....... preset setting, one of: + default, photo, picture, + drawing, icon, text + -preset must come first, as it overwrites other parameters + -z ............... activates lossless preset with given + level in [0:fast, ..., 9:slowest] + + -m ............... compression method (0=fast, 6=slowest), default=4 + -segments ........ number of segments to use (1..4), default=4 + -size ............ target size (in bytes) + -psnr .......... target PSNR (in dB. typically: 42) + + -s ......... input size (width x height) for YUV + -sns ............. spatial noise shaping (0:off, 100:max), default=50 + -f ............... filter strength (0=off..100), default=60 + -sharpness ....... filter sharpness (0:most .. 7:least sharp), default=0 + -strong ................ use strong filter instead of simple (default) + -nostrong .............. use simple filter instead of strong + -sharp_yuv ............. use sharper (and slower) RGB->YUV conversion + -partition_limit . limit quality to fit the 512k limit on + the first partition (0=no degradation ... 100=full) + -pass ............ analysis pass number (1..10) + -qrange .... specifies the permissible quality range + (default: 0 100) + -crop .. crop picture with the given rectangle + -resize ........ resize picture (after any cropping) + -mt .................... use multi-threading if available + -low_memory ............ reduce memory usage (slower encoding) + -map ............. print map of extra info + -print_psnr ............ prints averaged PSNR distortion + -print_ssim ............ prints averaged SSIM distortion + -print_lsim ............ prints local-similarity distortion + -d .......... dump the compressed output (PGM file) + -alpha_method .... transparency-compression method (0..1), default=1 + -alpha_filter . predictive filtering for alpha plane, + one of: none, fast (default) or best + -exact ................. preserve RGB values in transparent area, default=off + -blend_alpha ..... blend colors against background color + expressed as RGB values written in + hexadecimal, e.g. 0xc0e0d0 for red=0xc0 + green=0xe0 and blue=0xd0 + -noalpha ............... discard any transparency information + -lossless .............. encode image losslessly, default=off + -near_lossless ... use near-lossless image + preprocessing (0..100=off), default=100 + -hint ......... specify image characteristics hint, + one of: photo, picture or graph + + -metadata ..... comma separated list of metadata to + copy from the input to the output if present. + Valid values: all, none (default), exif, icc, xmp + + -short ................. condense printed message + -quiet ................. don't print anything + -version ............... print version number and exit + -noasm ................. disable all assembly optimizations + -v ..................... verbose, e.g. print encoding/decoding times + -progress .............. report encoding progress + +Experimental Options: + -jpeg_like ............. roughly match expected JPEG size + -af .................... auto-adjust filter strength + -pre ............. pre-processing filter + + +The main options you might want to try in order to further tune the +visual quality are: + -preset + -sns + -f + -m + +Namely: + * 'preset' will set up a default encoding configuration targeting a + particular type of input. It should appear first in the list of options, + so that subsequent options can take effect on top of this preset. + Default value is 'default'. + * 'sns' will progressively turn on (when going from 0 to 100) some additional + visual optimizations (like: segmentation map re-enforcement). This option + will balance the bit allocation differently. It tries to take bits from the + "easy" parts of the picture and use them in the "difficult" ones instead. + Usually, raising the sns value (at fixed -q value) leads to larger files, + but with better quality. + Typical value is around '75'. + * 'f' option directly links to the filtering strength used by the codec's + in-loop processing. The higher the value, the smoother the + highly-compressed area will look. This is particularly useful when aiming + at very small files. Typical values are around 20-30. Note that using the + option -strong/-nostrong will change the type of filtering. Use "-f 0" to + turn filtering off. + * 'm' controls the trade-off between encoding speed and quality. Default is 4. + You can try -m 5 or -m 6 to explore more (time-consuming) encoding + possibilities. A lower value will result in faster encoding at the expense + of quality. + +Decoding tool: +============== + +There is a decoding sample in examples/dwebp.c which will take +a .webp file and decode it to a PNG image file (amongst other formats). +This is simply to demonstrate the use of the API. You can verify the +file test.webp decodes to exactly the same as test_ref.ppm by using: + + cd examples + ./dwebp test.webp -ppm -o test.ppm + diff test.ppm test_ref.ppm + +The full list of options is available using -h: + +> dwebp -h +Usage: dwebp in_file [options] [-o out_file] + +Decodes the WebP image file to PNG format [Default]. +Note: Animated WebP files are not supported. + +Use following options to convert into alternate image formats: + -pam ......... save the raw RGBA samples as a color PAM + -ppm ......... save the raw RGB samples as a color PPM + -bmp ......... save as uncompressed BMP format + -tiff ........ save as uncompressed TIFF format + -pgm ......... save the raw YUV samples as a grayscale PGM + file with IMC4 layout + -yuv ......... save the raw YUV samples in flat layout + + Other options are: + -version ..... print version number and exit + -nofancy ..... don't use the fancy YUV420 upscaler + -nofilter .... disable in-loop filtering + -nodither .... disable dithering + -dither .. dithering strength (in 0..100) + -alpha_dither use alpha-plane dithering if needed + -mt .......... use multi-threading + -crop ... crop output with the given rectangle + -resize ......... scale the output (*after* any cropping) + -flip ........ flip the output vertically + -alpha ....... only save the alpha plane + -incremental . use incremental decoding (useful for tests) + -h ........... this help message + -v ........... verbose (e.g. print encoding/decoding times) + -quiet ....... quiet mode, don't print anything + -noasm ....... disable all assembly optimizations + +WebP file analysis tool: +======================== + +'webpinfo' can be used to print out the chunk level structure and bitstream +header information of WebP files. It can also check if the files are of valid +WebP format. + +Usage: webpinfo [options] in_files +Note: there could be multiple input files; + options must come before input files. +Options: + -version ........... Print version number and exit. + -quiet ............. Do not show chunk parsing information. + -diag .............. Show parsing error diagnosis. + -summary ........... Show chunk stats summary. + -bitstream_info .... Parse bitstream header. + +Visualization tool: +=================== + +There's a little self-serve visualization tool called 'vwebp' under the +examples/ directory. It uses OpenGL to open a simple drawing window and show +a decoded WebP file. It's not yet integrated in the automake build system, but +you can try to manually compile it using the recommendations below. + +Usage: vwebp in_file [options] + +Decodes the WebP image file and visualize it using OpenGL +Options are: + -version ..... print version number and exit + -noicc ....... don't use the icc profile if present + -nofancy ..... don't use the fancy YUV420 upscaler + -nofilter .... disable in-loop filtering + -dither dithering strength (0..100), default=50 + -noalphadither disable alpha plane dithering + -usebgcolor .. display background color + -mt .......... use multi-threading + -info ........ print info + -h ........... this help message + +Keyboard shortcuts: + 'c' ................ toggle use of color profile + 'b' ................ toggle background color display + 'i' ................ overlay file information + 'd' ................ disable blending & disposal (debug) + 'q' / 'Q' / ESC .... quit + +Building: +--------- + +Prerequisites: +1) OpenGL & OpenGL Utility Toolkit (GLUT) + Linux: + $ sudo apt-get install freeglut3-dev mesa-common-dev + Mac + Xcode: + - These libraries should be available in the OpenGL / GLUT frameworks. + Windows: + http://freeglut.sourceforge.net/index.php#download + +2) (Optional) qcms (Quick Color Management System) + i. Download qcms from Mozilla / Chromium: + http://hg.mozilla.org/mozilla-central/file/0e7639e3bdfb/gfx/qcms + http://src.chromium.org/viewvc/chrome/trunk/src/third_party/qcms + ii. Build and archive the source files as libqcms.a / qcms.lib + iii. Update makefile.unix / Makefile.vc + a) Define WEBP_HAVE_QCMS + b) Update include / library paths to reference the qcms directory. + +Build using makefile.unix / Makefile.vc: +$ make -f makefile.unix examples/vwebp +> nmake /f Makefile.vc CFG=release-static \ + ../obj/x64/release-static/bin/vwebp.exe + +Animation creation tool: +======================== +The utility 'img2webp' can turn a sequence of input images (PNG, JPEG, ...) +into an animated WebP file. It offers fine control over duration, encoding +modes, etc. + +Usage: + + img2webp [file-level options] [image files...] [per-frame options...] + +File-level options (only used at the start of compression): + -min_size ............ minimize size + -loop .......... loop count (default: 0, = infinite loop) + -kmax .......... maximum number of frame between key-frames + (0=only keyframes) + -kmin .......... minimum number of frame between key-frames + (0=disable key-frames altogether) + -mixed ............... use mixed lossy/lossless automatic mode + -v ................... verbose mode + -h ................... this help + -version ............. print version number and exit + +Per-frame options (only used for subsequent images input): + -d ............. frame duration in ms (default: 100) + -lossless ........... use lossless mode (default) + -lossy ... ........... use lossy mode + -q ........... quality + -m ............. method to use + +example: img2webp -loop 2 in0.png -lossy in1.jpg + -d 80 in2.tiff -o out.webp + +Note: if a single file name is passed as the argument, the arguments will be +tokenized from this file. The file name must not start with the character '-'. + +Animated GIF conversion: +======================== +Animated GIF files can be converted to WebP files with animation using the +gif2webp utility available under examples/. The files can then be viewed using +vwebp. + +Usage: + gif2webp [options] gif_file -o webp_file +Options: + -h / -help ............. this help + -lossy ................. encode image using lossy compression + -mixed ................. for each frame in the image, pick lossy + or lossless compression heuristically + -q ............. quality factor (0:small..100:big) + -m ............... compression method (0=fast, 6=slowest) + -min_size .............. minimize output size (default:off) + lossless compression by default; can be + combined with -q, -m, -lossy or -mixed + options + -kmin ............ min distance between key frames + -kmax ............ max distance between key frames + -f ............... filter strength (0=off..100) + -metadata ..... comma separated list of metadata to + copy from the input to the output if present + Valid values: all, none, icc, xmp (default) + -loop_compatibility .... use compatibility mode for Chrome + version prior to M62 (inclusive) + -mt .................... use multi-threading if available + + -version ............... print version number and exit + -v ..................... verbose + -quiet ................. don't print anything + +Building: +--------- +With the libgif development files installed, gif2webp can be built using +makefile.unix: +$ make -f makefile.unix examples/gif2webp + +or using autoconf: +$ ./configure --enable-everything +$ make + +Comparison of animated images: +============================== +Test utility anim_diff under examples/ can be used to compare two animated +images (each can be GIF or WebP). + +Usage: anim_diff [options] + +Options: + -dump_frames dump decoded frames in PAM format + -min_psnr ... minimum per-frame PSNR + -raw_comparison ..... if this flag is not used, RGB is + premultiplied before comparison + -max_diff ..... maximum allowed difference per channel + between corresponding pixels in subsequent + frames + -h .................. this help + -version ............ print version number and exit + +Building: +--------- +With the libgif development files and a C++ compiler installed, anim_diff can +be built using makefile.unix: +$ make -f makefile.unix examples/anim_diff + +or using autoconf: +$ ./configure --enable-everything +$ make + +Encoding API: +============= + +The main encoding functions are available in the header src/webp/encode.h +The ready-to-use ones are: +size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height, int stride, + float quality_factor, uint8_t** output); +size_t WebPEncodeBGR(const uint8_t* bgr, int width, int height, int stride, + float quality_factor, uint8_t** output); +size_t WebPEncodeRGBA(const uint8_t* rgba, int width, int height, int stride, + float quality_factor, uint8_t** output); +size_t WebPEncodeBGRA(const uint8_t* bgra, int width, int height, int stride, + float quality_factor, uint8_t** output); + +They will convert raw RGB samples to a WebP data. The only control supplied +is the quality factor. + +There are some variants for using the lossless format: + +size_t WebPEncodeLosslessRGB(const uint8_t* rgb, int width, int height, + int stride, uint8_t** output); +size_t WebPEncodeLosslessBGR(const uint8_t* bgr, int width, int height, + int stride, uint8_t** output); +size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, int width, int height, + int stride, uint8_t** output); +size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height, + int stride, uint8_t** output); + +Of course in this case, no quality factor is needed since the compression +occurs without loss of the input values, at the expense of larger output sizes. + +Advanced encoding API: +---------------------- + +A more advanced API is based on the WebPConfig and WebPPicture structures. + +WebPConfig contains the encoding settings and is not tied to a particular +picture. +WebPPicture contains input data, on which some WebPConfig will be used for +compression. +The encoding flow looks like: + +-------------------------------------- BEGIN PSEUDO EXAMPLE + +#include + + // Setup a config, starting form a preset and tuning some additional + // parameters + WebPConfig config; + if (!WebPConfigPreset(&config, WEBP_PRESET_PHOTO, quality_factor)) { + return 0; // version error + } + // ... additional tuning + config.sns_strength = 90; + config.filter_sharpness = 6; + config_error = WebPValidateConfig(&config); // not mandatory, but useful + + // Setup the input data + WebPPicture pic; + if (!WebPPictureInit(&pic)) { + return 0; // version error + } + pic.width = width; + pic.height = height; + // allocated picture of dimension width x height + if (!WebPPictureAlloc(&pic)) { + return 0; // memory error + } + // at this point, 'pic' has been initialized as a container, + // and can receive the Y/U/V samples. + // Alternatively, one could use ready-made import functions like + // WebPPictureImportRGB(), which will take care of memory allocation. + // In any case, past this point, one will have to call + // WebPPictureFree(&pic) to reclaim memory. + + // Set up a byte-output write method. WebPMemoryWriter, for instance. + WebPMemoryWriter wrt; + WebPMemoryWriterInit(&wrt); // initialize 'wrt' + + pic.writer = MyFileWriter; + pic.custom_ptr = my_opaque_structure_to_make_MyFileWriter_work; + + // Compress! + int ok = WebPEncode(&config, &pic); // ok = 0 => error occurred! + WebPPictureFree(&pic); // must be called independently of the 'ok' result. + + // output data should have been handled by the writer at that point. + // -> compressed data is the memory buffer described by wrt.mem / wrt.size + + // deallocate the memory used by compressed data + WebPMemoryWriterClear(&wrt); + +-------------------------------------- END PSEUDO EXAMPLE + +Decoding API: +============= + +This is mainly just one function to call: + +#include "webp/decode.h" +uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, + int* width, int* height); + +Please have a look at the file src/webp/decode.h for the details. +There are variants for decoding in BGR/RGBA/ARGB/BGRA order, along with +decoding to raw Y'CbCr samples. One can also decode the image directly into a +pre-allocated buffer. + +To detect a WebP file and gather the picture's dimensions, the function: + int WebPGetInfo(const uint8_t* data, size_t data_size, + int* width, int* height); +is supplied. No decoding is involved when using it. + +Incremental decoding API: +========================= + +In the case when data is being progressively transmitted, pictures can still +be incrementally decoded using a slightly more complicated API. Decoder state +is stored into an instance of the WebPIDecoder object. This object can be +created with the purpose of decoding either RGB or Y'CbCr samples. +For instance: + + WebPDecBuffer buffer; + WebPInitDecBuffer(&buffer); + buffer.colorspace = MODE_BGR; + ... + WebPIDecoder* idec = WebPINewDecoder(&buffer); + +As data is made progressively available, this incremental-decoder object +can be used to decode the picture further. There are two (mutually exclusive) +ways to pass freshly arrived data: + +either by appending the fresh bytes: + + WebPIAppend(idec, fresh_data, size_of_fresh_data); + +or by just mentioning the new size of the transmitted data: + + WebPIUpdate(idec, buffer, size_of_transmitted_buffer); + +Note that 'buffer' can be modified between each call to WebPIUpdate, in +particular when the buffer is resized to accommodate larger data. + +These functions will return the decoding status: either VP8_STATUS_SUSPENDED if +decoding is not finished yet or VP8_STATUS_OK when decoding is done. Any other +status is an error condition. + +The 'idec' object must always be released (even upon an error condition) by +calling: WebPDelete(idec). + +To retrieve partially decoded picture samples, one must use the corresponding +method: WebPIDecGetRGB or WebPIDecGetYUVA. +It will return the last displayable pixel row. + +Lastly, note that decoding can also be performed into a pre-allocated pixel +buffer. This buffer must be passed when creating a WebPIDecoder, calling +WebPINewRGB() or WebPINewYUVA(). + +Please have a look at the src/webp/decode.h header for further details. + +Advanced Decoding API: +====================== + +WebP decoding supports an advanced API which provides on-the-fly cropping and +rescaling, something of great usefulness on memory-constrained environments like +mobile phones. Basically, the memory usage will scale with the output's size, +not the input's, when one only needs a quick preview or a zoomed in portion of +an otherwise too-large picture. Some CPU can be saved too, incidentally. + +-------------------------------------- BEGIN PSEUDO EXAMPLE + // A) Init a configuration object + WebPDecoderConfig config; + CHECK(WebPInitDecoderConfig(&config)); + + // B) optional: retrieve the bitstream's features. + CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK); + + // C) Adjust 'config' options, if needed + config.options.no_fancy_upsampling = 1; + config.options.use_scaling = 1; + config.options.scaled_width = scaledWidth(); + config.options.scaled_height = scaledHeight(); + // etc. + + // D) Specify 'config' output options for specifying output colorspace. + // Optionally the external image decode buffer can also be specified. + config.output.colorspace = MODE_BGRA; + // Optionally, the config.output can be pointed to an external buffer as + // well for decoding the image. This externally supplied memory buffer + // should be big enough to store the decoded picture. + config.output.u.RGBA.rgba = (uint8_t*) memory_buffer; + config.output.u.RGBA.stride = scanline_stride; + config.output.u.RGBA.size = total_size_of_the_memory_buffer; + config.output.is_external_memory = 1; + + // E) Decode the WebP image. There are two variants w.r.t decoding image. + // The first one (E.1) decodes the full image and the second one (E.2) is + // used to incrementally decode the image using small input buffers. + // Any one of these steps can be used to decode the WebP image. + + // E.1) Decode full image. + CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK); + + // E.2) Decode image incrementally. + WebPIDecoder* const idec = WebPIDecode(NULL, NULL, &config); + CHECK(idec != NULL); + while (bytes_remaining > 0) { + VP8StatusCode status = WebPIAppend(idec, input, bytes_read); + if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) { + bytes_remaining -= bytes_read; + } else { + break; + } + } + WebPIDelete(idec); + + // F) Decoded image is now in config.output (and config.output.u.RGBA). + // It can be saved, displayed or otherwise processed. + + // G) Reclaim memory allocated in config's object. It's safe to call + // this function even if the memory is external and wasn't allocated + // by WebPDecode(). + WebPFreeDecBuffer(&config.output); + +-------------------------------------- END PSEUDO EXAMPLE + +Bugs: +===== + +Please report all bugs to the issue tracker: + https://bugs.chromium.org/p/webp +Patches welcome! See this page to get started: + http://www.webmproject.org/code/contribute/submitting-patches/ + +Discuss: +======== + +Email: webp-discuss@webmproject.org +Web: http://groups.google.com/a/webmproject.org/group/webp-discuss diff --git a/internal/libwebp-1.2.1/README.mux b/internal/libwebp-1.2.1/README.mux new file mode 100644 index 0000000..1926d14 --- /dev/null +++ b/internal/libwebp-1.2.1/README.mux @@ -0,0 +1,258 @@ +ïğż __ __ ____ ____ ____ __ __ _ __ __ + / \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\ + \ / __/ _ \ __/ / / (_/ /__ + \__\__/\_____/_____/__/ \__//_/\_____/__/___/v1.2.1 + + +Description: +============ + +WebPMux: set of two libraries 'Mux' and 'Demux' for creation, extraction and +manipulation of an extended format WebP file, which can have features like +color profile, metadata and animation. Reference command-line tools 'webpmux' +and 'vwebp' as well as the WebP container specification +'doc/webp-container-spec.txt' are also provided in this package. + +WebP Mux tool: +============== + +The examples/ directory contains a tool (webpmux) for manipulating WebP +files. The webpmux tool can be used to create an extended format WebP file and +also to extract or strip relevant data from such a file. + +A list of options is available using the -help command line flag: + +> webpmux -help +Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT + webpmux -set SET_OPTIONS INPUT -o OUTPUT + webpmux -duration DURATION_OPTIONS [-duration ...] + INPUT -o OUTPUT + webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT + webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT] + [-bgcolor BACKGROUND_COLOR] -o OUTPUT + webpmux -info INPUT + webpmux [-h|-help] + webpmux -version + webpmux argument_file_name + +GET_OPTIONS: + Extract relevant data: + icc get ICC profile + exif get EXIF metadata + xmp get XMP metadata + frame n get nth frame + +SET_OPTIONS: + Set color profile/metadata/parameters: + loop LOOP_COUNT set the loop count + bgcolor BACKGROUND_COLOR set the animation background color + icc file.icc set ICC profile + exif file.exif set EXIF metadata + xmp file.xmp set XMP metadata + where: 'file.icc' contains the ICC profile to be set, + 'file.exif' contains the EXIF metadata to be set + 'file.xmp' contains the XMP metadata to be set + +DURATION_OPTIONS: + Set duration of selected frames: + duration set duration for each frames + duration,frame set duration of a particular frame + duration,start,end set duration of frames in the + interval [start,end]) + where: 'duration' is the duration in milliseconds + 'start' is the start frame index + 'end' is the inclusive end frame index + The special 'end' value '0' means: last frame. + +STRIP_OPTIONS: + Strip color profile/metadata: + icc strip ICC profile + exif strip EXIF metadata + xmp strip XMP metadata + +FRAME_OPTIONS(i): + Create animation: + file_i +di+[xi+yi[+mi[bi]]] + where: 'file_i' is the i'th animation frame (WebP format), + 'di' is the pause duration before next frame, + 'xi','yi' specify the image offset for this frame, + 'mi' is the dispose method for this frame (0 or 1), + 'bi' is the blending method for this frame (+b or -b) + +LOOP_COUNT: + Number of times to repeat the animation. + Valid range is 0 to 65535 [Default: 0 (infinite)]. + +BACKGROUND_COLOR: + Background color of the canvas. + A,R,G,B + where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying + the Alpha, Red, Green and Blue component values respectively + [Default: 255,255,255,255] + +INPUT & OUTPUT are in WebP format. + +Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be +valid. + +Note: if a single file name is passed as the argument, the arguments will be +tokenized from this file. The file name must not start with the character '-'. + +Visualization tool: +=================== + +The examples/ directory also contains a tool (vwebp) for viewing WebP files. +It decodes the image and visualizes it using OpenGL. See the libwebp README +for details on building and running this program. + +Mux API: +======== +The Mux API contains methods for adding data to and reading data from WebP +files. This API currently supports XMP/EXIF metadata, ICC profile and animation. +Other features may be added in subsequent releases. + +Example#1 (pseudo code): Creating a WebPMux object with image data, color +profile and XMP metadata. + + int copy_data = 0; + WebPMux* mux = WebPMuxNew(); + // ... (Prepare image data). + WebPMuxSetImage(mux, &image, copy_data); + // ... (Prepare ICC profile data). + WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data); + // ... (Prepare XMP metadata). + WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data); + // Get data from mux in WebP RIFF format. + WebPMuxAssemble(mux, &output_data); + WebPMuxDelete(mux); + // ... (Consume output_data; e.g. write output_data.bytes to file). + WebPDataClear(&output_data); + + +Example#2 (pseudo code): Get image and color profile data from a WebP file. + + int copy_data = 0; + // ... (Read data from file). + WebPMux* mux = WebPMuxCreate(&data, copy_data); + WebPMuxGetFrame(mux, 1, &image); + // ... (Consume image; e.g. call WebPDecode() to decode the data). + WebPMuxGetChunk(mux, "ICCP", &icc_profile); + // ... (Consume icc_profile). + WebPMuxDelete(mux); + free(data); + + +For a detailed Mux API reference, please refer to the header file +(src/webp/mux.h). + +Demux API: +========== +The Demux API enables extraction of images and extended format data from +WebP files. This API currently supports reading of XMP/EXIF metadata, ICC +profile and animated images. Other features may be added in subsequent +releases. + +Code example: Demuxing WebP data to extract all the frames, ICC profile +and EXIF/XMP metadata. + + WebPDemuxer* demux = WebPDemux(&webp_data); + uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); + uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); + // ... (Get information about the features present in the WebP file). + uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); + + // ... (Iterate over all frames). + WebPIterator iter; + if (WebPDemuxGetFrame(demux, 1, &iter)) { + do { + // ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(), + // ... and get other frame properties like width, height, offsets etc. + // ... see 'struct WebPIterator' below for more info). + } while (WebPDemuxNextFrame(&iter)); + WebPDemuxReleaseIterator(&iter); + } + + // ... (Extract metadata). + WebPChunkIterator chunk_iter; + if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter); + // ... (Consume the ICC profile in 'chunk_iter.chunk'). + WebPDemuxReleaseChunkIterator(&chunk_iter); + if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter); + // ... (Consume the EXIF metadata in 'chunk_iter.chunk'). + WebPDemuxReleaseChunkIterator(&chunk_iter); + if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter); + // ... (Consume the XMP metadata in 'chunk_iter.chunk'). + WebPDemuxReleaseChunkIterator(&chunk_iter); + WebPDemuxDelete(demux); + + +For a detailed Demux API reference, please refer to the header file +(src/webp/demux.h). + +AnimEncoder API: +================ +The AnimEncoder API can be used to create animated WebP images. + +Code example: + + WebPAnimEncoderOptions enc_options; + WebPAnimEncoderOptionsInit(&enc_options); + // ... (Tune 'enc_options' as needed). + WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options); + while() { + WebPConfig config; + WebPConfigInit(&config); + // ... (Tune 'config' as needed). + WebPAnimEncoderAdd(enc, frame, duration, &config); + } + WebPAnimEncoderAssemble(enc, webp_data); + WebPAnimEncoderDelete(enc); + // ... (Write the 'webp_data' to a file, or re-mux it further). + + +For a detailed AnimEncoder API reference, please refer to the header file +(src/webp/mux.h). + +AnimDecoder API: +================ +This AnimDecoder API allows decoding (possibly) animated WebP images. + +Code Example: + + WebPAnimDecoderOptions dec_options; + WebPAnimDecoderOptionsInit(&dec_options); + // Tune 'dec_options' as needed. + WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options); + WebPAnimInfo anim_info; + WebPAnimDecoderGetInfo(dec, &anim_info); + for (uint32_t i = 0; i < anim_info.loop_count; ++i) { + while (WebPAnimDecoderHasMoreFrames(dec)) { + uint8_t* buf; + int timestamp; + WebPAnimDecoderGetNext(dec, &buf, ×tamp); + // ... (Render 'buf' based on 'timestamp'). + // ... (Do NOT free 'buf', as it is owned by 'dec'). + } + WebPAnimDecoderReset(dec); + } + const WebPDemuxer* demuxer = WebPAnimDecoderGetDemuxer(dec); + // ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data). + WebPAnimDecoderDelete(dec); + +For a detailed AnimDecoder API reference, please refer to the header file +(src/webp/demux.h). + + +Bugs: +===== + +Please report all bugs to the issue tracker: + https://bugs.chromium.org/p/webp +Patches welcome! See this page to get started: + http://www.webmproject.org/code/contribute/submitting-patches/ + +Discuss: +======== + +Email: webp-discuss@webmproject.org +Web: http://groups.google.com/a/webmproject.org/group/webp-discuss diff --git a/internal/libwebp-1.2.1/README.webp_js b/internal/libwebp-1.2.1/README.webp_js new file mode 100644 index 0000000..3d453a2 --- /dev/null +++ b/internal/libwebp-1.2.1/README.webp_js @@ -0,0 +1,75 @@ + __ __ ____ ____ ____ __ ____ + / \\/ \ _ \ _ \ _ \ (__)/ __\ + \ / __/ _ \ __/ _) \_ \ + \__\__/_____/____/_/ /____/____/ + +Description: +============ + +This file describes the compilation of libwebp into a JavaScript decoder +using Emscripten and CMake. + + - install the Emscripten SDK following the procedure described at: + https://emscripten.org/docs/getting_started/downloads.html#installation-instructions-using-the-emsdk-recommended + After installation, you should have some global variable positioned to the + location of the SDK. In particular, $EMSDK should point to the + top-level directory containing Emscripten tools. + + - configure the project 'WEBP_JS' with CMake using: + + cd webp_js && \ + emcmake cmake -DWEBP_BUILD_WEBP_JS=ON \ + ../ + + - compile webp.js using 'emmake make'. + + - that's it! Upon completion, you should have the webp.js and + webp.wasm files generated. + +The callable JavaScript function is WebPToSDL(), which decodes a raw WebP +bitstream into a canvas. See webp_js/index.html for a simple usage sample +(see below for instructions). + +Demo HTML page: +=============== + + The HTML page webp_js/index.html requires an HTTP server to serve the WebP + image example. It's easy to just use Python for that. + +cd webp_js && python -m SimpleHTTPServer 8080 + +and then navigate to http://localhost:8080 in your favorite browser. + + +Web-Assembly (WASM) version: +============================ + + CMakeLists.txt is configured to build the WASM version when using + the option WEBP_BUILD_WEBP_JS=ON. The compilation step will assemble + the files 'webp_wasm.js', 'webp_wasm.wasm' in the webp_js/ directory. + See webp_js/index_wasm.html for a simple demo page using the WASM version + of the library. + + You will need a fairly recent version of Emscripten (at least 2.0.18, + latest-upstream is recommended) and of your WASM-enabled browser to run this + version. + +Caveat: +======= + + - First decoding using the library is usually slower, due to just-in-time + compilation. + + - Some versions of llvm produce the following compile error when SSE2 is + enabled. + +"Unsupported: %516 = bitcast <8 x i16> %481 to i128 + LLVM ERROR: BitCast Instruction not yet supported for integer types larger than 64 bits" + + The corresponding Emscripten bug is at: + https://github.com/kripken/emscripten/issues/3788 + + Therefore, SSE2 optimization is currently disabled in CMakeLists.txt. + + - If WEBP_ENABLE_SIMD is set to 1 the JavaScript version (webp.js) will be + disabled as wasm2js does not support SIMD. diff --git a/internal/libwebp-1.2.1/autogen.sh b/internal/libwebp-1.2.1/autogen.sh new file mode 100755 index 0000000..8ef9bab --- /dev/null +++ b/internal/libwebp-1.2.1/autogen.sh @@ -0,0 +1,2 @@ +#! /bin/sh -e +exec autoreconf -fi diff --git a/internal/libwebp-1.2.1/build.gradle b/internal/libwebp-1.2.1/build.gradle new file mode 100644 index 0000000..bbd9bcf --- /dev/null +++ b/internal/libwebp-1.2.1/build.gradle @@ -0,0 +1,435 @@ +// Define dependencies. +buildscript { + repositories { + maven { + url "https://jcenter.bintray.com" + } + } + dependencies { + classpath "com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}" + } +} + +// Define versions in the project. +project.ext { + buildToolsVersion = "${BUILD_TOOLS_VERSION}" + compileSdkVersion = COMPILE_SDK_VERSION.toInteger() +} + +// Core libraries and executables. +apply plugin: "c" +def NEON +model { + buildTypes { + debug + release + } + platforms { + arm { + architecture "arm" + } + arm64 { + architecture "arm64" + } + x86 { + architecture "x86" + } + x64 { + architecture "x86_64" + } + mips32r2 + mips32r5 + mips64r6 + } + toolChains { + gcc(Gcc) { + target("mips32r2") { + cCompiler.args "-mips32r2" + } + target("mips32r5") { + cCompiler.args "-mips32r5" + } + target("mips64r6") { + cCompiler.args "-mips64r6" + } + } + } + binaries { + all { + if (toolChain in Gcc) { + cCompiler.args "-fPIC" + cCompiler.args "-Wall" + cCompiler.define "ANDROID" + cCompiler.define "HAVE_MALLOC_H" + } + // Optimizations. + if (buildType == buildTypes.release) { + if (toolChain in Gcc) { + cCompiler.args "-finline-functions" + cCompiler.args "-ffast-math" + cCompiler.args "-ffunction-sections" + cCompiler.args "-fdata-sections" + } + if (toolChain in Clang) { + cCompiler.args "-frename-registers -s" + } + } + // mips32 fails to build with clang from r14b + // https://bugs.chromium.org/p/webp/issues/detail?id=343 + if (toolChain in Clang) { + if (getTargetPlatform() == "mips") { + cCompiler.args "-no-integrated-as" + } + } + // Check for NEON usage. + if (getTargetPlatform() == "arm") { + NEON = "c.neon" + cCompiler.define "HAVE_CPU_FEATURES_H" + } else { + NEON = "c" + } + + cCompiler.args "-I" + file(".").absolutePath + } + // Link to pthread for shared libraries. + withType(SharedLibraryBinarySpec) { + if (toolChain in Gcc) { + cCompiler.define "HAVE_PTHREAD" + cCompiler.define "WEBP_USE_THREAD" + linker.args "-pthread" + } + } + } + components { + webp(NativeLibrarySpec) { + sources { + c { + source { + srcDir "src/dec" + include "alpha_dec.c" + include "buffer_dec.c" + include "frame_dec.c" + include "idec_dec.c" + include "io_dec.c" + include "quant_dec.c" + include "tree_dec.c" + include "vp8_dec.c" + include "vp8l_dec.c" + include "webp_dec.c" + srcDir "src/dsp" + include "alpha_processing.c" + include "alpha_processing_mips_dsp_r2.c" + include "alpha_processing_neon.$NEON" + include "alpha_processing_sse2.c" + include "alpha_processing_sse41.c" + include "cpu.c" + include "dec.c" + include "dec_clip_tables.c" + include "dec_mips32.c" + include "dec_mips_dsp_r2.c" + include "dec_msa.c" + include "dec_neon.$NEON" + include "dec_sse2.c" + include "dec_sse41.c" + include "filters.c" + include "filters_mips_dsp_r2.c" + include "filters_msa.c" + include "filters_neon.$NEON" + include "filters_sse2.c" + include "lossless.c" + include "lossless_mips_dsp_r2.c" + include "lossless_msa.c" + include "lossless_neon.$NEON" + include "lossless_sse2.c" + include "lossless_sse41.c" + include "rescaler.c" + include "rescaler_mips32.c" + include "rescaler_mips_dsp_r2.c" + include "rescaler_msa.c" + include "rescaler_neon.$NEON" + include "rescaler_sse2.c" + include "upsampling.c" + include "upsampling_mips_dsp_r2.c" + include "upsampling_msa.c" + include "upsampling_neon.$NEON" + include "upsampling_sse2.c" + include "upsampling_sse41.c" + include "yuv.c" + include "yuv_mips32.c" + include "yuv_mips_dsp_r2.c" + include "yuv_neon.$NEON" + include "yuv_sse2.c" + include "yuv_sse41.c" + srcDir "src/utils" + include "bit_reader_utils.c" + include "color_cache_utils.c" + include "filters_utils.c" + include "huffman_utils.c" + include "quant_levels_dec_utils.c" + include "random_utils.c" + include "rescaler_utils.c" + include "thread_utils.c" + include "utils.c" + srcDir "src/dsp" + include "cost.c" + include "cost_mips32.c" + include "cost_mips_dsp_r2.c" + include "cost_neon.$NEON" + include "cost_sse2.c" + include "enc.c" + include "enc_mips32.c" + include "enc_mips_dsp_r2.c" + include "enc_msa.c" + include "enc_neon.$NEON" + include "enc_sse2.c" + include "enc_sse41.c" + include "lossless_enc.c" + include "lossless_enc_mips32.c" + include "lossless_enc_mips_dsp_r2.c" + include "lossless_enc_msa.c" + include "lossless_enc_neon.$NEON" + include "lossless_enc_sse2.c" + include "lossless_enc_sse41.c" + include "ssim.c" + include "ssim_sse2.c" + srcDir "src/enc" + include "alpha_enc.c" + include "analysis_enc.c" + include "backward_references_cost_enc.c" + include "backward_references_enc.c" + include "config_enc.c" + include "cost_enc.c" + include "filter_enc.c" + include "frame_enc.c" + include "histogram_enc.c" + include "iterator_enc.c" + include "near_lossless_enc.c" + include "picture_enc.c" + include "picture_csp_enc.c" + include "picture_psnr_enc.c" + include "picture_rescale_enc.c" + include "picture_tools_enc.c" + include "predictor_enc.c" + include "quant_enc.c" + include "syntax_enc.c" + include "token_enc.c" + include "tree_enc.c" + include "vp8l_enc.c" + include "webp_enc.c" + srcDir "src/utils" + include "bit_writer_utils.c" + include "huffman_encode_utils.c" + include "quant_levels_utils.c" + } + exportedHeaders { + srcDir "src" + } + } + } + } + + webpdemux(NativeLibrarySpec) { + sources { + c { + source { + srcDir "src/demux" + include "anim_decode.c" + include "demux.c" + } + } + } + } + + webpmux(NativeLibrarySpec) { + sources { + c { + source { + srcDir "src/mux/" + include "anim_encode.c" + include "muxedit.c" + include "muxinternal.c" + include "muxread.c" + } + } + } + } + + // Executables from examples. + example_util(NativeLibrarySpec) { + binaries { + all { + lib library: "webp", linkage: "static" + } + } + sources { + c { + source { + srcDir "./examples" + include "example_util.c" + } + } + } + } + + imageio_util(NativeLibrarySpec) { + binaries { + all { + lib library: "webp", linkage: "static" + } + } + sources { + c { + source { + srcDir "./imageio" + include "imageio_util.c" + } + } + } + } + + imagedec(NativeLibrarySpec) { + binaries { + all { + lib library: "webpdemux", linkage: "static" + lib library: "webp", linkage: "static" + } + } + sources { + c { + source { + srcDir "./imageio" + include "image_dec.c" + include "jpegdec.c" + include "metadata.c" + include "pngdec.c" + include "pnmdec.c" + include "tiffdec.c" + include "webpdec.c" + } + } + } + } + + imageenc(NativeLibrarySpec) { + binaries { + all { + lib library: "webp", linkage: "static" + lib library: "imageio_util", linkage: "static" + } + } + sources { + c { + source { + srcDir "./imageio" + include "image_enc.c" + } + } + } + } + + cwebp(NativeExecutableSpec) { + binaries { + all { + lib library: "example_util", linkage: "static" + lib library: "imagedec", linkage: "static" + lib library: "imageio_util", linkage: "static" + lib library: "webpdemux", linkage: "static" + lib library: "webp", linkage: "static" + } + } + sources { + c { + source { + srcDir "./examples" + include "cwebp.c" + } + } + } + } + + dwebp(NativeExecutableSpec) { + binaries { + all { + lib library: "example_util", linkage: "static" + lib library: "imagedec", linkage: "static" + lib library: "imageenc", linkage: "static" + lib library: "imageio_util", linkage: "static" + lib library: "webpdemux", linkage: "static" + lib library: "webp" + } + } + sources { + c { + source { + srcDir "./examples" + include "dwebp.c" + } + } + } + } + + webpmux_example(NativeExecutableSpec) { + binaries { + all { + lib library: "example_util", linkage: "static" + lib library: "imageio_util", linkage: "static" + lib library: "webpmux", linkage: "static" + lib library: "webp" + } + } + sources { + c { + source { + srcDir "./examples" + include "webpmux.c" + } + } + } + } + + img2webp_example(NativeExecutableSpec) { + binaries { + all { + lib library: "example_util", linkage: "static" + lib library: "imagedec", linkage: "static" + lib library: "imageio_util", linkage: "static" + lib library: "webpmux", linkage: "static" + lib library: "webpdemux", linkage: "static" + lib library: "webp" + } + } + sources { + c { + source { + srcDir "./examples" + include "img2webp.c" + } + } + } + } + + webpinfo_example(NativeExecutableSpec) { + binaries { + all { + lib library: "example_util", linkage: "static" + lib library: "imageio_util", linkage: "static" + lib library: "webp" + } + } + sources { + c { + source { + srcDir "./examples" + include "webpinfo.c" + } + } + } + } + } + tasks { + // Task to test all possible configurations. + buildAllExecutables(Task) { + dependsOn $.binaries.findAll { it.buildable } + } + } +} diff --git a/internal/libwebp-1.2.1/cmake/WebPConfig.cmake.in b/internal/libwebp-1.2.1/cmake/WebPConfig.cmake.in new file mode 100644 index 0000000..822fc59 --- /dev/null +++ b/internal/libwebp-1.2.1/cmake/WebPConfig.cmake.in @@ -0,0 +1,11 @@ +set(WebP_VERSION @PROJECT_VERSION@) +set(WEBP_VERSION ${WebP_VERSION}) + +@PACKAGE_INIT@ + +include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") + +set(WebP_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@") +set(WEBP_INCLUDE_DIRS ${WebP_INCLUDE_DIRS}) +set(WebP_LIBRARIES "@INSTALLED_LIBRARIES@") +set(WEBP_LIBRARIES "${WebP_LIBRARIES}") diff --git a/internal/libwebp-1.2.1/cmake/config.h.in b/internal/libwebp-1.2.1/cmake/config.h.in new file mode 100644 index 0000000..3770935 --- /dev/null +++ b/internal/libwebp-1.2.1/cmake/config.h.in @@ -0,0 +1,149 @@ +/* Adapted from the autotools src/webp/config.h.in. */ + +/* Define if building universal (internal helper macro) */ +/* TODO: handle properly in CMake */ +#cmakedefine AC_APPLE_UNIVERSAL_BUILD 1 + +/* Set to 1 if __builtin_bswap16 is available */ +#cmakedefine HAVE_BUILTIN_BSWAP16 1 + +/* Set to 1 if __builtin_bswap32 is available */ +#cmakedefine HAVE_BUILTIN_BSWAP32 1 + +/* Set to 1 if __builtin_bswap64 is available */ +#cmakedefine HAVE_BUILTIN_BSWAP64 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_CPU_FEATURES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_GLUT_GLUT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_GL_GLUT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_OPENGL_GLUT_H 1 + +/* Have PTHREAD_PRIO_INHERIT. */ +#cmakedefine HAVE_PTHREAD_PRIO_INHERIT @HAVE_PTHREAD_PRIO_INHERIT@ + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SHLWAPI_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_WINCODEC_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_WINDOWS_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +/* TODO: handle properly in CMake */ +#cmakedefine LT_OBJDIR "@LT_OBJDIR@" + +/* Name of package */ +#cmakedefine PACKAGE "@PROJECT_NAME@" + +/* Define to the address where bug reports for this package should be sent. */ +#cmakedefine PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" + +/* Define to the full name of this package. */ +#cmakedefine PACKAGE_NAME "@PACKAGE_NAME@" + +/* Define to the full name and version of this package. */ +#cmakedefine PACKAGE_STRING "@PACKAGE_STRING@" + +/* Define to the one symbol short name of this package. */ +#cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@" + +/* Define to the home page for this package. */ +#cmakedefine PACKAGE_URL "@PACKAGE_URL@" + +/* Define to the version of this package. */ +#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@" + +/* Define to 1 if you have the ANSI C header files. */ +#cmakedefine STDC_HEADERS 1 + +/* Version number of package */ +#cmakedefine VERSION "@VERSION@" + +/* Set to 1 if GIF library is installed */ +#cmakedefine WEBP_HAVE_GIF 1 + +/* Set to 1 if OpenGL is supported */ +#cmakedefine WEBP_HAVE_GL 1 + +/* Set to 1 if JPEG library is installed */ +#cmakedefine WEBP_HAVE_JPEG 1 + +/* Set to 1 if NEON is supported */ +#cmakedefine WEBP_HAVE_NEON + +/* Set to 1 if runtime detection of NEON is enabled */ +/* TODO: handle properly in CMake */ +#cmakedefine WEBP_HAVE_NEON_RTCD + +/* Set to 1 if PNG library is installed */ +#cmakedefine WEBP_HAVE_PNG 1 + +/* Set to 1 if SDL library is installed */ +#cmakedefine WEBP_HAVE_SDL 1 + +/* Set to 1 if SSE2 is supported */ +#cmakedefine WEBP_HAVE_SSE2 1 + +/* Set to 1 if SSE4.1 is supported */ +#cmakedefine WEBP_HAVE_SSE41 1 + +/* Set to 1 if TIFF library is installed */ +#cmakedefine WEBP_HAVE_TIFF 1 + +/* Enable near lossless encoding */ +#cmakedefine WEBP_NEAR_LOSSLESS 1 + +/* Undefine this to disable thread support. */ +#cmakedefine WEBP_USE_THREAD 1 + +/* 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/internal/libwebp-1.2.1/cmake/cpu.cmake b/internal/libwebp-1.2.1/cmake/cpu.cmake new file mode 100644 index 0000000..bc0dbc9 --- /dev/null +++ b/internal/libwebp-1.2.1/cmake/cpu.cmake @@ -0,0 +1,162 @@ +# Copyright (c) 2021 Google LLC. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE 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. + +# Check for SIMD extensions. +include(CMakePushCheckState) + +function(webp_check_compiler_flag WEBP_SIMD_FLAG ENABLE_SIMD) + if(NOT ENABLE_SIMD) + message(STATUS "Disabling ${WEBP_SIMD_FLAG} optimization.") + set(WEBP_HAVE_${WEBP_SIMD_FLAG} 0 PARENT_SCOPE) + return() + endif() + unset(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG} CACHE) + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}) + check_c_source_compiles(" + #include \"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/dsp.h\" + int main(void) { + #if !defined(WEBP_USE_${WEBP_SIMD_FLAG}) + this is not valid code + #endif + return 0; + } + " WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG}) + cmake_pop_check_state() + if(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG}) + set(WEBP_HAVE_${WEBP_SIMD_FLAG} 1 PARENT_SCOPE) + else() + set(WEBP_HAVE_${WEBP_SIMD_FLAG} 0 PARENT_SCOPE) + endif() +endfunction() + +# those are included in the names of WEBP_USE_* in c++ code. +set(WEBP_SIMD_FLAGS "SSE41;SSE2;MIPS32;MIPS_DSP_R2;NEON;MSA") +set(WEBP_SIMD_FILE_EXTENSIONS + "_sse41.c;_sse2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c") +if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "MSVC") + # With at least Visual Studio 12 (2013)+ /arch is not necessary to build SSE2 + # or SSE4 code unless a lesser /arch is forced. MSVC does not have a SSE4 + # flag, but an AVX one. Using that with SSE4 code risks generating illegal + # instructions when used on machines with SSE4 only. The flags are left for + # older (untested) versions to avoid any potential compatibility issues. + if(MSVC_VERSION GREATER_EQUAL 1800 AND NOT CMAKE_C_FLAGS MATCHES "/arch:") + set(SIMD_ENABLE_FLAGS) + else() + set(SIMD_ENABLE_FLAGS "/arch:AVX;/arch:SSE2;;;;") + endif() + set(SIMD_DISABLE_FLAGS) +else() + set(SIMD_ENABLE_FLAGS + "-msse4.1;-msse2;-mips32;-mdspr2;-mfpu=neon;-mmsa") + set(SIMD_DISABLE_FLAGS + "-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa") +endif() + +set(WEBP_SIMD_FILES_TO_NOT_INCLUDE) +set(WEBP_SIMD_FILES_TO_INCLUDE) +set(WEBP_SIMD_FLAGS_TO_INCLUDE) + +if(${ANDROID}) + if(${ANDROID_ABI} STREQUAL "armeabi-v7a") + # This is because Android studio uses the configuration "-march=armv7-a + # -mfloat-abi=softfp -mfpu=vfpv3-d16" that does not trigger neon + # optimizations but should (as this configuration does not exist anymore). + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon ") + endif() +endif() + +list(LENGTH WEBP_SIMD_FLAGS WEBP_SIMD_FLAGS_LENGTH) +math(EXPR WEBP_SIMD_FLAGS_RANGE "${WEBP_SIMD_FLAGS_LENGTH} - 1") + +foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE}) + # With Emscripten 2.0.9 -msimd128 -mfpu=neon will enable NEON, but the + # source will fail to compile. + if(EMSCRIPTEN AND ${I_SIMD} GREATER_EQUAL 2) + break() + endif() + + list(GET WEBP_SIMD_FLAGS ${I_SIMD} WEBP_SIMD_FLAG) + + # First try with no extra flag added as the compiler might have default flags + # (especially on Android). + unset(WEBP_HAVE_${WEBP_SIMD_FLAG} CACHE) + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS) + webp_check_compiler_flag(${WEBP_SIMD_FLAG} ${WEBP_ENABLE_SIMD}) + if(NOT WEBP_HAVE_${WEBP_SIMD_FLAG}) + list(GET SIMD_ENABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG) + if(EMSCRIPTEN) + set(SIMD_COMPILE_FLAG "-msimd128 ${SIMD_COMPILE_FLAG}") + endif() + set(CMAKE_REQUIRED_FLAGS ${SIMD_COMPILE_FLAG}) + webp_check_compiler_flag(${WEBP_SIMD_FLAG} ${WEBP_ENABLE_SIMD}) + else() + if(MSVC AND SIMD_ENABLE_FLAGS) + # The detection for SSE2/SSE4 support under MSVC is based on the compiler + # version so e.g., clang-cl will require flags to enable the assembly. + list(GET SIMD_ENABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG) + else() + set(SIMD_COMPILE_FLAG " ") + endif() + endif() + # Check which files we should include or not. + list(GET WEBP_SIMD_FILE_EXTENSIONS ${I_SIMD} WEBP_SIMD_FILE_EXTENSION) + file(GLOB SIMD_FILES "${CMAKE_CURRENT_LIST_DIR}/../" + "src/dsp/*${WEBP_SIMD_FILE_EXTENSION}") + if(WEBP_HAVE_${WEBP_SIMD_FLAG}) + # Memorize the file and flags. + foreach(FILE ${SIMD_FILES}) + list(APPEND WEBP_SIMD_FILES_TO_INCLUDE ${FILE}) + list(APPEND WEBP_SIMD_FLAGS_TO_INCLUDE ${SIMD_COMPILE_FLAG}) + endforeach() + else() + # Remove the file from the list. + foreach(FILE ${SIMD_FILES}) + list(APPEND WEBP_SIMD_FILES_NOT_TO_INCLUDE ${FILE}) + endforeach() + # Explicitly disable SIMD. + if(SIMD_DISABLE_FLAGS) + list(GET SIMD_DISABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG) + include(CheckCCompilerFlag) + if(SIMD_COMPILE_FLAG) + # Between 3.17.0 and 3.18.2 check_cxx_compiler_flag() sets a normal + # variable at parent scope while check_cxx_source_compiles() continues + # to set an internal cache variable, so we unset both to avoid the + # failure / success state persisting between checks. See + # https://gitlab.kitware.com/cmake/cmake/-/issues/21207. + unset(HAS_COMPILE_FLAG) + unset(HAS_COMPILE_FLAG CACHE) + check_c_compiler_flag(${SIMD_COMPILE_FLAG} HAS_COMPILE_FLAG) + if(HAS_COMPILE_FLAG) + # Do one more check for Clang to circumvent CMake issue 13194. + if(COMMAND check_compiler_flag_common_patterns) + # Only in CMake 3.0 and above. + check_compiler_flag_common_patterns(COMMON_PATTERNS) + else() + set(COMMON_PATTERNS) + endif() + set(CMAKE_REQUIRED_DEFINITIONS ${SIMD_COMPILE_FLAG}) + check_c_source_compiles("int main(void) {return 0;}" + FLAG_${SIMD_COMPILE_FLAG} + FAIL_REGEX + "warning: argument unused during compilation:" + ${COMMON_PATTERNS}) + if(NOT FLAG_${SIMD_COMPILE_FLAG}) + unset(HAS_COMPILE_FLAG) + unset(HAS_COMPILE_FLAG CACHE) + endif() + endif() + if(HAS_COMPILE_FLAG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_COMPILE_FLAG}") + endif() + endif() + endif() + endif() + cmake_pop_check_state() +endforeach() diff --git a/internal/libwebp-1.2.1/cmake/deps.cmake b/internal/libwebp-1.2.1/cmake/deps.cmake new file mode 100644 index 0000000..e00611f --- /dev/null +++ b/internal/libwebp-1.2.1/cmake/deps.cmake @@ -0,0 +1,180 @@ +# Copyright (c) 2021 Google LLC. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE 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. + +# Generate the config.h to compile with specific intrinsics / libs. + +# Check for compiler options. +include(CheckCSourceCompiles) +check_c_source_compiles(" + int main(void) { + (void)__builtin_bswap16(0); + return 0; + } + " HAVE_BUILTIN_BSWAP16) +check_c_source_compiles(" + int main(void) { + (void)__builtin_bswap32(0); + return 0; + } + " HAVE_BUILTIN_BSWAP32) +check_c_source_compiles(" + int main(void) { + (void)__builtin_bswap64(0); + return 0; + } + " HAVE_BUILTIN_BSWAP64) + +# Check for libraries. +if(WEBP_USE_THREAD) + find_package(Threads) + if(Threads_FOUND) + # work around cmake bug on QNX (https://cmake.org/Bug/view.php?id=11333) + if(CMAKE_USE_PTHREADS_INIT AND NOT CMAKE_SYSTEM_NAME STREQUAL "QNX") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") + endif() + check_c_source_compiles(" + #include + int main (void) { + int attr = PTHREAD_PRIO_INHERIT; + return attr; + } + " FLAG_HAVE_PTHREAD_PRIO_INHERIT) + set(HAVE_PTHREAD_PRIO_INHERIT ${FLAG_HAVE_PTHREAD_PRIO_INHERIT}) + list(APPEND WEBP_DEP_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + endif() + set(WEBP_USE_THREAD ${Threads_FOUND}) +endif() + +# TODO: this seems unused, check with autotools. +set(LT_OBJDIR ".libs/") + +# Only useful for vwebp, so useless for now. +find_package(OpenGL) +set(WEBP_HAVE_GL ${OPENGL_FOUND}) + +# Check if we need to link to the C math library. We do not look for it as it is +# not found when cross-compiling, while it is here. +check_c_source_compiles(" + #include + int main(int argc, char** argv) { + return (int)pow(argc, 2.5); + } + " HAVE_MATH_LIBRARY) +if(NOT HAVE_MATH_LIBRARY) + message(STATUS "Adding -lm flag.") + list(APPEND WEBP_DEP_LIBRARIES m) +endif() + +# Find the standard image libraries. +set(WEBP_DEP_IMG_LIBRARIES) +set(WEBP_DEP_IMG_INCLUDE_DIRS) +foreach(I_LIB PNG JPEG TIFF) + find_package(${I_LIB}) + set(WEBP_HAVE_${I_LIB} ${${I_LIB}_FOUND}) + if(${I_LIB}_FOUND) + list(APPEND WEBP_DEP_IMG_LIBRARIES ${${I_LIB}_LIBRARIES}) + list(APPEND WEBP_DEP_IMG_INCLUDE_DIRS ${${I_LIB}_INCLUDE_DIR} + ${${I_LIB}_INCLUDE_DIRS}) + endif() +endforeach() +if(WEBP_DEP_IMG_INCLUDE_DIRS) + list(REMOVE_DUPLICATES WEBP_DEP_IMG_INCLUDE_DIRS) +endif() + +# GIF detection, gifdec isn't part of the imageio lib. +include(CMakePushCheckState) +set(WEBP_DEP_GIF_LIBRARIES) +set(WEBP_DEP_GIF_INCLUDE_DIRS) +find_package(GIF) +set(WEBP_HAVE_GIF ${GIF_FOUND}) +if(GIF_FOUND) + # GIF find_package only locates the header and library, it doesn't fail + # compile tests when detecting the version, but falls back to 3 (as of at + # least cmake 3.7.2). Make sure the library links to avoid incorrect detection + # when cross compiling. + cmake_push_check_state() + set(CMAKE_REQUIRED_LIBRARIES ${GIF_LIBRARIES}) + set(CMAKE_REQUIRED_INCLUDES ${GIF_INCLUDE_DIR}) + check_c_source_compiles(" + #include + int main(void) { + (void)DGifOpenFileHandle; + return 0; + } + " GIF_COMPILES) + cmake_pop_check_state() + if(GIF_COMPILES) + list(APPEND WEBP_DEP_GIF_LIBRARIES ${GIF_LIBRARIES}) + list(APPEND WEBP_DEP_GIF_INCLUDE_DIRS ${GIF_INCLUDE_DIR}) + else() + unset(GIF_FOUND) + endif() +endif() + +# Check for specific headers. +include(CheckIncludeFiles) +check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS) +check_include_files(dlfcn.h HAVE_DLFCN_H) +check_include_files(GLUT/glut.h HAVE_GLUT_GLUT_H) +check_include_files(GL/glut.h HAVE_GL_GLUT_H) +check_include_files(inttypes.h HAVE_INTTYPES_H) +check_include_files(memory.h HAVE_MEMORY_H) +check_include_files(OpenGL/glut.h HAVE_OPENGL_GLUT_H) +check_include_files(shlwapi.h HAVE_SHLWAPI_H) +check_include_files(stdint.h HAVE_STDINT_H) +check_include_files(stdlib.h HAVE_STDLIB_H) +check_include_files(strings.h HAVE_STRINGS_H) +check_include_files(string.h HAVE_STRING_H) +check_include_files(sys/stat.h HAVE_SYS_STAT_H) +check_include_files(sys/types.h HAVE_SYS_TYPES_H) +check_include_files(unistd.h HAVE_UNISTD_H) +check_include_files(wincodec.h HAVE_WINCODEC_H) +check_include_files(windows.h HAVE_WINDOWS_H) + +# Windows specifics +if(HAVE_WINCODEC_H) + list(APPEND WEBP_DEP_LIBRARIES + shlwapi + ole32 + windowscodecs) +endif() + +# Check for SIMD extensions. +include(${CMAKE_CURRENT_LIST_DIR}/cpu.cmake) + +# Define extra info. +set(PACKAGE ${PROJECT_NAME}) +set(PACKAGE_NAME ${PROJECT_NAME}) + +# Read from configure.ac. +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_AC) +string(REGEX MATCHALL + "\\[([0-9a-z\\.:/]*)\\]" + CONFIGURE_AC_PACKAGE_INFO + ${CONFIGURE_AC}) +function(strip_bracket VAR) + string(LENGTH ${${VAR}} TMP_LEN) + math(EXPR TMP_LEN ${TMP_LEN}-2) + string(SUBSTRING ${${VAR}} + 1 + ${TMP_LEN} + TMP_SUB) + set(${VAR} ${TMP_SUB} PARENT_SCOPE) +endfunction() + +list(GET CONFIGURE_AC_PACKAGE_INFO 1 PACKAGE_VERSION) +strip_bracket(PACKAGE_VERSION) +list(GET CONFIGURE_AC_PACKAGE_INFO 2 PACKAGE_BUGREPORT) +strip_bracket(PACKAGE_BUGREPORT) +list(GET CONFIGURE_AC_PACKAGE_INFO 3 PACKAGE_URL) +strip_bracket(PACKAGE_URL) + +# Build more info. +set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") +set(PACKAGE_TARNAME ${PACKAGE_NAME}) +set(VERSION ${PACKAGE_VERSION}) diff --git a/internal/libwebp-1.2.1/codereview.settings b/internal/libwebp-1.2.1/codereview.settings new file mode 100644 index 0000000..ccba2ee --- /dev/null +++ b/internal/libwebp-1.2.1/codereview.settings @@ -0,0 +1,4 @@ +# This file is used by git cl to get repository specific information. +GERRIT_HOST: True +CODE_REVIEW_SERVER: chromium-review.googlesource.com +GERRIT_SQUASH_UPLOADS: False diff --git a/internal/libwebp-1.2.1/configure.ac b/internal/libwebp-1.2.1/configure.ac new file mode 100644 index 0000000..7d5b8d2 --- /dev/null +++ b/internal/libwebp-1.2.1/configure.ac @@ -0,0 +1,798 @@ +AC_INIT([libwebp], [1.2.1], + [https://bugs.chromium.org/p/webp],, + [http://developers.google.com/speed/webp]) +AC_CANONICAL_HOST +AC_PREREQ([2.60]) +AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) + +dnl === automake >= 1.12 requires this for 'unusual archivers' support. +dnl === it must occur before LT_INIT (AC_PROG_LIBTOOL). +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) + +AC_PROG_LIBTOOL +AC_PROG_SED +AM_PROG_CC_C_O + +dnl === Enable less verbose output when building. +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl == test endianness +AC_C_BIGENDIAN + +dnl === SET_IF_UNSET(shell_var, value) +dnl === Set the shell variable 'shell_var' to 'value' if it is unset. +AC_DEFUN([SET_IF_UNSET], [test "${$1+set}" = "set" || $1=$2]) + +AC_ARG_ENABLE([everything], + AS_HELP_STRING([--enable-everything], + [Enable all optional targets. These can still be + disabled with --disable-target]), + [SET_IF_UNSET([enable_libwebpdecoder], [$enableval]) + SET_IF_UNSET([enable_libwebpdemux], [$enableval]) + SET_IF_UNSET([enable_libwebpextras], [$enableval]) + SET_IF_UNSET([enable_libwebpmux], [$enableval])]) + +dnl === Check whether libwebpmux should be built +AC_MSG_CHECKING(whether libwebpmux is to be built) +AC_ARG_ENABLE([libwebpmux], + AS_HELP_STRING([--disable-libwebpmux], + [Disable libwebpmux @<:@default=no@:>@]), + [], [enable_libwebpmux=yes]) +AC_MSG_RESULT(${enable_libwebpmux-no}) +AM_CONDITIONAL([BUILD_MUX], [test "$enable_libwebpmux" = "yes"]) + +dnl === Check whether libwebpdemux should be built +AC_MSG_CHECKING(whether libwebpdemux is to be built) +AC_ARG_ENABLE([libwebpdemux], + AS_HELP_STRING([--disable-libwebpdemux], + [Disable libwebpdemux @<:@default=no@:>@]), + [], [enable_libwebpdemux=yes]) +AC_MSG_RESULT(${enable_libwebpdemux-no}) +AM_CONDITIONAL([BUILD_DEMUX], [test "$enable_libwebpdemux" = "yes"]) + +dnl === Check whether decoder library should be built. +AC_MSG_CHECKING(whether decoder library is to be built) +AC_ARG_ENABLE([libwebpdecoder], + AS_HELP_STRING([--enable-libwebpdecoder], + [Build libwebpdecoder @<:@default=no@:>@])) +AC_MSG_RESULT(${enable_libwebpdecoder-no}) +AM_CONDITIONAL([BUILD_LIBWEBPDECODER], [test "$enable_libwebpdecoder" = "yes"]) + +dnl === Check whether libwebpextras should be built +AC_MSG_CHECKING(whether libwebpextras is to be built) +AC_ARG_ENABLE([libwebpextras], + AS_HELP_STRING([--enable-libwebpextras], + [Build libwebpextras @<:@default=no@:>@])) +AC_MSG_RESULT(${enable_libwebpextras-no}) +AM_CONDITIONAL([BUILD_EXTRAS], [test "$enable_libwebpextras" = "yes"]) + +dnl === If --enable-asserts is not defined, define NDEBUG + +AC_MSG_CHECKING(whether asserts are enabled) +AC_ARG_ENABLE([asserts], + AS_HELP_STRING([--enable-asserts], + [Enable assert checks])) +if test "x${enable_asserts-no}" = "xno"; then + AM_CPPFLAGS="${AM_CPPFLAGS} -DNDEBUG" +fi +AC_MSG_RESULT(${enable_asserts-no}) +AC_SUBST([AM_CPPFLAGS]) + +AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=DIR], + [Path to the pkgconfig directory @<:@LIBDIR/pkgconfig@:>@]), + [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) +AC_SUBST([pkgconfigdir]) + +dnl === TEST_AND_ADD_CFLAGS(var, flag) +dnl === Checks whether $CC supports 'flag' and adds it to 'var' +dnl === on success. +AC_DEFUN([TEST_AND_ADD_CFLAGS], + [SAVED_CFLAGS="$CFLAGS" + CFLAGS="-Werror $2" + AC_MSG_CHECKING([whether $CC supports $2]) + dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition. + AC_COMPILE_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])], + [AC_MSG_RESULT([yes])] + dnl Simply append the variable avoiding a + dnl compatibility ifdef for AS_VAR_APPEND as this + dnl variable shouldn't grow all that large. + [$1="${$1} $2"], + [AC_MSG_RESULT([no])]) + CFLAGS="$SAVED_CFLAGS"]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-fvisibility=hidden]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wall]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wconstant-conversion]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wdeclaration-after-statement]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wextra]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wfloat-conversion]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-nonliteral]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-security]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-declarations]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-prototypes]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wparentheses-equality]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshadow]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshorten-64-to-32]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wundef]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code-aggressive]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused-but-set-variable]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused]) +TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wvla]) +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62040 +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61622 +AS_IF([test "$GCC" = "yes" ], [ + gcc_version=`$CC -dumpversion` + gcc_wht_bug="" + case "$host_cpu" in + aarch64|arm64) + case "$gcc_version" in + 4.9|4.9.0|4.9.1) gcc_wht_bug=yes ;; + esac + esac + AS_IF([test "$gcc_wht_bug" = "yes"], [ + TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-frename-registers])])]) +# Use -flax-vector-conversions, if available, when building intrinsics with +# older versions of gcc. The flag appeared in 4.3.x, but if backported, and +# -fno-lax-vector-conversions is set, errors may occur with the intrinsics +# files along with the older system includes, e.g., emmintrin.h. +# Originally observed with cc (GCC) 4.2.1 20070831 patched [FreeBSD] (9.3). +# https://bugs.chromium.org/p/webp/issues/detail?id=274 +AS_IF([test "$GCC" = "yes" ], [ + case "$host_cpu" in + amd64|i?86|x86_64) + AC_COMPILE_IFELSE( + dnl only check for -flax-vector-conversions with older gcc, skip + dnl clang as it reports itself as 4.2.1, but the flag isn't needed. + [AC_LANG_SOURCE([#if !defined(__clang__) && defined(__GNUC__) && \ + ((__GNUC__ << 8) | __GNUC_MINOR__) < 0x403 + #error old gcc + #endif + int main(void) { return 0; } + ])],, + [TEST_AND_ADD_CFLAGS([INTRINSICS_CFLAGS], + [-flax-vector-conversions])]) + ;; + esac]) +AC_SUBST([AM_CFLAGS]) + +dnl === Check for machine specific flags +AC_ARG_ENABLE([sse4.1], + AS_HELP_STRING([--disable-sse4.1], + [Disable detection of SSE4.1 support + @<:@default=auto@:>@])) + +AS_IF([test "x$enable_sse4_1" != "xno" -a "x$enable_sse2" != "xno"], [ + SSE41_FLAGS="$INTRINSICS_CFLAGS $SSE41_FLAGS" + TEST_AND_ADD_CFLAGS([SSE41_FLAGS], [-msse4.1]) + AS_IF([test -n "$SSE41_FLAGS"], [ + SAVED_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $SSE41_FLAGS" + AC_CHECK_HEADER([smmintrin.h], + [AC_DEFINE(WEBP_HAVE_SSE41, [1], + [Set to 1 if SSE4.1 is supported])], + [SSE41_FLAGS=""]) + CFLAGS=$SAVED_CFLAGS]) + AC_SUBST([SSE41_FLAGS])]) + +AC_ARG_ENABLE([sse2], + AS_HELP_STRING([--disable-sse2], + [Disable detection of SSE2 support + @<:@default=auto@:>@])) + +AS_IF([test "x$enable_sse2" != "xno"], [ + SSE2_FLAGS="$INTRINSICS_CFLAGS $SSE2_FLAGS" + TEST_AND_ADD_CFLAGS([SSE2_FLAGS], [-msse2]) + AS_IF([test -n "$SSE2_FLAGS"], [ + SAVED_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $SSE2_FLAGS" + AC_CHECK_HEADER([emmintrin.h], + [AC_DEFINE(WEBP_HAVE_SSE2, [1], + [Set to 1 if SSE2 is supported])], + [SSE2_FLAGS=""]) + CFLAGS=$SAVED_CFLAGS]) + AC_SUBST([SSE2_FLAGS])]) + +AC_ARG_ENABLE([neon], + AS_HELP_STRING([--disable-neon], + [Disable detection of NEON support + @<:@default=auto@:>@])) + +AC_ARG_ENABLE([neon_rtcd], + AS_HELP_STRING([--disable-neon-rtcd], + [Disable runtime detection of NEON support via + /proc/cpuinfo on Linux hosts + @<:@default=auto@:>@])) +# For ARM(7) hosts: +# Both NEON flags unset and NEON support detected = build all modules with NEON +# NEON detected with the use of -mfpu=neon = build only NEON modules with NEON +AS_IF([test "x$enable_neon" != "xno"], [ + case "$host_cpu" in + arm|armv7*) + # Test for NEON support without flags before falling back to -mfpu=neon + for flag in '' '-mfpu=neon'; do + LOCAL_NEON_FLAGS="$INTRINSICS_CFLAGS $NEON_FLAGS" + TEST_AND_ADD_CFLAGS([LOCAL_NEON_FLAGS], [$flag]) + SAVED_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $LOCAL_NEON_FLAGS" + + dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition. + AC_COMPILE_IFELSE([AC_LANG_SOURCE([ + #include + int main(void) { + int8x8_t v = vdup_n_s8(0); + (void)v; + return 0; + }])], + [NEON_FLAGS="$(echo $LOCAL_NEON_FLAGS | $SED 's/^ *//')" + AS_IF([test -n "$NEON_FLAGS"], [ + AS_IF([test "${host_os%%-*}" = "linux" -o \ + "x$enable_neon_rtcd" = "xno"], [ + CFLAGS=$SAVED_CFLAGS + AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported]) + break + ],[ + AC_MSG_WARN(m4_normalize([NEON runtime cpu-detection is + unavailable for ${host_os%%-*}. Force + with CFLAGS=-mfpu=neon or + --disable-neon-rtcd.])) + enable_neon_rtcd=no + NEON_FLAGS="" + ]) + ],[ + CFLAGS=$SAVED_CFLAGS + AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported]) + break + ])]) + CFLAGS=$SAVED_CFLAGS + done + + AS_IF([test -n "$NEON_FLAGS"], [ + # If NEON is available and rtcd is disabled apply NEON_FLAGS globally. + AS_IF([test "x$enable_neon_rtcd" = "xno"], [ + AM_CFLAGS="$AM_CFLAGS $NEON_FLAGS" + NEON_FLAGS=""], + [AC_DEFINE(WEBP_HAVE_NEON_RTCD, [1], + [Set to 1 if runtime detection of NEON is enabled])])]) + + case "$host_os" in + *android*) AC_CHECK_HEADERS([cpu-features.h]) ;; + esac + ;; + aarch64*|arm64*) + AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported]) + ;; + esac + AC_SUBST([NEON_FLAGS])]) + +dnl === CLEAR_LIBVARS([var_pfx]) +dnl === Clears _{INCLUDES,LIBS}. +AC_DEFUN([CLEAR_LIBVARS], [$1_INCLUDES=""; $1_LIBS=""]) + +dnl === WITHLIB_OPTION([opt_pfx], [outvar_pfx]) +dnl === Defines --with-{include,lib}dir options which set +dnl === the variables _{INCLUDES,LIBS}. +AC_DEFUN([WITHLIB_OPTION], + [AC_ARG_WITH([$1includedir], + AS_HELP_STRING([--with-$1includedir=DIR], + [use $2 includes from DIR]), + $2_INCLUDES="-I$withval") + AC_ARG_WITH([$1libdir], + AS_HELP_STRING([--with-$1libdir=DIR], + [use $2 libraries from DIR]), + [$2_LIBS="-L$withval"])]) + +dnl === LIBCHECK_PROLOGUE([var_pfx]) +dnl === Caches the current values of CPPFLAGS/LIBS in SAVED_* then +dnl === prepends the current values with _{INCLUDES,LIBS}. +AC_DEFUN([LIBCHECK_PROLOGUE], + [SAVED_CPPFLAGS=$CPPFLAGS + SAVED_LIBS=$LIBS + CPPFLAGS="$$1_INCLUDES $CPPFLAGS" + LIBS="$$1_LIBS $LIBS"]) + +dnl === LIBCHECK_EPILOGUE([var_pfx]) +dnl === Restores the values of CPPFLAGS/LIBS from SAVED_* and exports +dnl === _{INCLUDES,LIBS} with AC_SUBST. +AC_DEFUN([LIBCHECK_EPILOGUE], + [AC_SUBST($1_LIBS) + AC_SUBST($1_INCLUDES) + CPPFLAGS=$SAVED_CPPFLAGS + LIBS=$SAVED_LIBS]) + +dnl === Check for gcc builtins + +dnl === CHECK_FOR_BUILTIN([builtin], [param], [define]) +dnl === links a C AC_LANG_PROGRAM, with () +dnl === AC_DEFINE'ing if successful. +AC_DEFUN([CHECK_FOR_BUILTIN], + [AC_LANG_PUSH([C]) + AC_MSG_CHECKING([for $1]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([], [(void)$1($2)])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([$3], [1], + [Set to 1 if $1 is available])], + [AC_MSG_RESULT([no])]), + AC_LANG_POP]) + +dnl AC_CHECK_FUNC doesn't work with builtin's. +CHECK_FOR_BUILTIN([__builtin_bswap16], [1u << 15], [HAVE_BUILTIN_BSWAP16]) +CHECK_FOR_BUILTIN([__builtin_bswap32], [1u << 31], [HAVE_BUILTIN_BSWAP32]) +CHECK_FOR_BUILTIN([__builtin_bswap64], [1ull << 63], [HAVE_BUILTIN_BSWAP64]) + +dnl === Check for pthread support +AC_ARG_ENABLE([threading], + AS_HELP_STRING([--disable-threading], + [Disable detection of thread support]),, + [enable_threading=yes]) +if test "$enable_threading" = "yes"; then + AC_MSG_NOTICE([checking for threading support...]) + AX_PTHREAD([AC_DEFINE([WEBP_USE_THREAD], [1], + [Undefine this to disable thread support.]) + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + CC="$PTHREAD_CC" + ], + [AC_CHECK_FUNC([_beginthreadex], + [AC_DEFINE([WEBP_USE_THREAD], [1], + [Undefine this to disable thread + support.])], + [enable_threading=no])]) +fi +AC_MSG_NOTICE([checking if threading is enabled... ${enable_threading-no}]) + +dnl === check for OpenGL/GLUT support === + +AC_ARG_ENABLE([gl], AS_HELP_STRING([--disable-gl], + [Disable detection of OpenGL support + @<:@default=auto@:>@])) +AS_IF([test "x$enable_gl" != "xno"], [ + CLEAR_LIBVARS([GL]) + WITHLIB_OPTION([gl], [GL]) + + LIBCHECK_PROLOGUE([GL]) + + glut_cflags="none" + glut_ldflags="none" + case $host_os in + darwin*) + # Special case for OSX builds. Append these to give the user a chance to + # override with --with-gl* + glut_cflags="$glut_cflags|-framework GLUT -framework OpenGL" + glut_ldflags="$glut_ldflags|-framework GLUT -framework OpenGL" + # quiet deprecation warnings for glut + TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wno-deprecated-declarations]) + ;; + esac + + GLUT_SAVED_CPPFLAGS="$CPPFLAGS" + SAVED_IFS="$IFS" + IFS="|" + for flag in $glut_cflags; do + # restore IFS immediately as the autoconf macros may need the default. + IFS="$SAVED_IFS" + unset ac_cv_header_GL_glut_h + unset ac_cv_header_OpenGL_glut_h + + case $flag in + none) ;; + *) CPPFLAGS="$flag $CPPFLAGS";; + esac + AC_CHECK_HEADERS([GL/glut.h GLUT/glut.h OpenGL/glut.h], + [glut_headers=yes; + test "$flag" = "none" || GL_INCLUDES="$CPPFLAGS"; + break]) + CPPFLAGS="$GLUT_SAVED_CPPFLAGS" + test "$glut_headers" = "yes" && break + done + IFS="$SAVED_IFS" + + if test "$glut_headers" = "yes"; then + AC_LANG_PUSH([C]) + GLUT_SAVED_LDFLAGS="$LDFLAGS" + SAVED_IFS="$IFS" + IFS="|" + for flag in $glut_ldflags; do + # restore IFS immediately as the autoconf macros may need the default. + IFS="$SAVED_IFS" + unset ac_cv_search_glBegin + + case $flag in + none) ;; + *) LDFLAGS="$flag $LDFLAGS";; + esac + + # find libGL + GL_SAVED_LIBS="$LIBS" + AC_SEARCH_LIBS([glBegin], [GL OpenGL opengl32]) + LIBS="$GL_SAVED_LIBS" + + # A direct link to libGL may not be necessary on e.g., linux. + GLUT_SAVED_LIBS="$LIBS" + for lib in "" "-lglut" "-lglut $ac_cv_search_glBegin"; do + LIBS="$lib" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([ + #ifdef __cplusplus + # define EXTERN_C extern "C" + #else + # define EXTERN_C + #endif + EXTERN_C char glOrtho(); + EXTERN_C char glutMainLoop(); + ],[ + glOrtho(); + glutMainLoop(); + ]) + ], + AC_DEFINE(WEBP_HAVE_GL, [1], + [Set to 1 if OpenGL is supported]) + [glut_support=yes], [] + ) + if test "$glut_support" = "yes"; then + GL_LIBS="$LDFLAGS $lib" + break + fi + done + LIBS="$GLUT_SAVED_LIBS" + LDFLAGS="$GLUT_SAVED_LDFLAGS" + test "$glut_support" = "yes" && break + done + IFS="$SAVED_IFS" + AC_LANG_POP + fi + + LIBCHECK_EPILOGUE([GL]) + + if test "$glut_support" = "yes" -a "$enable_libwebpdemux" = "yes"; then + build_vwebp=yes + else + AC_MSG_NOTICE( + m4_normalize([Not building vwebp. + OpenGL libraries and --enable-libwebpdemux are required.])) + fi +]) +AM_CONDITIONAL([BUILD_VWEBP], [test "$build_vwebp" = "yes"]) + +dnl === check for SDL support === + +AC_ARG_ENABLE([sdl], + AS_HELP_STRING([--disable-sdl], + [Disable detection of SDL support + @<:@default=auto@:>@])) +AS_IF([test "x$enable_sdl" != "xno"], [ + CLEAR_LIBVARS([SDL]) + AC_PATH_PROGS([LIBSDL_CONFIG], [sdl-config]) + if test -n "$LIBSDL_CONFIG"; then + SDL_INCLUDES=`$LIBSDL_CONFIG --cflags` + SDL_LIBS="`$LIBSDL_CONFIG --libs`" + fi + + WITHLIB_OPTION([sdl], [SDL]) + + sdl_header="no" + LIBCHECK_PROLOGUE([SDL]) + AC_CHECK_HEADER([SDL/SDL.h], [sdl_header="SDL/SDL.h"], + [AC_CHECK_HEADER([SDL.h], [sdl_header="SDL.h"], + [AC_MSG_WARN(SDL library not available - no sdl.h)])]) + if test x"$sdl_header" != "xno"; then + AC_LANG_PUSH(C) + SDL_SAVED_LIBS="$LIBS" + for lib in "" "-lSDL" "-lSDLmain -lSDL"; do + LIBS="$SDL_SAVED_LIBS $lib" + # Perform a full link to ensure SDL_main is resolved if needed. + AC_LINK_IFELSE( + [AC_LANG_SOURCE([ + #include <$sdl_header> + int main(int argc, char** argv) { + SDL_Init(0); + return 0; + }])], + [SDL_LIBS="$LDFLAGS $LIBS" + SDL_INCLUDES="$SDL_INCLUDES -DWEBP_HAVE_SDL" + AC_DEFINE(WEBP_HAVE_SDL, [1], + [Set to 1 if SDL library is installed]) + sdl_support=yes] + ) + if test x"$sdl_support" = "xyes"; then + break + fi + done + # LIBS is restored by LIBCHECK_EPILOGUE + AC_LANG_POP + if test x"$sdl_header" = "xSDL.h"; then + SDL_INCLUDES="$SDL_INCLUDES -DWEBP_HAVE_JUST_SDL_H" + fi + fi + LIBCHECK_EPILOGUE([SDL]) + + if test x"$sdl_support" = "xyes"; then + build_vwebp_sdl=yes + else + AC_MSG_NOTICE([Not building vwebp-sdl. SDL library is required.]) + fi +]) + +AM_CONDITIONAL([BUILD_VWEBP_SDL], [test "$build_vwebp_sdl" = "yes"]) + +dnl === check for PNG support === + +AC_ARG_ENABLE([png], AS_HELP_STRING([--disable-png], + [Disable detection of PNG format support + @<:@default=auto@:>@])) +AS_IF([test "x$enable_png" != "xno"], [ + CLEAR_LIBVARS([PNG]) + AC_PATH_PROGS([LIBPNG_CONFIG], + [libpng-config libpng16-config libpng15-config libpng14-config \ + libpng12-config]) + if test -n "$LIBPNG_CONFIG"; then + PNG_INCLUDES=`$LIBPNG_CONFIG --cflags` + PNG_LIBS="`$LIBPNG_CONFIG --ldflags`" + fi + + WITHLIB_OPTION([png], [PNG]) + + LIBCHECK_PROLOGUE([PNG]) + AC_CHECK_HEADER(png.h, + AC_SEARCH_LIBS(png_get_libpng_ver, [png], + [test "$ac_cv_search_png_get_libpng_ver" = "none required" \ + || PNG_LIBS="$PNG_LIBS $ac_cv_search_png_get_libpng_ver" + PNG_INCLUDES="$PNG_INCLUDES -DWEBP_HAVE_PNG" + AC_DEFINE(WEBP_HAVE_PNG, [1], + [Set to 1 if PNG library is installed]) + png_support=yes + ], + [AC_MSG_WARN(Optional png library not found) + PNG_LIBS="" + PNG_INCLUDES="" + ], + [$MATH_LIBS]), + [AC_MSG_WARN(png library not available - no png.h) + PNG_LIBS="" + PNG_INCLUDES="" + ], + ) + LIBCHECK_EPILOGUE([PNG]) +]) + +dnl === check for JPEG support === + +AC_ARG_ENABLE([jpeg], + AS_HELP_STRING([--disable-jpeg], + [Disable detection of JPEG format support + @<:@default=auto@:>@])) +AS_IF([test "x$enable_jpeg" != "xno"], [ + CLEAR_LIBVARS([JPEG]) + WITHLIB_OPTION([jpeg], [JPEG]) + + LIBCHECK_PROLOGUE([JPEG]) + AC_CHECK_HEADER(jpeglib.h, + AC_CHECK_LIB(jpeg, jpeg_set_defaults, + [JPEG_LIBS="$JPEG_LIBS -ljpeg" + JPEG_INCLUDES="$JPEG_INCLUDES -DWEBP_HAVE_JPEG" + AC_DEFINE(WEBP_HAVE_JPEG, [1], + [Set to 1 if JPEG library is installed]) + jpeg_support=yes + ], + AC_MSG_WARN(Optional jpeg library not found), + [$MATH_LIBS]), + AC_MSG_WARN(jpeg library not available - no jpeglib.h) + ) + LIBCHECK_EPILOGUE([JPEG]) +]) + +dnl === check for TIFF support === + +AC_ARG_ENABLE([tiff], + AS_HELP_STRING([--disable-tiff], + [Disable detection of TIFF format support + @<:@default=auto@:>@])) +AS_IF([test "x$enable_tiff" != "xno"], [ + CLEAR_LIBVARS([TIFF]) + WITHLIB_OPTION([tiff], [TIFF]) + + LIBCHECK_PROLOGUE([TIFF]) + AC_CHECK_HEADER(tiffio.h, + AC_CHECK_LIB(tiff, TIFFGetVersion, + [TIFF_LIBS="$TIFF_LIBS -ltiff" + TIFF_INCLUDES="$TIFF_INCLUDES -DWEBP_HAVE_TIFF" + AC_DEFINE(WEBP_HAVE_TIFF, [1], + [Set to 1 if TIFF library is installed]) + tiff_support=yes + ], + AC_MSG_WARN(Optional tiff library not found), + [$MATH_LIBS]), + AC_MSG_WARN(tiff library not available - no tiffio.h) + ) + LIBCHECK_EPILOGUE([TIFF]) +]) + +dnl === check for GIF support === + +AC_ARG_ENABLE([gif], AS_HELP_STRING([--disable-gif], + [Disable detection of GIF format support + @<:@default=auto@:>@])) +AS_IF([test "x$enable_gif" != "xno"], [ + CLEAR_LIBVARS([GIF]) + WITHLIB_OPTION([gif], [GIF]) + + LIBCHECK_PROLOGUE([GIF]) + AC_CHECK_HEADER(gif_lib.h, + AC_CHECK_LIB([gif], [DGifOpenFileHandle], + [GIF_LIBS="$GIF_LIBS -lgif" + AC_DEFINE(WEBP_HAVE_GIF, [1], + [Set to 1 if GIF library is installed]) + gif_support=yes + ], + AC_MSG_WARN(Optional gif library not found), + [$MATH_LIBS]), + AC_MSG_WARN(gif library not available - no gif_lib.h) + ) + LIBCHECK_EPILOGUE([GIF]) + + if test "$gif_support" = "yes" -a \ + "$enable_libwebpdemux" = "yes"; then + build_anim_diff=yes + else + AC_MSG_NOTICE( + [Not building anim_diff. libgif and --enable-libwebpdemux are required.]) + fi + + if test "$gif_support" = "yes" -a \ + "$enable_libwebpmux" = "yes"; then + build_gif2webp=yes + else + AC_MSG_NOTICE( + [Not building gif2webp. libgif and --enable-libwebpmux are required.]) + fi +]) +AM_CONDITIONAL([BUILD_ANIMDIFF], [test "${build_anim_diff}" = "yes"]) +AM_CONDITIONAL([BUILD_GIF2WEBP], [test "${build_gif2webp}" = "yes"]) + +if test "$enable_libwebpdemux" = "yes" -a "$enable_libwebpmux" = "yes"; then + build_img2webp=yes +else + AC_MSG_NOTICE( + m4_normalize([Not building img2webp. + --enable-libwebpdemux & --enable-libwebpmux are required.])) +fi +AM_CONDITIONAL([BUILD_IMG2WEBP], [test "${build_img2webp}" = "yes"]) + +if test "$enable_libwebpmux" = "yes"; then + build_webpinfo=yes +else + AC_MSG_NOTICE([Not building webpinfo. --enable-libwebpdemux is required.]) +fi +AM_CONDITIONAL([BUILD_WEBPINFO], [test "${build_webpinfo}" = "yes"]) + +dnl === check for WIC support === + +AC_ARG_ENABLE([wic], + AS_HELP_STRING([--disable-wic], + [Disable Windows Imaging Component (WIC) detection. + @<:@default=auto@:>@]),, + [enable_wic=yes]) + +case $host_os in +mingw*) +if test "$enable_wic" = "yes"; then + AC_CHECK_HEADERS([wincodec.h shlwapi.h windows.h]) + if test "$ac_cv_header_wincodec_h" = "yes"; then + AC_MSG_CHECKING(for Windows Imaging Component support) + SAVED_LIBS=$LIBS + LIBS="-lshlwapi -lole32 $LIBS" + # match include structure from [cd]webp.c + wic_headers=" + #define INITGUID + #define CINTERFACE + #define COBJMACROS + #define _WIN32_IE 0x500 + + #include + #include + #include + " + # test for functions from each lib and the GUID is created properly + wic_main=" + int main(void) { + CLSID_WICImagingFactory; + CoInitialize(NULL); + SHCreateStreamOnFile(NULL, 0, NULL); + return 0; + } + " + AC_LANG_PUSH(C) + AC_LINK_IFELSE( + [AC_LANG_SOURCE([ + $wic_headers + $wic_main])], + [wic_support=yes], + [wic_support=no] + ) + AC_LANG_POP + + test "$wic_support" = "yes" || LIBS=$SAVED_LIBS + AC_MSG_RESULT(${wic_support-no}) + fi +fi +esac + +dnl === If --enable-swap-16bit-csp is defined, add -DWEBP_SWAP_16BIT_CSP=1 + +USE_SWAP_16BIT_CSP="" +AC_MSG_CHECKING(if --enable-swap-16bit-csp option is specified) +AC_ARG_ENABLE([swap-16bit-csp], + AS_HELP_STRING([--enable-swap-16bit-csp], + [Enable byte swap for 16 bit colorspaces])) +if test "$enable_swap_16bit_csp" = "yes"; then + USE_SWAP_16BIT_CSP="-DWEBP_SWAP_16BIT_CSP=1" +fi +AC_MSG_RESULT(${enable_swap_16bit_csp-no}) +AC_SUBST(USE_SWAP_16BIT_CSP) + +dnl === If --disable-near-lossless is defined, add -DWEBP_NEAR_LOSSLESS=0 + +AC_DEFINE(WEBP_NEAR_LOSSLESS, [1], [Enable near lossless encoding]) +AC_MSG_CHECKING(if --disable-near-lossless option is specified) +AC_ARG_ENABLE([near_lossless], + AS_HELP_STRING([--disable-near-lossless], + [Disable near lossless encoding]), + [], [enable_near_lossless=yes]) +if test "$enable_near_lossless" = "no"; then + AC_DEFINE(WEBP_NEAR_LOSSLESS, [0], [Enable near lossless encoding]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + +dnl ========================= + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([src/webp/config.h]) +AC_CONFIG_FILES([Makefile src/Makefile man/Makefile \ + examples/Makefile extras/Makefile imageio/Makefile \ + src/dec/Makefile src/enc/Makefile src/dsp/Makefile \ + src/demux/Makefile src/mux/Makefile \ + src/utils/Makefile \ + src/libwebp.pc src/libwebpdecoder.pc \ + src/demux/libwebpdemux.pc src/mux/libwebpmux.pc]) + + +AC_OUTPUT + +AC_MSG_NOTICE([ +WebP Configuration Summary +-------------------------- + +Shared libraries: ${enable_shared} +Static libraries: ${enable_static} +Threading support: ${enable_threading-no} +libwebp: yes +libwebpdecoder: ${enable_libwebpdecoder-no} +libwebpdemux: ${enable_libwebpdemux-no} +libwebpmux: ${enable_libwebpmux-no} +libwebpextras: ${enable_libwebpextras-no} + +Tools: +cwebp : ${enable_libwebpdemux-no} + Input format support + ==================== + JPEG : ${jpeg_support-no} + PNG : ${png_support-no} + TIFF : ${tiff_support-no} + WIC : ${wic_support-no} +dwebp : ${enable_libwebpdemux-no} + Output format support + ===================== + PNG : ${png_support-no} + WIC : ${wic_support-no} +GIF support : ${gif_support-no} +anim_diff : ${build_anim_diff-no} +gif2webp : ${build_gif2webp-no} +img2webp : ${build_img2webp-no} +webpmux : ${enable_libwebpmux-no} +vwebp : ${build_vwebp-no} +webpinfo : ${build_webpinfo-no} +SDL support : ${sdl_support-no} +vwebp_sdl : ${build_vwebp_sdl-no} +]) diff --git a/internal/libwebp-1.2.1/doc/README b/internal/libwebp-1.2.1/doc/README new file mode 100644 index 0000000..7f23e0c --- /dev/null +++ b/internal/libwebp-1.2.1/doc/README @@ -0,0 +1,29 @@ + +Generate libwebp Container Spec Docs from Text Source +===================================================== + +HTML generation requires kramdown [1], easily installed as a +rubygem [2]. Rubygems installation should satisfy dependencies +automatically. + +[1]: http://kramdown.rubyforge.org/ +[2]: http://rubygems.org/ + +HTML generation can then be done from the project root: + +$ kramdown doc/webp-container-spec.txt --template doc/template.html > \ + doc/output/webp-container-spec.html + +kramdown can optionally syntax highlight code blocks, using CodeRay [3], +a dependency of kramdown that rubygems will install automatically. The +following will apply inline CSS styling; an external stylesheet is not +needed. + +$ kramdown doc/webp-lossless-bitstream-spec.txt --template \ + doc/template.html --coderay-css style --coderay-line-numbers ' ' \ + --coderay-default-lang c > \ + doc/output/webp-lossless-bitstream-spec.html + +Optimally, use kramdown 0.13.7 or newer if syntax highlighting desired. + +[3]: http://coderay.rubychan.de/ diff --git a/internal/libwebp-1.2.1/doc/TODO b/internal/libwebp-1.2.1/doc/TODO new file mode 100644 index 0000000..b0a9382 --- /dev/null +++ b/internal/libwebp-1.2.1/doc/TODO @@ -0,0 +1,13 @@ +, 20111004 + +* Determine that normative RFC 2119 terms (MUST, SHOULD, MAY, etc.) are + truly intended in all cases where capitalized. + +* Several passages could be made clearer. + + * Overall edit for scope. Portions are phrased as an introduction to + the 0.1.3 RIFF container additions, rather than a holistic guide to + WebP. + + * To wit, suggest s/[spec|specification]/guide/g . "Spec" can imply a + standards track; in any case it's too formal for a work in progress. diff --git a/internal/libwebp-1.2.1/doc/template.html b/internal/libwebp-1.2.1/doc/template.html new file mode 100644 index 0000000..5dbc289 --- /dev/null +++ b/internal/libwebp-1.2.1/doc/template.html @@ -0,0 +1,94 @@ + + + + + WebP Container Specification + + + + +<%= @body %> + + diff --git a/internal/libwebp-1.2.1/doc/webp-container-spec.txt b/internal/libwebp-1.2.1/doc/webp-container-spec.txt new file mode 100644 index 0000000..0f587e3 --- /dev/null +++ b/internal/libwebp-1.2.1/doc/webp-container-spec.txt @@ -0,0 +1,819 @@ + + + +WebP Container Specification +============================ + +* TOC placeholder +{:toc} + + +Introduction +------------ + +WebP is an image format that uses either (i) the VP8 key frame encoding +to compress image data in a lossy way, or (ii) the WebP lossless encoding +(and possibly other encodings in the future). These encoding schemes should +make it more efficient than currently used formats. It is optimized for fast +image transfer over the network (e.g., for websites). The WebP format has +feature parity (color profile, metadata, animation etc) with other formats as +well. This document describes the structure of a WebP file. + +The WebP container (i.e., RIFF container for WebP) allows feature support over +and above the basic use case of WebP (i.e., a file containing a single image +encoded as a VP8 key frame). The WebP container provides additional support +for: + + * **Lossless compression.** An image can be losslessly compressed, using the + WebP Lossless Format. + + * **Metadata.** An image may have metadata stored in EXIF or XMP formats. + + * **Transparency.** An image may have transparency, i.e., an alpha channel. + + * **Color Profile.** An image may have an embedded ICC profile as described + by the [International Color Consortium][iccspec]. + + * **Animation.** An image may have multiple frames with pauses between them, + making it an animation. + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", +"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this +document are to be interpreted as described in [RFC 2119][]. + +Bit numbering in chunk diagrams starts at `0` for the most significant bit +('MSB 0') as described in [RFC 1166][]. + +Terminology & Basics +------------------------ + +A WebP file contains either a still image (i.e., an encoded matrix of pixels) +or an [animation](#animation). Optionally, it can also contain transparency +information, color profile and metadata. In case we need to refer only to the +matrix of pixels, we will call it the _canvas_ of the image. + +Below are additional terms used throughout this document: + +_Reader/Writer_ + +: Code that reads WebP files is referred to as a _reader_, while code that + writes them is referred to as a _writer_. + +_uint16_ + +: A 16-bit, little-endian, unsigned integer. + +_uint24_ + +: A 24-bit, little-endian, unsigned integer. + +_uint32_ + +: A 32-bit, little-endian, unsigned integer. + +_FourCC_ + +: A _FourCC_ (four-character code) is a _uint32_ created by concatenating four + ASCII characters in little-endian order. + +_1-based_ + +: An unsigned integer field storing values offset by `-1`. e.g., Such a field + would store value _25_ as _24_. + + +RIFF File Format +---------------- + +The WebP file format is based on the RIFF (resource interchange file format) +document format. + +The basic element of a RIFF file is a _chunk_. It consists of: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Chunk FourCC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Chunk Size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Chunk Payload | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Chunk FourCC: 32 bits + +: ASCII four-character code used for chunk identification. + +Chunk Size: 32 bits (_uint32_) + +: The size of the chunk not including this field, the chunk identifier or + padding. + +Chunk Payload: _Chunk Size_ bytes + +: The data payload. If _Chunk Size_ is odd, a single padding byte -- that + SHOULD be `0` -- is added. + +_ChunkHeader('ABCD')_ + +: This is used to describe the _FourCC_ and _Chunk Size_ header of individual + chunks, where 'ABCD' is the FourCC for the chunk. This element's + size is 8 bytes. + +**Note:** RIFF has a convention that all-uppercase chunk FourCCs are standard +chunks that apply to any RIFF file format, while FourCCs specific to a file +format are all lowercase. WebP does not follow this convention. + + +WebP File Header +---------------- + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 'R' | 'I' | 'F' | 'F' | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | File Size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 'W' | 'E' | 'B' | 'P' | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +'RIFF': 32 bits + +: The ASCII characters 'R' 'I' 'F' 'F'. + +File Size: 32 bits (_uint32_) + +: The size of the file in bytes starting at offset 8. The maximum value of + this field is 2^32 minus 10 bytes and thus the size of the whole file is at + most 4GiB minus 2 bytes. + +'WEBP': 32 bits + +: The ASCII characters 'W' 'E' 'B' 'P'. + +A WebP file MUST begin with a RIFF header with the FourCC 'WEBP'. The file size +in the header is the total size of the chunks that follow plus `4` bytes for +the 'WEBP' FourCC. The file SHOULD NOT contain anything after it. As the size +of any chunk is even, the size given by the RIFF header is also even. The +contents of individual chunks will be described in the following sections. + + +Simple File Format (Lossy) +-------------------------- + +This layout SHOULD be used if the image requires _lossy_ encoding and does not +require transparency or other advanced features provided by the extended format. +Files with this layout are smaller and supported by older software. + +Simple WebP (lossy) file format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | WebP file header (12 bytes) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | VP8 chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +VP8 chunk: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ChunkHeader('VP8 ') | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | VP8 data | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +VP8 data: _Chunk Size_ bytes + +: VP8 bitstream data. + +The VP8 bitstream format specification can be found at [VP8 Data Format and +Decoding Guide][vp8spec]. Note that the VP8 frame header contains the VP8 frame +width and height. That is assumed to be the width and height of the canvas. + +The VP8 specification describes how to decode the image into Y'CbCr +format. To convert to RGB, Rec. 601 SHOULD be used. + + +Simple File Format (Lossless) +----------------------------- + +**Note:** Older readers may not support files using the lossless format. + +This layout SHOULD be used if the image requires _lossless_ encoding (with an +optional transparency channel) and does not require advanced features provided +by the extended format. + +Simple WebP (lossless) file format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | WebP file header (12 bytes) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | VP8L chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +VP8L chunk: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ChunkHeader('VP8L') | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | VP8L data | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +VP8L data: _Chunk Size_ bytes + +: VP8L bitstream data. + +The current specification of the VP8L bitstream can be found at +[WebP Lossless Bitstream Format][webpllspec]. Note that the VP8L header +contains the VP8L image width and height. That is assumed to be the width +and height of the canvas. + + +Extended File Format +-------------------- + +**Note:** Older readers may not support files using the extended format. + +An extended format file consists of: + + * A 'VP8X' chunk with information about features used in the file. + + * An optional 'ICCP' chunk with color profile. + + * An optional 'ANIM' chunk with animation control data. + + * Image data. + + * An optional 'EXIF' chunk with EXIF metadata. + + * An optional 'XMP ' chunk with XMP metadata. + + * An optional list of [unknown chunks](#unknown-chunks). + +For a _still image_, the _image data_ consists of a single frame, which is made +up of: + + * An optional [alpha subchunk](#alpha). + + * A [bitstream subchunk](#bitstream-vp8vp8l). + +For an _animated image_, the _image data_ consists of multiple frames. More +details about frames can be found in the [Animation](#animation) section. + +All chunks SHOULD be placed in the same order as listed above. If a chunk +appears in the wrong place, the file is invalid, but readers MAY parse the +file, ignoring the chunks that come too late. + +**Rationale:** Setting the order of chunks should allow quicker file +parsing. For example, if an 'ALPH' chunk does not appear in its required +position, a decoder can choose to stop searching for it. The rule of +ignoring late chunks should make programs that need to do a full search +give the same results as the ones stopping early. + +Extended WebP file header: +{:#extended_header} + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | WebP file header (12 bytes) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ChunkHeader('VP8X') | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Rsv|I|L|E|X|A|R| Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Canvas Width Minus One | ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ... Canvas Height Minus One | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Reserved (Rsv): 2 bits + +: SHOULD be `0`. + +ICC profile (I): 1 bit + +: Set if the file contains an ICC profile. + +Alpha (L): 1 bit + +: Set if any of the frames of the image contain transparency information + ("alpha"). + +EXIF metadata (E): 1 bit + +: Set if the file contains EXIF metadata. + +XMP metadata (X): 1 bit + +: Set if the file contains XMP metadata. + +Animation (A): 1 bit + +: Set if this is an animated image. Data in 'ANIM' and 'ANMF' chunks should be + used to control the animation. + +Reserved (R): 1 bit + +: SHOULD be `0`. + +Reserved: 24 bits + +: SHOULD be `0`. + +Canvas Width Minus One: 24 bits + +: _1-based_ width of the canvas in pixels. + The actual canvas width is '1 + Canvas Width Minus One' + +Canvas Height Minus One: 24 bits + +: _1-based_ height of the canvas in pixels. + The actual canvas height is '1 + Canvas Height Minus One' + +The product of _Canvas Width_ and _Canvas Height_ MUST be at most `2^32 - 1`. + +Future specifications MAY add more fields. + +### Chunks + +#### Animation + +An animation is controlled by ANIM and ANMF chunks. + +ANIM Chunk: +{:#anim_chunk} + +For an animated image, this chunk contains the _global parameters_ of the +animation. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ChunkHeader('ANIM') | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Background Color | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Loop Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Background Color: 32 bits (_uint32_) + +: The default background color of the canvas in \[Blue, Green, Red, Alpha\] + byte order. This color MAY be used to fill the unused space on the canvas + around the frames, as well as the transparent pixels of the first frame. + Background color is also used when disposal method is `1`. + +**Note**: + + * Background color MAY contain a transparency value (alpha), even if the + _Alpha_ flag in [VP8X chunk](#extended_header) is unset. + + * Viewer applications SHOULD treat the background color value as a hint, and + are not required to use it. + + * The canvas is cleared at the start of each loop. The background color MAY be + used to achieve this. + +Loop Count: 16 bits (_uint16_) + +: The number of times to loop the animation. `0` means infinitely. + +This chunk MUST appear if the _Animation_ flag in the VP8X chunk is set. +If the _Animation_ flag is not set and this chunk is present, it +SHOULD be ignored. + +ANMF chunk: + +For animated images, this chunk contains information about a _single_ frame. +If the _Animation flag_ is not set, then this chunk SHOULD NOT be present. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ChunkHeader('ANMF') | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Frame X | ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ... Frame Y | Frame Width Minus One ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ... | Frame Height Minus One | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Frame Duration | Reserved |B|D| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Frame Data | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Frame X: 24 bits (_uint24_) + +: The X coordinate of the upper left corner of the frame is `Frame X * 2` + +Frame Y: 24 bits (_uint24_) + +: The Y coordinate of the upper left corner of the frame is `Frame Y * 2` + +Frame Width Minus One: 24 bits (_uint24_) + +: The _1-based_ width of the frame. + The frame width is `1 + Frame Width Minus One` + +Frame Height Minus One: 24 bits (_uint24_) + +: The _1-based_ height of the frame. + The frame height is `1 + Frame Height Minus One` + +Frame Duration: 24 bits (_uint24_) + +: The time to wait before displaying the next frame, in 1 millisecond units. + Note the interpretation of frame duration of 0 (and often <= 10) is + implementation defined. Many tools and browsers assign a minimum duration + similar to GIF. + +Reserved: 6 bits + +: SHOULD be 0. + +Blending method (B): 1 bit + +: Indicates how transparent pixels of _the current frame_ are to be blended + with corresponding pixels of the previous canvas: + + * `0`: Use alpha blending. After disposing of the previous frame, render the + current frame on the canvas using [alpha-blending](#alpha-blending). If + the current frame does not have an alpha channel, assume alpha value of + 255, effectively replacing the rectangle. + + * `1`: Do not blend. After disposing of the previous frame, render the + current frame on the canvas by overwriting the rectangle covered by the + current frame. + +Disposal method (D): 1 bit + +: Indicates how _the current frame_ is to be treated after it has been + displayed (before rendering the next frame) on the canvas: + + * `0`: Do not dispose. Leave the canvas as is. + + * `1`: Dispose to background color. Fill the _rectangle_ on the canvas + covered by the _current frame_ with background color specified in the + [ANIM chunk](#anim_chunk). + +**Notes**: + + * The frame disposal only applies to the _frame rectangle_, that is, the + rectangle defined by _Frame X_, _Frame Y_, _frame width_ and _frame height_. + It may or may not cover the whole canvas. + +{:#alpha-blending} + * **Alpha-blending**: + + Given that each of the R, G, B and A channels is 8-bit, and the RGB + channels are _not premultiplied_ by alpha, the formula for blending + 'dst' onto 'src' is: + +~~~~~ + blend.A = src.A + dst.A * (1 - src.A / 255) + if blend.A = 0 then + blend.RGB = 0 + else + blend.RGB = (src.RGB * src.A + + dst.RGB * dst.A * (1 - src.A / 255)) / blend.A +~~~~~ + + * Alpha-blending SHOULD be done in linear color space, by taking into account + the [color profile](#color-profile) of the image. If the color profile is + not present, sRGB is to be assumed. (Note that sRGB also needs to be + linearized due to a gamma of ~2.2). + +Frame Data: _Chunk Size_ - `16` bytes + +: Consists of: + + * An optional [alpha subchunk](#alpha) for the frame. + + * A [bitstream subchunk](#bitstream-vp8vp8l) for the frame. + + * An optional list of [unknown chunks](#unknown-chunks). + +**Note**: The 'ANMF' payload, _Frame Data_ above, consists of individual +_padded_ chunks as described by the [RIFF file format](#riff-file-format). + +#### Alpha + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ChunkHeader('ALPH') | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Rsv| P | F | C | Alpha Bitstream... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Reserved (Rsv): 2 bits + +: SHOULD be `0`. + +Pre-processing (P): 2 bits + +: These INFORMATIVE bits are used to signal the pre-processing that has + been performed during compression. The decoder can use this information to + e.g. dither the values or smooth the gradients prior to display. + + * `0`: no pre-processing + * `1`: level reduction + +Filtering method (F): 2 bits + +: The filtering method used: + + * `0`: None. + * `1`: Horizontal filter. + * `2`: Vertical filter. + * `3`: Gradient filter. + +For each pixel, filtering is performed using the following calculations. +Assume the alpha values surrounding the current `X` position are labeled as: + + C | B | + ---+---+ + A | X | + +We seek to compute the alpha value at position `X`. First, a prediction is +made depending on the filtering method: + + * Method `0`: predictor = 0 + * Method `1`: predictor = A + * Method `2`: predictor = B + * Method `3`: predictor = clip(A + B - C) + +where `clip(v)` is equal to: + + * 0 if v < 0 + * 255 if v > 255 + * v otherwise + +The final value is derived by adding the decompressed value `X` to the +predictor and using modulo-256 arithmetic to wrap the \[256-511\] range +into the \[0-255\] one: + +`alpha = (predictor + X) % 256` + +There are special cases for left-most and top-most pixel positions: + + * Top-left value at location (0,0) uses 0 as predictor value. Otherwise, + * For horizontal or gradient filtering methods, the left-most pixels at + location (0, y) are predicted using the location (0, y-1) just above. + * For vertical or gradient filtering methods, the top-most pixels at + location (x, 0) are predicted using the location (x-1, 0) on the left. + + +Decoders are not required to use this information in any specified way. + +Compression method (C): 2 bits + +: The compression method used: + + * `0`: No compression. + * `1`: Compressed using the WebP lossless format. + +Alpha bitstream: _Chunk Size_ - `1` bytes + +: Encoded alpha bitstream. + +This optional chunk contains encoded alpha data for this frame. A frame +containing a 'VP8L' chunk SHOULD NOT contain this chunk. + +**Rationale**: The transparency information is already part of the 'VP8L' +chunk. + +The alpha channel data is stored as uncompressed raw data (when +compression method is '0') or compressed using the lossless format +(when the compression method is '1'). + + * Raw data: consists of a byte sequence of length width * height, + containing all the 8-bit transparency values in scan order. + + * Lossless format compression: the byte sequence is a compressed + image-stream (as described in the [WebP Lossless Bitstream Format] + [webpllspec]) of implicit dimension width x height. That is, this + image-stream does NOT contain any headers describing the image dimension. + + **Rationale**: the dimension is already known from other sources, + so storing it again would be redundant and error-prone. + + Once the image-stream is decoded into ARGB color values, following + the process described in the lossless format specification, the + transparency information must be extracted from the *green* channel + of the ARGB quadruplet. + + **Rationale**: the green channel is allowed extra transformation + steps in the specification -- unlike the other channels -- that can + improve compression. + +#### Bitstream (VP8/VP8L) + +This chunk contains compressed bitstream data for a single frame. + +A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the +significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using +"VP8L" as its tag. + +The formats of VP8 and VP8L chunks are as described in sections +[Simple File Format (Lossy)](#simple-file-format-lossy) +and [Simple File Format (Lossless)](#simple-file-format-lossless) respectively. + +#### Color profile + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ChunkHeader('ICCP') | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Color Profile | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Color Profile: _Chunk Size_ bytes + +: ICC profile. + +This chunk MUST appear before the image data. + +There SHOULD be at most one such chunk. If there are more such chunks, readers +MAY ignore all except the first one. +See the [ICC Specification][iccspec] for details. + +If this chunk is not present, sRGB SHOULD be assumed. + +#### Metadata + +Metadata can be stored in 'EXIF' or 'XMP ' chunks. + +There SHOULD be at most one chunk of each type ('EXIF' and 'XMP '). If there +are more such chunks, readers MAY ignore all except the first one. Also, a file +may possibly contain both 'EXIF' and 'XMP ' chunks. + +The chunks are defined as follows: + +EXIF chunk: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ChunkHeader('EXIF') | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | EXIF Metadata | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +EXIF Metadata: _Chunk Size_ bytes + +: image metadata in EXIF format. + +XMP chunk: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ChunkHeader('XMP ') | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | XMP Metadata | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +XMP Metadata: _Chunk Size_ bytes + +: image metadata in XMP format. + +Additional guidance about handling metadata can be found in the +Metadata Working Group's [Guidelines for Handling Metadata][metadata]. + +#### Unknown Chunks + +A RIFF chunk (described in [this](#terminology-amp-basics) section) whose _chunk +tag_ is different from any of the chunks described in this document, is +considered an _unknown chunk_. + +**Rationale**: Allowing unknown chunks gives a provision for future extension +of the format, and also allows storage of any application-specific data. + +A file MAY contain unknown chunks: + + * At the end of the file as described in [Extended WebP file + header](#extended_header) section. + * At the end of ANMF chunks as described in the + [Animation](#animation) section. + +Readers SHOULD ignore these chunks. Writers SHOULD preserve them in their +original order (unless they specifically intend to modify these chunks). + +### Assembling the Canvas from frames + +Here we provide an overview of how a reader should assemble a canvas in the +case of an animated image. The notation _VP8X.field_ means the field in the +'VP8X' chunk with the same description. + +Displaying an _animated image_ canvas MUST be equivalent to the following +pseudocode: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +assert VP8X.flags.hasAnimation +canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with + background color ANIM.background_color. +loop_count ← ANIM.loopCount +dispose_method ← ANIM.disposeMethod +if loop_count == 0: + loop_count = ∞ +frame_params ← nil +assert next chunk in image_data is ANMF +for loop = 0..loop_count - 1 + clear canvas to ANIM.background_color or application defined color + until eof or non-ANMF chunk + frame_params.frameX = Frame X + frame_params.frameY = Frame Y + frame_params.frameWidth = Frame Width Minus One + 1 + frame_params.frameHeight = Frame Height Minus One + 1 + frame_params.frameDuration = Frame Duration + frame_right = frame_params.frameX + frame_params.frameWidth + frame_bottom = frame_params.frameY + frame_params.frameHeight + assert VP8X.canvasWidth >= frame_right + assert VP8X.canvasHeight >= frame_bottom + for subchunk in 'Frame Data': + if subchunk.tag == "ALPH": + assert alpha subchunks not found in 'Frame Data' earlier + frame_params.alpha = alpha_data + else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L": + assert bitstream subchunks not found in 'Frame Data' earlier + frame_params.bitstream = bitstream_data + render frame with frame_params.alpha and frame_params.bitstream on + canvas with top-left corner at (frame_params.frameX, + frame_params.frameY), using dispose method dispose_method. + canvas contains the decoded image. + Show the contents of the canvas for frame_params.frameDuration * 1ms. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +Example File Layouts +-------------------- + +A lossy encoded image with alpha may look as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +RIFF/WEBP ++- VP8X (descriptions of features used) ++- ALPH (alpha bitstream) ++- VP8 (bitstream) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A losslessly encoded image may look as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +RIFF/WEBP ++- VP8X (descriptions of features used) ++- XYZW (unknown chunk) ++- VP8L (lossless bitstream) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A lossless image with ICC profile and XMP metadata may +look as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +RIFF/WEBP ++- VP8X (descriptions of features used) ++- ICCP (color profile) ++- VP8L (lossless bitstream) ++- XMP (metadata) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An animated image with EXIF metadata may look as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +RIFF/WEBP ++- VP8X (descriptions of features used) ++- ANIM (global animation parameters) ++- ANMF (frame1 parameters + data) ++- ANMF (frame2 parameters + data) ++- ANMF (frame3 parameters + data) ++- ANMF (frame4 parameters + data) ++- EXIF (metadata) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[vp8spec]: http://tools.ietf.org/html/rfc6386 +[webpllspec]: https://chromium.googlesource.com/webm/libwebp/+/HEAD/doc/webp-lossless-bitstream-spec.txt +[iccspec]: http://www.color.org/icc_specs2.xalter +[metadata]: http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf +[rfc 1166]: http://tools.ietf.org/html/rfc1166 +[rfc 2119]: http://tools.ietf.org/html/rfc2119 diff --git a/internal/libwebp-1.2.1/doc/webp-lossless-bitstream-spec.txt b/internal/libwebp-1.2.1/doc/webp-lossless-bitstream-spec.txt new file mode 100644 index 0000000..2d2dde1 --- /dev/null +++ b/internal/libwebp-1.2.1/doc/webp-lossless-bitstream-spec.txt @@ -0,0 +1,1090 @@ + + +Specification for WebP Lossless Bitstream +========================================= + +_Jyrki Alakuijala, Ph.D., Google, Inc., 2012-06-19_ + +Paragraphs marked as \[AMENDED\] were amended on 2014-09-16. + +Abstract +-------- + +WebP lossless is an image format for lossless compression of ARGB +images. The lossless format stores and restores the pixel values +exactly, including the color values for zero alpha pixels. The +format uses subresolution images, recursively embedded into the format +itself, for storing statistical data about the images, such as the used +entropy codes, spatial predictors, color space conversion, and color +table. LZ77, Huffman coding, and a color cache are used for compression +of the bulk data. Decoding speeds faster than PNG have been +demonstrated, as well as 25% denser compression than can be achieved +using today's PNG format. + + +* TOC placeholder +{:toc} + + +Nomenclature +------------ + +ARGB +: A pixel value consisting of alpha, red, green, and blue values. + +ARGB image +: A two-dimensional array containing ARGB pixels. + +color cache +: A small hash-addressed array to store recently used colors, to be able + to recall them with shorter codes. + +color indexing image +: A one-dimensional image of colors that can be indexed using a small + integer (up to 256 within WebP lossless). + +color transform image +: A two-dimensional subresolution image containing data about + correlations of color components. + +distance mapping +: Changes LZ77 distances to have the smallest values for pixels in 2D + proximity. + +entropy image +: A two-dimensional subresolution image indicating which entropy coding + should be used in a respective square in the image, i.e., each pixel + is a meta Huffman code. + +Huffman code +: A classic way to do entropy coding where a smaller number of bits are + used for more frequent codes. + +LZ77 +: Dictionary-based sliding window compression algorithm that either + emits symbols or describes them as sequences of past symbols. + +meta Huffman code +: A small integer (up to 16 bits) that indexes an element in the meta + Huffman table. + +predictor image +: A two-dimensional subresolution image indicating which spatial + predictor is used for a particular square in the image. + +prefix coding +: A way to entropy code larger integers that codes a few bits of the + integer using an entropy code and codifies the remaining bits raw. + This allows for the descriptions of the entropy codes to remain + relatively small even when the range of symbols is large. + +scan-line order +: A processing order of pixels, left-to-right, top-to-bottom, starting + from the left-hand-top pixel, proceeding to the right. Once a row is + completed, continue from the left-hand column of the next row. + + +1 Introduction +-------------- + +This document describes the compressed data representation of a WebP +lossless image. It is intended as a detailed reference for WebP lossless +encoder and decoder implementation. + +In this document, we extensively use C programming language syntax to +describe the bitstream, and assume the existence of a function for +reading bits, `ReadBits(n)`. The bytes are read in the natural order of +the stream containing them, and bits of each byte are read in +least-significant-bit-first order. When multiple bits are read at the +same time, the integer is constructed from the original data in the +original order. The most significant bits of the returned integer are +also the most significant bits of the original data. Thus the statement + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +b = ReadBits(2); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +is equivalent with the two statements below: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +b = ReadBits(1); +b |= ReadBits(1) << 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We assume that each color component (e.g. alpha, red, blue and green) is +represented using an 8-bit byte. We define the corresponding type as +uint8. A whole ARGB pixel is represented by a type called uint32, an +unsigned integer consisting of 32 bits. In the code showing the behavior +of the transformations, alpha value is codified in bits 31..24, red in +bits 23..16, green in bits 15..8 and blue in bits 7..0, but +implementations of the format are free to use another representation +internally. + +Broadly, a WebP lossless image contains header data, transform +information and actual image data. Headers contain width and height of +the image. A WebP lossless image can go through four different types of +transformation before being entropy encoded. The transform information +in the bitstream contains the data required to apply the respective +inverse transforms. + + +2 RIFF Header +------------- + +The beginning of the header has the RIFF container. This consists of the +following 21 bytes: + + 1. String "RIFF" + 2. A little-endian 32 bit value of the block length, the whole size + of the block controlled by the RIFF header. Normally this equals + the payload size (file size minus 8 bytes: 4 bytes for the 'RIFF' + identifier and 4 bytes for storing the value itself). + 3. String "WEBP" (RIFF container name). + 4. String "VP8L" (chunk tag for lossless encoded image data). + 5. A little-endian 32-bit value of the number of bytes in the + lossless stream. + 6. One byte signature 0x2f. + +The first 28 bits of the bitstream specify the width and height of the +image. Width and height are decoded as 14-bit integers as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int image_width = ReadBits(14) + 1; +int image_height = ReadBits(14) + 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The 14-bit dynamics for image size limit the maximum size of a WebP +lossless image to 16384✕16384 pixels. + +The alpha_is_used bit is a hint only, and should not impact decoding. +It should be set to 0 when all alpha values are 255 in the picture, and +1 otherwise. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int alpha_is_used = ReadBits(1); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The version_number is a 3 bit code that must be set to 0. Any other value +should be treated as an error. \[AMENDED\] + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int version_number = ReadBits(3); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +3 Transformations +----------------- + +Transformations are reversible manipulations of the image data that can +reduce the remaining symbolic entropy by modeling spatial and color +correlations. Transformations can make the final compression more dense. + +An image can go through four types of transformation. A 1 bit indicates +the presence of a transform. Each transform is allowed to be used only +once. The transformations are used only for the main level ARGB image: +the subresolution images have no transforms, not even the 0 bit +indicating the end-of-transforms. + +Typically an encoder would use these transforms to reduce the Shannon +entropy in the residual image. Also, the transform data can be decided +based on entropy minimization. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +while (ReadBits(1)) { // Transform present. + // Decode transform type. + enum TransformType transform_type = ReadBits(2); + // Decode transform data. + ... +} + +// Decode actual image data (Section 4). +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If a transform is present then the next two bits specify the transform +type. There are four types of transforms. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +enum TransformType { + PREDICTOR_TRANSFORM = 0, + COLOR_TRANSFORM = 1, + SUBTRACT_GREEN = 2, + COLOR_INDEXING_TRANSFORM = 3, +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The transform type is followed by the transform data. Transform data +contains the information required to apply the inverse transform and +depends on the transform type. Next we describe the transform data for +different types. + + +### Predictor Transform + +The predictor transform can be used to reduce entropy by exploiting the +fact that neighboring pixels are often correlated. In the predictor +transform, the current pixel value is predicted from the pixels already +decoded (in scan-line order) and only the residual value (actual - +predicted) is encoded. The _prediction mode_ determines the type of +prediction to use. We divide the image into squares and all the pixels +in a square use same prediction mode. + +The first 3 bits of prediction data define the block width and height in +number of bits. The number of block columns, `block_xsize`, is used in +indexing two-dimensionally. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int size_bits = ReadBits(3) + 2; +int block_width = (1 << size_bits); +int block_height = (1 << size_bits); +#define DIV_ROUND_UP(num, den) ((num) + (den) - 1) / (den)) +int block_xsize = DIV_ROUND_UP(image_width, 1 << size_bits); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The transform data contains the prediction mode for each block of the +image. All the `block_width * block_height` pixels of a block use same +prediction mode. The prediction modes are treated as pixels of an image +and encoded using the same techniques described in +[Chapter 4](#image-data). + +For a pixel _x, y_, one can compute the respective filter block address +by: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int block_index = (y >> size_bits) * block_xsize + + (x >> size_bits); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are 14 different prediction modes. In each prediction mode, the +current pixel value is predicted from one or more neighboring pixels +whose values are already known. + +We choose the neighboring pixels (TL, T, TR, and L) of the current pixel +(P) as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +O O O O O O O O O O O +O O O O O O O O O O O +O O O O TL T TR O O O O +O O O O L P X X X X X +X X X X X X X X X X X +X X X X X X X X X X X +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +where TL means top-left, T top, TR top-right, L left pixel. +At the time of predicting a value for P, all pixels O, TL, T, TR and L +have been already processed, and pixel P and all pixels X are unknown. + +Given the above neighboring pixels, the different prediction modes are +defined as follows. + +| Mode | Predicted value of each channel of the current pixel | +| ------ | ------------------------------------------------------- | +| 0 | 0xff000000 (represents solid black color in ARGB) | +| 1 | L | +| 2 | T | +| 3 | TR | +| 4 | TL | +| 5 | Average2(Average2(L, TR), T) | +| 6 | Average2(L, TL) | +| 7 | Average2(L, T) | +| 8 | Average2(TL, T) | +| 9 | Average2(T, TR) | +| 10 | Average2(Average2(L, TL), Average2(T, TR)) | +| 11 | Select(L, T, TL) | +| 12 | ClampAddSubtractFull(L, T, TL) | +| 13 | ClampAddSubtractHalf(Average2(L, T), TL) | + + +`Average2` is defined as follows for each ARGB component: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +uint8 Average2(uint8 a, uint8 b) { + return (a + b) / 2; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Select predictor is defined as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +uint32 Select(uint32 L, uint32 T, uint32 TL) { + // L = left pixel, T = top pixel, TL = top left pixel. + + // ARGB component estimates for prediction. + int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL); + int pRed = RED(L) + RED(T) - RED(TL); + int pGreen = GREEN(L) + GREEN(T) - GREEN(TL); + int pBlue = BLUE(L) + BLUE(T) - BLUE(TL); + + // Manhattan distances to estimates for left and top pixels. + int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) + + abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L)); + int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) + + abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T)); + + // Return either left or top, the one closer to the prediction. + if (pL < pT) { // \[AMENDED\] + return L; + } else { + return T; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The functions `ClampAddSubtractFull` and `ClampAddSubtractHalf` are +performed for each ARGB component as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Clamp the input value between 0 and 255. +int Clamp(int a) { + return (a < 0) ? 0 : (a > 255) ? 255 : a; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int ClampAddSubtractFull(int a, int b, int c) { + return Clamp(a + b - c); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int ClampAddSubtractHalf(int a, int b) { + return Clamp(a + (a - b) / 2); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are special handling rules for some border pixels. If there is a +prediction transform, regardless of the mode \[0..13\] for these pixels, +the predicted value for the left-topmost pixel of the image is +0xff000000, L-pixel for all pixels on the top row, and T-pixel for all +pixels on the leftmost column. + +Addressing the TR-pixel for pixels on the rightmost column is +exceptional. The pixels on the rightmost column are predicted by using +the modes \[0..13\] just like pixels not on border, but by using the +leftmost pixel on the same row as the current TR-pixel. The TR-pixel +offset in memory is the same for border and non-border pixels. + + +### Color Transform + +The goal of the color transform is to decorrelate the R, G and B values +of each pixel. Color transform keeps the green (G) value as it is, +transforms red (R) based on green and transforms blue (B) based on green +and then based on red. + +As is the case for the predictor transform, first the image is divided +into blocks and the same transform mode is used for all the pixels in a +block. For each block there are three types of color transform elements. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +typedef struct { + uint8 green_to_red; + uint8 green_to_blue; + uint8 red_to_blue; +} ColorTransformElement; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The actual color transformation is done by defining a color transform +delta. The color transform delta depends on the `ColorTransformElement`, +which is the same for all the pixels in a particular block. The delta is +added during color transform. The inverse color transform then is just +subtracting those deltas. + +The color transform function is defined as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +void ColorTransform(uint8 red, uint8 blue, uint8 green, + ColorTransformElement *trans, + uint8 *new_red, uint8 *new_blue) { + // Transformed values of red and blue components + uint32 tmp_red = red; + uint32 tmp_blue = blue; + + // Applying transform is just adding the transform deltas + tmp_red += ColorTransformDelta(trans->green_to_red, green); + tmp_blue += ColorTransformDelta(trans->green_to_blue, green); + tmp_blue += ColorTransformDelta(trans->red_to_blue, red); + + *new_red = tmp_red & 0xff; + *new_blue = tmp_blue & 0xff; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`ColorTransformDelta` is computed using a signed 8-bit integer +representing a 3.5-fixed-point number, and a signed 8-bit RGB color +channel (c) \[-128..127\] and is defined as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int8 ColorTransformDelta(int8 t, int8 c) { + return (t * c) >> 5; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A conversion from the 8-bit unsigned representation (uint8) to the 8-bit +signed one (int8) is required before calling ColorTransformDelta(). +It should be performed using 8-bit two's complement (that is: uint8 range +\[128-255\] is mapped to the \[-128, -1\] range of its converted int8 value). + +The multiplication is to be done using more precision (with at least +16-bit dynamics). The sign extension property of the shift operation +does not matter here: only the lowest 8 bits are used from the result, +and there the sign extension shifting and unsigned shifting are +consistent with each other. + +Now we describe the contents of color transform data so that decoding +can apply the inverse color transform and recover the original red and +blue values. The first 3 bits of the color transform data contain the +width and height of the image block in number of bits, just like the +predictor transform: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int size_bits = ReadBits(3) + 2; +int block_width = 1 << size_bits; +int block_height = 1 << size_bits; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The remaining part of the color transform data contains +`ColorTransformElement` instances corresponding to each block of the +image. `ColorTransformElement` instances are treated as pixels of an +image and encoded using the methods described in +[Chapter 4](#image-data). + +During decoding, `ColorTransformElement` instances of the blocks are +decoded and the inverse color transform is applied on the ARGB values of +the pixels. As mentioned earlier, that inverse color transform is just +subtracting `ColorTransformElement` values from the red and blue +channels. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +void InverseTransform(uint8 red, uint8 green, uint8 blue, + ColorTransformElement *p, + uint8 *new_red, uint8 *new_blue) { + // Applying inverse transform is just subtracting the + // color transform deltas + red -= ColorTransformDelta(p->green_to_red_, green); + blue -= ColorTransformDelta(p->green_to_blue_, green); + blue -= ColorTransformDelta(p->red_to_blue_, red & 0xff); + + *new_red = red & 0xff; + *new_blue = blue & 0xff; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +### Subtract Green Transform + +The subtract green transform subtracts green values from red and blue +values of each pixel. When this transform is present, the decoder needs +to add the green value to both red and blue. There is no data associated +with this transform. The decoder applies the inverse transform as +follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +void AddGreenToBlueAndRed(uint8 green, uint8 *red, uint8 *blue) { + *red = (*red + green) & 0xff; + *blue = (*blue + green) & 0xff; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This transform is redundant as it can be modeled using the color +transform, but it is still often useful. Since it can extend the +dynamics of the color transform and there is no additional data here, +the subtract green transform can be coded using fewer bits than a +full-blown color transform. + + +### Color Indexing Transform + +If there are not many unique pixel values, it may be more efficient to +create a color index array and replace the pixel values by the array's +indices. The color indexing transform achieves this. (In the context of +WebP lossless, we specifically do not call this a palette transform +because a similar but more dynamic concept exists in WebP lossless +encoding: color cache.) + +The color indexing transform checks for the number of unique ARGB values +in the image. If that number is below a threshold (256), it creates an +array of those ARGB values, which is then used to replace the pixel +values with the corresponding index: the green channel of the pixels are +replaced with the index; all alpha values are set to 255; all red and +blue values to 0. + +The transform data contains color table size and the entries in the +color table. The decoder reads the color indexing transform data as +follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// 8 bit value for color table size +int color_table_size = ReadBits(8) + 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The color table is stored using the image storage format itself. The +color table can be obtained by reading an image, without the RIFF +header, image size, and transforms, assuming a height of one pixel and +a width of `color_table_size`. The color table is always +subtraction-coded to reduce image entropy. The deltas of palette colors +contain typically much less entropy than the colors themselves, leading +to significant savings for smaller images. In decoding, every final +color in the color table can be obtained by adding the previous color +component values by each ARGB component separately, and storing the +least significant 8 bits of the result. + +The inverse transform for the image is simply replacing the pixel values +(which are indices to the color table) with the actual color table +values. The indexing is done based on the green component of the ARGB +color. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Inverse transform +argb = color_table[GREEN(argb)]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the index is equal or larger than color_table_size, the argb color value +should be set to 0x00000000 (transparent black). \[AMENDED\] + +When the color table is small (equal to or less than 16 colors), several +pixels are bundled into a single pixel. The pixel bundling packs several +(2, 4, or 8) pixels into a single pixel, reducing the image width +respectively. Pixel bundling allows for a more efficient joint +distribution entropy coding of neighboring pixels, and gives some +arithmetic coding-like benefits to the entropy code, but it can only be +used when there are a small number of unique values. + +`color_table_size` specifies how many pixels are combined together: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int width_bits; +if (color_table_size <= 2) { + width_bits = 3; +} else if (color_table_size <= 4) { + width_bits = 2; +} else if (color_table_size <= 16) { + width_bits = 1; +} else { + width_bits = 0; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`width_bits` has a value of 0, 1, 2 or 3. A value of 0 indicates no +pixel bundling to be done for the image. A value of 1 indicates that two +pixels are combined together, and each pixel has a range of \[0..15\]. A +value of 2 indicates that four pixels are combined together, and each +pixel has a range of \[0..3\]. A value of 3 indicates that eight pixels +are combined together and each pixel has a range of \[0..1\], i.e., a +binary value. + +The values are packed into the green component as follows: + + * `width_bits` = 1: for every x value where x â‰Ħ 0 (mod 2), a green + value at x is positioned into the 4 least-significant bits of the + green value at x / 2, a green value at x + 1 is positioned into the + 4 most-significant bits of the green value at x / 2. + * `width_bits` = 2: for every x value where x â‰Ħ 0 (mod 4), a green + value at x is positioned into the 2 least-significant bits of the + green value at x / 4, green values at x + 1 to x + 3 in order to the + more significant bits of the green value at x / 4. + * `width_bits` = 3: for every x value where x â‰Ħ 0 (mod 8), a green + value at x is positioned into the least-significant bit of the green + value at x / 8, green values at x + 1 to x + 7 in order to the more + significant bits of the green value at x / 8. + + +4 Image Data +------------ + +Image data is an array of pixel values in scan-line order. + +### 4.1 Roles of Image Data + +We use image data in five different roles: + + 1. ARGB image: Stores the actual pixels of the image. + 1. Entropy image: Stores the + [meta Huffman codes](#decoding-of-meta-huffman-codes). The red and green + components of a pixel define the meta Huffman code used in a particular + block of the ARGB image. + 1. Predictor image: Stores the metadata for [Predictor + Transform](#predictor-transform). The green component of a pixel defines + which of the 14 predictors is used within a particular block of the + ARGB image. + 1. Color transform image. It is created by `ColorTransformElement` values + (defined in [Color Transform](#color-transform)) for different blocks of + the image. Each `ColorTransformElement` `'cte'` is treated as a pixel whose + alpha component is `255`, red component is `cte.red_to_blue`, green + component is `cte.green_to_blue` and blue component is `cte.green_to_red`. + 1. Color indexing image: An array of of size `color_table_size` (up to 256 + ARGB values) storing the metadata for the + [Color Indexing Transform](#color-indexing-transform). This is stored as an + image of width `color_table_size` and height `1`. + +### 4.2 Encoding of Image data + +The encoding of image data is independent of its role. + +The image is first divided into a set of fixed-size blocks (typically 16x16 +blocks). Each of these blocks are modeled using their own entropy codes. Also, +several blocks may share the same entropy codes. + +**Rationale:** Storing an entropy code incurs a cost. This cost can be minimized +if statistically similar blocks share an entropy code, thereby storing that code +only once. For example, an encoder can find similar blocks by clustering them +using their statistical properties, or by repeatedly joining a pair of randomly +selected clusters when it reduces the overall amount of bits needed to encode +the image. + +Each pixel is encoded using one of the three possible methods: + + 1. Huffman coded literal: each channel (green, red, blue and alpha) is + entropy-coded independently; + 2. LZ77 backward reference: a sequence of pixels are copied from elsewhere + in the image; or + 3. Color cache code: using a short multiplicative hash code (color cache + index) of a recently seen color. + +The following sub-sections describe each of these in detail. + +#### 4.2.1 Huffman Coded Literals + +The pixel is stored as Huffman coded values of green, red, blue and alpha (in +that order). See [this section](#decoding-entropy-coded-image-data) for details. + +#### 4.2.2 LZ77 Backward Reference + +Backward references are tuples of _length_ and _distance code_: + + * Length indicates how many pixels in scan-line order are to be copied. + * Distance code is a number indicating the position of a previously seen + pixel, from which the pixels are to be copied. The exact mapping is + described [below](#distance-mapping). + +The length and distance values are stored using **LZ77 prefix coding**. + +LZ77 prefix coding divides large integer values into two parts: the _prefix +code_ and the _extra bits_: the prefix code is stored using an entropy code, +while the extra bits are stored as they are (without an entropy code). + +**Rationale**: This approach reduces the storage requirement for the entropy +code. Also, large values are usually rare, and so extra bits would be used for +very few values in the image. Thus, this approach results in a better +compression overall. + +The following table denotes the prefix codes and extra bits used for storing +different range of values. + +Note: The maximum backward reference length is limited to 4096. Hence, only the +first 24 prefix codes (with the respective extra bits) are meaningful for length +values. For distance values, however, all the 40 prefix codes are valid. + +| Value range | Prefix code | Extra bits | +| --------------- | ----------- | ---------- | +| 1 | 0 | 0 | +| 2 | 1 | 0 | +| 3 | 2 | 0 | +| 4 | 3 | 0 | +| 5..6 | 4 | 1 | +| 7..8 | 5 | 1 | +| 9..12 | 6 | 2 | +| 13..16 | 7 | 2 | +| ... | ... | ... | +| 3072..4096 | 23 | 10 | +| ... | ... | ... | +| 524289..786432 | 38 | 18 | +| 786433..1048576 | 39 | 18 | + +The pseudocode to obtain a (length or distance) value from the prefix code is +as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +if (prefix_code < 4) { + return prefix_code + 1; +} +int extra_bits = (prefix_code - 2) >> 1; +int offset = (2 + (prefix_code & 1)) << extra_bits; +return offset + ReadBits(extra_bits) + 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Distance Mapping:** +{:#distance-mapping} + +As noted previously, distance code is a number indicating the position of a +previously seen pixel, from which the pixels are to be copied. This sub-section +defines the mapping between a distance code and the position of a previous +pixel. + +The distance codes larger than 120 denote the pixel-distance in scan-line +order, offset by 120. + +The smallest distance codes \[1..120\] are special, and are reserved for a close +neighborhood of the current pixel. This neighborhood consists of 120 pixels: + + * Pixels that are 1 to 7 rows above the current pixel, and are up to 8 columns + to the left or up to 7 columns to the right of the current pixel. \[Total + such pixels = `7 * (8 + 1 + 7) = 112`\]. + * Pixels that are in same row as the current pixel, and are up to 8 columns to + the left of the current pixel. \[`8` such pixels\]. + +The mapping between distance code `i` and the neighboring pixel offset +`(xi, yi)` is as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(0, 1), (1, 0), (1, 1), (-1, 1), (0, 2), (2, 0), (1, 2), (-1, 2), +(2, 1), (-2, 1), (2, 2), (-2, 2), (0, 3), (3, 0), (1, 3), (-1, 3), +(3, 1), (-3, 1), (2, 3), (-2, 3), (3, 2), (-3, 2), (0, 4), (4, 0), +(1, 4), (-1, 4), (4, 1), (-4, 1), (3, 3), (-3, 3), (2, 4), (-2, 4), +(4, 2), (-4, 2), (0, 5), (3, 4), (-3, 4), (4, 3), (-4, 3), (5, 0), +(1, 5), (-1, 5), (5, 1), (-5, 1), (2, 5), (-2, 5), (5, 2), (-5, 2), +(4, 4), (-4, 4), (3, 5), (-3, 5), (5, 3), (-5, 3), (0, 6), (6, 0), +(1, 6), (-1, 6), (6, 1), (-6, 1), (2, 6), (-2, 6), (6, 2), (-6, 2), +(4, 5), (-4, 5), (5, 4), (-5, 4), (3, 6), (-3, 6), (6, 3), (-6, 3), +(0, 7), (7, 0), (1, 7), (-1, 7), (5, 5), (-5, 5), (7, 1), (-7, 1), +(4, 6), (-4, 6), (6, 4), (-6, 4), (2, 7), (-2, 7), (7, 2), (-7, 2), +(3, 7), (-3, 7), (7, 3), (-7, 3), (5, 6), (-5, 6), (6, 5), (-6, 5), +(8, 0), (4, 7), (-4, 7), (7, 4), (-7, 4), (8, 1), (8, 2), (6, 6), +(-6, 6), (8, 3), (5, 7), (-5, 7), (7, 5), (-7, 5), (8, 4), (6, 7), +(-6, 7), (7, 6), (-7, 6), (8, 5), (7, 7), (-7, 7), (8, 6), (8, 7) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example, distance code `1` indicates offset of `(0, 1)` for the neighboring +pixel, that is, the pixel above the current pixel (0-pixel difference in +X-direction and 1 pixel difference in Y-direction). Similarly, distance code +`3` indicates left-top pixel. + +The decoder can convert a distances code 'i' to a scan-line order distance +'dist' as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(xi, yi) = distance_map[i] +dist = x + y * xsize +if (dist < 1) { + dist = 1 +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +where 'distance_map' is the mapping noted above and `xsize` is the width of the +image in pixels. + + +#### 4.2.3 Color Cache Coding + +Color cache stores a set of colors that have been recently used in the image. + +**Rationale:** This way, the recently used colors can sometimes be referred to +more efficiently than emitting them using other two methods (described in +[4.2.1](#huffman-coded-literals) and [4.2.2](#lz77-backward-reference)). + +Color cache codes are stored as follows. First, there is a 1-bit value that +indicates if the color cache is used. If this bit is 0, no color cache codes +exist, and they are not transmitted in the Huffman code that decodes the green +symbols and the length prefix codes. However, if this bit is 1, the color cache +size is read next: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int color_cache_code_bits = ReadBits(4); +int color_cache_size = 1 << color_cache_code_bits; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`color_cache_code_bits` defines the size of the color_cache by (1 << +`color_cache_code_bits`). The range of allowed values for +`color_cache_code_bits` is \[1..11\]. Compliant decoders must indicate a +corrupted bitstream for other values. + +A color cache is an array of size `color_cache_size`. Each entry +stores one ARGB color. Colors are looked up by indexing them by +(0x1e35a7bd * `color`) >> (32 - `color_cache_code_bits`). Only one +lookup is done in a color cache; there is no conflict resolution. + +In the beginning of decoding or encoding of an image, all entries in all +color cache values are set to zero. The color cache code is converted to +this color at decoding time. The state of the color cache is maintained +by inserting every pixel, be it produced by backward referencing or as +literals, into the cache in the order they appear in the stream. + + +5 Entropy Code +-------------- + +### 5.1 Overview + +Most of the data is coded using [canonical Huffman code][canonical_huff]. Hence, +the codes are transmitted by sending the _Huffman code lengths_, as opposed to +the actual _Huffman codes_. + +In particular, the format uses **spatially-variant Huffman coding**. In other +words, different blocks of the image can potentially use different entropy +codes. + +**Rationale**: Different areas of the image may have different characteristics. So, allowing them to use different entropy codes provides more flexibility and +potentially a better compression. + +### 5.2 Details + +The encoded image data consists of two parts: + + 1. Meta Huffman codes + 1. Entropy-coded image data + +#### 5.2.1 Decoding of Meta Huffman Codes + +As noted earlier, the format allows the use of different Huffman codes for +different blocks of the image. _Meta Huffman codes_ are indexes identifying +which Huffman codes to use in different parts of the image. + +Meta Huffman codes may be used _only_ when the image is being used in the +[role](#roles-of-image-data) of an _ARGB image_. + +There are two possibilities for the meta Huffman codes, indicated by a 1-bit +value: + + * If this bit is zero, there is only one meta Huffman code used everywhere in + the image. No more data is stored. + * If this bit is one, the image uses multiple meta Huffman codes. These meta + Huffman codes are stored as an _entropy image_ (described below). + +**Entropy image:** + +The entropy image defines which Huffman codes are used in different parts of the +image, as described below. + +The first 3-bits contain the `huffman_bits` value. The dimensions of the entropy +image are derived from 'huffman_bits'. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int huffman_bits = ReadBits(3) + 2; +int huffman_xsize = DIV_ROUND_UP(xsize, 1 << huffman_bits); +int huffman_ysize = DIV_ROUND_UP(ysize, 1 << huffman_bits); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +where `DIV_ROUND_UP` is as defined [earlier](#predictor-transform). + +Next bits contain an entropy image of width `huffman_xsize` and height +`huffman_ysize`. + +**Interpretation of Meta Huffman Codes:** + +For any given pixel (x, y), there is a set of five Huffman codes associated with +it. These codes are (in bitstream order): + + * **Huffman code #1**: used for green channel, backward-reference length and + color cache + * **Huffman code #2, #3 and #4**: used for red, blue and alpha channels + respectively. + * **Huffman code #5**: used for backward-reference distance. + +From here on, we refer to this set as a **Huffman code group**. + +The number of Huffman code groups in the ARGB image can be obtained by finding +the _largest meta Huffman code_ from the entropy image: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int num_huff_groups = max(entropy image) + 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +where `max(entropy image)` indicates the largest Huffman code stored in the +entropy image. + +As each Huffman code groups contains five Huffman codes, the total number of +Huffman codes is: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int num_huff_codes = 5 * num_huff_groups; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Given a pixel (x, y) in the ARGB image, we can obtain the corresponding Huffman +codes to be used as follows: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int position = (y >> huffman_bits) * huffman_xsize + (x >> huffman_bits); +int meta_huff_code = (entropy_image[pos] >> 8) & 0xffff; +HuffmanCodeGroup huff_group = huffman_code_groups[meta_huff_code]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +where, we have assumed the existence of `HuffmanCodeGroup` structure, which +represents a set of five Huffman codes. Also, `huffman_code_groups` is an array +of `HuffmanCodeGroup` (of size `num_huff_groups`). + +The decoder then uses Huffman code group `huff_group` to decode the pixel +(x, y) as explained in the [next section](#decoding-entropy-coded-image-data). + +#### 5.2.2 Decoding Entropy-coded Image Data + +For the current position (x, y) in the image, the decoder first identifies the +corresponding Huffman code group (as explained in the last section). Given the +Huffman code group, the pixel is read and decoded as follows: + +Read next symbol S from the bitstream using Huffman code #1. \[See +[next section](#decoding-the-code-lengths) for details on decoding the Huffman +code lengths\]. Note that S is any integer in the range `0` to +`(256 + 24 + ` [`color_cache_size`](#color-cache-code)`- 1)`. + +The interpretation of S depends on its value: + + 1. if S < 256 + 1. Use S as the green component + 1. Read red from the bitstream using Huffman code #2 + 1. Read blue from the bitstream using Huffman code #3 + 1. Read alpha from the bitstream using Huffman code #4 + 1. if S < 256 + 24 + 1. Use S - 256 as a length prefix code + 1. Read extra bits for length from the bitstream + 1. Determine backward-reference length L from length prefix code and the + extra bits read. + 1. Read distance prefix code from the bitstream using Huffman code #5 + 1. Read extra bits for distance from the bitstream + 1. Determine backward-reference distance D from distance prefix code and + the extra bits read. + 1. Copy the L pixels (in scan-line order) from the sequence of pixels + prior to them by D pixels. + 1. if S >= 256 + 24 + 1. Use S - (256 + 24) as the index into the color cache. + 1. Get ARGB color from the color cache at that index. + + +**Decoding the Code Lengths:** +{:#decoding-the-code-lengths} + +This section describes the details about reading a symbol from the bitstream by +decoding the Huffman code length. + +The Huffman code lengths can be coded in two ways. The method used is specified +by a 1-bit value. + + * If this bit is 1, it is a _simple code length code_, and + * If this bit is 0, it is a _normal code length code_. + +**(i) Simple Code Length Code:** + +This variant is used in the special case when only 1 or 2 Huffman code lengths +are non-zero, and are in the range of \[0, 255\]. All other Huffman code lengths +are implicitly zeros. + +The first bit indicates the number of non-zero code lengths: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int num_code_lengths = ReadBits(1) + 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The first code length is stored either using a 1-bit code for values of 0 and 1, +or using an 8-bit code for values in range \[0, 255\]. The second code length, +when present, is coded as an 8-bit code. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int is_first_8bits = ReadBits(1); +code_lengths[0] = ReadBits(1 + 7 * is_first_8bits); +if (num_code_lengths == 2) { + code_lengths[1] = ReadBits(8); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Note:** Another special case is when _all_ Huffman code lengths are _zeros_ +(an empty Huffman code). For example, a Huffman code for distance can be empty +if there are no backward references. Similarly, Huffman codes for alpha, red, +and blue can be empty if all pixels within the same meta Huffman code are +produced using the color cache. However, this case doesn't need a special +handling, as empty Huffman codes can be coded as those containing a single +symbol `0`. + +**(ii) Normal Code Length Code:** + +The code lengths of a Huffman code are read as follows: `num_code_lengths` +specifies the number of code lengths; the rest of the code lengths +(according to the order in `kCodeLengthCodeOrder`) are zeros. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +int kCodeLengthCodes = 19; +int kCodeLengthCodeOrder[kCodeLengthCodes] = { + 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; +int code_lengths[kCodeLengthCodes] = { 0 }; // All zeros. +int num_code_lengths = 4 + ReadBits(4); +for (i = 0; i < num_code_lengths; ++i) { + code_lengths[kCodeLengthCodeOrder[i]] = ReadBits(3); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * Code length code \[0..15\] indicates literal code lengths. + * Value 0 means no symbols have been coded. + * Values \[1..15\] indicate the bit length of the respective code. + * Code 16 repeats the previous non-zero value \[3..6\] times, i.e., + 3 + `ReadBits(2)` times. If code 16 is used before a non-zero + value has been emitted, a value of 8 is repeated. + * Code 17 emits a streak of zeros \[3..10\], i.e., 3 + `ReadBits(3)` + times. + * Code 18 emits a streak of zeros of length \[11..138\], i.e., + 11 + `ReadBits(7)` times. + + +6 Overall Structure of the Format +--------------------------------- + +Below is a view into the format in Backus-Naur form. It does not cover +all details. End-of-image (EOI) is only implicitly coded into the number +of pixels (xsize * ysize). + + +#### Basic Structure + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ::= + ::= +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +#### Structure of Transforms + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ::= (1-bit value 1; ) | + 1-bit value 0 + ::= | | | + + ::= 2-bit value 0; + ::= 3-bit sub-pixel code ; + ::= 2-bit value 1; + ::= 3-bit sub-pixel code ; + ::= 2-bit value 2 + ::= 2-bit value 3; + ::= 8-bit color count; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +#### Structure of the Image Data + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ::= + ::= + ::= 1-bit value 0 | + (1-bit value 1; ) + ::= 3-bit subsample value; + ::= 1 bit value 0 | + (1-bit value 1; 4-bit value for color cache size) + ::= | + ::= + + See "Interpretation of Meta Huffman codes" to + understand what each of these five Huffman codes are + for. + ::= | + ::= see "Simple code length code" for details + ::= ; encoded code lengths + ::= see section "Normal code length code" + ::= (( | | ) + ) | "" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A possible example sequence: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1-bit value 1 +1-bit value 11-bit value 0 + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[canonical_huff]: http://en.wikipedia.org/wiki/Canonical_Huffman_code diff --git a/internal/libwebp-1.2.1/examples/Android.mk b/internal/libwebp-1.2.1/examples/Android.mk new file mode 100644 index 0000000..13fb3b4 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/Android.mk @@ -0,0 +1,93 @@ +LOCAL_PATH := $(call my-dir) + +################################################################################ +# libexample_util + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + example_util.c \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src + +LOCAL_MODULE := example_util + +include $(BUILD_STATIC_LIBRARY) + +################################################################################ +# cwebp + +include $(CLEAR_VARS) + +# Note: to enable jpeg/png encoding the sources from AOSP can be used with +# minor modification to their Android.mk files. +LOCAL_SRC_FILES := \ + cwebp.c \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webpdemux webp + +LOCAL_MODULE := cwebp + +include $(BUILD_EXECUTABLE) + +################################################################################ +# dwebp + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + dwebp.c \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_STATIC_LIBRARIES := example_util imagedec imageenc webpdemux webp +LOCAL_MODULE := dwebp + +include $(BUILD_EXECUTABLE) + +################################################################################ +# webpmux + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + webpmux.c \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_STATIC_LIBRARIES := example_util imageio_util webpmux webp + +LOCAL_MODULE := webpmux_example + +include $(BUILD_EXECUTABLE) + +################################################################################ +# img2webp + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + img2webp.c \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webpmux webpdemux \ + webp + +LOCAL_MODULE := img2webp_example + +include $(BUILD_EXECUTABLE) + +################################################################################ +# webpinfo + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + webpinfo.c \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_STATIC_LIBRARIES := example_util imageio_util webp + +LOCAL_MODULE := webpinfo_example + +include $(BUILD_EXECUTABLE) diff --git a/internal/libwebp-1.2.1/examples/Makefile.am b/internal/libwebp-1.2.1/examples/Makefile.am new file mode 100644 index 0000000..8d52589 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/Makefile.am @@ -0,0 +1,119 @@ +AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src + +bin_PROGRAMS = +if BUILD_DEMUX + bin_PROGRAMS += dwebp cwebp +endif +if BUILD_ANIMDIFF + noinst_PROGRAMS = anim_diff anim_dump +endif +if BUILD_GIF2WEBP + bin_PROGRAMS += gif2webp +endif +if BUILD_IMG2WEBP + bin_PROGRAMS += img2webp +endif +if BUILD_MUX + bin_PROGRAMS += webpmux +endif +if BUILD_VWEBP + bin_PROGRAMS += vwebp +endif +if BUILD_WEBPINFO + bin_PROGRAMS += webpinfo +endif + +noinst_LTLIBRARIES = libexample_util.la + +libexample_util_la_SOURCES = example_util.c example_util.h +libexample_util_la_LIBADD = ../src/libwebp.la + +anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h gifdec.c gifdec.h +anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES) +anim_diff_LDADD = +anim_diff_LDADD += ../src/demux/libwebpdemux.la +anim_diff_LDADD += libexample_util.la +anim_diff_LDADD += ../imageio/libimageio_util.la +anim_diff_LDADD += $(GIF_LIBS) -lm + +anim_dump_SOURCES = anim_dump.c anim_util.c anim_util.h gifdec.c gifdec.h +anim_dump_CPPFLAGS = $(AM_CPPFLAGS) $(PNG_INCLUDES) +anim_dump_CPPFLAGS += $(GIF_INCLUDES) +anim_dump_LDADD = +anim_dump_LDADD += ../src/demux/libwebpdemux.la +anim_dump_LDADD += libexample_util.la +anim_dump_LDADD += ../imageio/libimageio_util.la +anim_dump_LDADD += ../imageio/libimageenc.la +anim_dump_LDADD += $(PNG_LIBS) $(GIF_LIBS) $(TIFF_LIBS) -lm + +cwebp_SOURCES = cwebp.c stopwatch.h +cwebp_CPPFLAGS = $(AM_CPPFLAGS) +cwebp_LDADD = +cwebp_LDADD += libexample_util.la +cwebp_LDADD += ../imageio/libimageio_util.la +cwebp_LDADD += ../imageio/libimagedec.la +cwebp_LDADD += ../src/libwebp.la +cwebp_LDADD += $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS) + +dwebp_SOURCES = dwebp.c stopwatch.h +dwebp_CPPFLAGS = $(AM_CPPFLAGS) +dwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES) +dwebp_LDADD = +dwebp_LDADD += libexample_util.la +dwebp_LDADD += ../imageio/libimagedec.la +dwebp_LDADD += ../imageio/libimageenc.la +dwebp_LDADD += ../imageio/libimageio_util.la +dwebp_LDADD += ../src/libwebp.la +dwebp_LDADD +=$(PNG_LIBS) $(JPEG_LIBS) + +gif2webp_SOURCES = gif2webp.c gifdec.c gifdec.h +gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES) +gif2webp_LDADD = +gif2webp_LDADD += libexample_util.la +gif2webp_LDADD += ../imageio/libimageio_util.la +gif2webp_LDADD += ../src/mux/libwebpmux.la +gif2webp_LDADD += ../src/libwebp.la +gif2webp_LDADD += $(GIF_LIBS) + +vwebp_SOURCES = vwebp.c +vwebp_CPPFLAGS = $(AM_CPPFLAGS) $(GL_INCLUDES) +vwebp_LDADD = +vwebp_LDADD += libexample_util.la +vwebp_LDADD += ../imageio/libimageio_util.la +vwebp_LDADD += ../src/demux/libwebpdemux.la +vwebp_LDADD += $(GL_LIBS) + +webpmux_SOURCES = webpmux.c +webpmux_CPPFLAGS = $(AM_CPPFLAGS) +webpmux_LDADD = +webpmux_LDADD += libexample_util.la +webpmux_LDADD += ../imageio/libimageio_util.la +webpmux_LDADD += ../src/mux/libwebpmux.la +webpmux_LDADD += ../src/libwebp.la + +img2webp_SOURCES = img2webp.c +img2webp_CPPFLAGS = $(AM_CPPFLAGS) +img2webp_LDADD = +img2webp_LDADD += libexample_util.la +img2webp_LDADD += ../imageio/libimageio_util.la +img2webp_LDADD += ../imageio/libimagedec.la +img2webp_LDADD += ../src/mux/libwebpmux.la +img2webp_LDADD += ../src/libwebp.la +img2webp_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS) + +webpinfo_SOURCES = webpinfo.c +webpinfo_CPPFLAGS = $(AM_CPPFLAGS) +webpinfo_LDADD = +webpinfo_LDADD += libexample_util.la +webpinfo_LDADD += ../imageio/libimageio_util.la +webpinfo_LDADD += ../src/libwebp.la + +if BUILD_LIBWEBPDECODER + anim_diff_LDADD += ../src/libwebpdecoder.la + anim_dump_LDADD += ../src/libwebpdecoder.la + vwebp_LDADD += ../src/libwebpdecoder.la +else + anim_diff_LDADD += ../src/libwebp.la + anim_dump_LDADD += ../src/libwebp.la + vwebp_LDADD += ../src/libwebp.la +endif diff --git a/internal/libwebp-1.2.1/examples/anim_diff.c b/internal/libwebp-1.2.1/examples/anim_diff.c new file mode 100644 index 0000000..7ffabc8 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/anim_diff.c @@ -0,0 +1,317 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Checks if given pair of animated GIF/WebP images are identical: +// That is: their reconstructed canvases match pixel-by-pixel and their other +// animation properties (loop count etc) also match. +// +// example: anim_diff foo.gif bar.webp + +#include +#include +#include +#include // for 'strtod'. +#include // for 'strcmp'. + +#include "./anim_util.h" +#include "./example_util.h" +#include "./unicode.h" + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + +// Returns true if 'a + b' will overflow. +static int AdditionWillOverflow(int a, int b) { + return (b > 0) && (a > INT_MAX - b); +} + +static int FramesAreEqual(const uint8_t* const rgba1, + const uint8_t* const rgba2, int width, int height) { + const int stride = width * 4; // Always true for 'DecodedFrame.rgba'. + return !memcmp(rgba1, rgba2, stride * height); +} + +static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst, + int max_allowed_diff) { + const int src_a = (src >> 24) & 0xff; + const int src_r = (src >> 16) & 0xff; + const int src_g = (src >> 8) & 0xff; + const int src_b = (src >> 0) & 0xff; + const int dst_a = (dst >> 24) & 0xff; + const int dst_r = (dst >> 16) & 0xff; + 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); +} + +static int FramesAreSimilar(const uint8_t* const rgba1, + const uint8_t* const rgba2, + int width, int height, int max_allowed_diff) { + int i, j; + assert(max_allowed_diff > 0); + for (j = 0; j < height; ++j) { + for (i = 0; i < width; ++i) { + const int stride = width * 4; + const size_t offset = j * stride + i; + if (!PixelsAreSimilar(rgba1[offset], rgba2[offset], max_allowed_diff)) { + return 0; + } + } + } + return 1; +} + +// Minimize number of frames by combining successive frames that have at max +// 'max_diff' difference per channel between corresponding pixels. +static void MinimizeAnimationFrames(AnimatedImage* const img, int max_diff) { + uint32_t i; + for (i = 1; i < img->num_frames; ++i) { + DecodedFrame* const frame1 = &img->frames[i - 1]; + DecodedFrame* const frame2 = &img->frames[i]; + const uint8_t* const rgba1 = frame1->rgba; + const uint8_t* const rgba2 = frame2->rgba; + int should_merge_frames = 0; + // If merging frames will result in integer overflow for 'duration', + // skip merging. + if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue; + if (max_diff > 0) { + should_merge_frames = FramesAreSimilar(rgba1, rgba2, img->canvas_width, + img->canvas_height, max_diff); + } else { + should_merge_frames = + FramesAreEqual(rgba1, rgba2, img->canvas_width, img->canvas_height); + } + if (should_merge_frames) { // Merge 'i+1'th frame into 'i'th frame. + frame1->duration += frame2->duration; + if (i + 1 < img->num_frames) { + memmove(&img->frames[i], &img->frames[i + 1], + (img->num_frames - i - 1) * sizeof(*img->frames)); + } + --img->num_frames; + --i; + } + } +} + +static int CompareValues(uint32_t a, uint32_t b, const char* output_str) { + if (a != b) { + fprintf(stderr, "%s: %d vs %d\n", output_str, a, b); + return 0; + } + return 1; +} + +static int CompareBackgroundColor(uint32_t bg1, uint32_t bg2, int premultiply) { + if (premultiply) { + const int alpha1 = (bg1 >> 24) & 0xff; + const int alpha2 = (bg2 >> 24) & 0xff; + if (alpha1 == 0 && alpha2 == 0) return 1; + } + if (bg1 != bg2) { + fprintf(stderr, "Background color mismatch: 0x%08x vs 0x%08x\n", + bg1, bg2); + return 0; + } + return 1; +} + +// Note: As long as frame durations and reconstructed frames are identical, it +// is OK for other aspects like offsets, dispose/blend method to vary. +static int CompareAnimatedImagePair(const AnimatedImage* const img1, + const AnimatedImage* const img2, + int premultiply, + double min_psnr) { + int ok = 1; + const int is_multi_frame_image = (img1->num_frames > 1); + uint32_t i; + + ok = CompareValues(img1->canvas_width, img2->canvas_width, + "Canvas width mismatch") && ok; + ok = CompareValues(img1->canvas_height, img2->canvas_height, + "Canvas height mismatch") && ok; + ok = CompareValues(img1->num_frames, img2->num_frames, + "Frame count mismatch") && ok; + if (!ok) return 0; // These are fatal failures, can't proceed. + + if (is_multi_frame_image) { // Checks relevant for multi-frame images only. + int max_loop_count_workaround = 0; + // Transcodes to webp increase the gif loop count by 1 for compatibility. + // When the gif has the maximum value the webp value will be off by one. + if ((img1->format == ANIM_GIF && img1->loop_count == 65536 && + img2->format == ANIM_WEBP && img2->loop_count == 65535) || + (img1->format == ANIM_WEBP && img1->loop_count == 65535 && + img2->format == ANIM_GIF && img2->loop_count == 65536)) { + max_loop_count_workaround = 1; + } + ok = (max_loop_count_workaround || + CompareValues(img1->loop_count, img2->loop_count, + "Loop count mismatch")) && ok; + ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor, + premultiply) && ok; + } + + for (i = 0; i < img1->num_frames; ++i) { + // Pixel-by-pixel comparison. + const uint8_t* const rgba1 = img1->frames[i].rgba; + const uint8_t* const rgba2 = img2->frames[i].rgba; + int max_diff; + double psnr; + if (is_multi_frame_image) { // Check relevant for multi-frame images only. + const char format[] = "Frame #%d, duration mismatch"; + char tmp[sizeof(format) + 8]; + ok = ok && (snprintf(tmp, sizeof(tmp), format, i) >= 0); + ok = ok && CompareValues(img1->frames[i].duration, + img2->frames[i].duration, tmp); + } + GetDiffAndPSNR(rgba1, rgba2, img1->canvas_width, img1->canvas_height, + premultiply, &max_diff, &psnr); + if (min_psnr > 0.) { + if (psnr < min_psnr) { + fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i, + psnr, min_psnr); + ok = 0; + } + } else { + if (max_diff != 0) { + fprintf(stderr, "Frame #%d, max pixel diff: %d\n", i, max_diff); + ok = 0; + } + } + } + return ok; +} + +static void Help(void) { + printf("Usage: anim_diff [options]\n"); + printf("\nOptions:\n"); + printf(" -dump_frames dump decoded frames in PAM format\n"); + printf(" -min_psnr ... minimum per-frame PSNR\n"); + printf(" -raw_comparison ..... if this flag is not used, RGB is\n"); + printf(" premultiplied before comparison\n"); + printf(" -max_diff ..... maximum allowed difference per channel\n" + " between corresponding pixels in subsequent\n" + " frames\n"); + printf(" -h .................. this help\n"); + printf(" -version ............ print version number and exit\n"); +} + +int main(int argc, const char* argv[]) { + int return_code = -1; + int dump_frames = 0; + const char* dump_folder = NULL; + double min_psnr = 0.; + int got_input1 = 0; + int got_input2 = 0; + int premultiply = 1; + int max_diff = 0; + int i, c; + const char* files[2] = { NULL, NULL }; + AnimatedImage images[2]; + + INIT_WARGV(argc, argv); + + for (c = 1; c < argc; ++c) { + int parse_error = 0; + if (!strcmp(argv[c], "-dump_frames")) { + if (c < argc - 1) { + dump_frames = 1; + dump_folder = (const char*)GET_WARGV(argv, ++c); + } else { + parse_error = 1; + } + } else if (!strcmp(argv[c], "-min_psnr")) { + if (c < argc - 1) { + min_psnr = ExUtilGetFloat(argv[++c], &parse_error); + } else { + parse_error = 1; + } + } else if (!strcmp(argv[c], "-raw_comparison")) { + premultiply = 0; + } else if (!strcmp(argv[c], "-max_diff")) { + if (c < argc - 1) { + max_diff = ExUtilGetInt(argv[++c], 0, &parse_error); + } else { + parse_error = 1; + } + } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { + Help(); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-version")) { + int dec_version, demux_version; + GetAnimatedImageVersions(&dec_version, &demux_version); + printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n", + (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff, + (dec_version >> 0) & 0xff, + (demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff, + (demux_version >> 0) & 0xff); + FREE_WARGV_AND_RETURN(0); + } else { + if (!got_input1) { + files[0] = (const char*)GET_WARGV(argv, c); + got_input1 = 1; + } else if (!got_input2) { + files[1] = (const char*)GET_WARGV(argv, c); + got_input2 = 1; + } else { + parse_error = 1; + } + } + if (parse_error) { + Help(); + FREE_WARGV_AND_RETURN(-1); + } + } + if (argc < 3) { + Help(); + FREE_WARGV_AND_RETURN(-1); + } + + + if (!got_input2) { + Help(); + FREE_WARGV_AND_RETURN(-1); + } + + if (dump_frames) { + WPRINTF("Dumping decoded frames in: %s\n", (const W_CHAR*)dump_folder); + } + + memset(images, 0, sizeof(images)); + for (i = 0; i < 2; ++i) { + WPRINTF("Decoding file: %s\n", (const W_CHAR*)files[i]); + if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) { + WFPRINTF(stderr, "Error decoding file: %s\n Aborting.\n", + (const W_CHAR*)files[i]); + return_code = -2; + goto End; + } else { + MinimizeAnimationFrames(&images[i], max_diff); + } + } + + if (!CompareAnimatedImagePair(&images[0], &images[1], + premultiply, min_psnr)) { + WFPRINTF(stderr, "\nFiles %s and %s differ.\n", (const W_CHAR*)files[0], + (const W_CHAR*)files[1]); + return_code = -3; + } else { + WPRINTF("\nFiles %s and %s are identical.\n", (const W_CHAR*)files[0], + (const W_CHAR*)files[1]); + return_code = 0; + } + End: + ClearAnimatedImage(&images[0]); + ClearAnimatedImage(&images[1]); + FREE_WARGV_AND_RETURN(return_code); +} diff --git a/internal/libwebp-1.2.1/examples/anim_dump.c b/internal/libwebp-1.2.1/examples/anim_dump.c new file mode 100644 index 0000000..e447338 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/anim_dump.c @@ -0,0 +1,121 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Decodes an animated WebP file and dumps the decoded frames as PNG or TIFF. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include // for 'strcmp'. + +#include "./anim_util.h" +#include "webp/decode.h" +#include "../imageio/image_enc.h" +#include "./unicode.h" + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + +static void Help(void) { + printf("Usage: anim_dump [options] files...\n"); + printf("\nOptions:\n"); + printf(" -folder .... dump folder (default: '.')\n"); + printf(" -prefix .... prefix for dumped frames " + "(default: 'dump_')\n"); + printf(" -tiff ............... save frames as TIFF\n"); + printf(" -pam ................ save frames as PAM\n"); + printf(" -h .................. this help\n"); + printf(" -version ............ print version number and exit\n"); +} + +int main(int argc, const char* argv[]) { + int error = 0; + const W_CHAR* dump_folder = TO_W_CHAR("."); + const W_CHAR* prefix = TO_W_CHAR("dump_"); + const W_CHAR* suffix = TO_W_CHAR("png"); + WebPOutputFileFormat format = PNG; + int c; + + INIT_WARGV(argc, argv); + + if (argc < 2) { + Help(); + FREE_WARGV_AND_RETURN(-1); + } + + for (c = 1; !error && c < argc; ++c) { + if (!strcmp(argv[c], "-folder")) { + if (c + 1 == argc) { + fprintf(stderr, "missing argument after option '%s'\n", argv[c]); + error = 1; + break; + } + dump_folder = GET_WARGV(argv, ++c); + } else if (!strcmp(argv[c], "-prefix")) { + if (c + 1 == argc) { + fprintf(stderr, "missing argument after option '%s'\n", argv[c]); + error = 1; + break; + } + prefix = GET_WARGV(argv, ++c); + } else if (!strcmp(argv[c], "-tiff")) { + format = TIFF; + suffix = TO_W_CHAR("tiff"); + } else if (!strcmp(argv[c], "-pam")) { + format = PAM; + suffix = TO_W_CHAR("pam"); + } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { + Help(); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-version")) { + int dec_version, demux_version; + GetAnimatedImageVersions(&dec_version, &demux_version); + printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n", + (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff, + (dec_version >> 0) & 0xff, + (demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff, + (demux_version >> 0) & 0xff); + FREE_WARGV_AND_RETURN(0); + } else { + uint32_t i; + AnimatedImage image; + const W_CHAR* const file = GET_WARGV(argv, c); + memset(&image, 0, sizeof(image)); + WPRINTF("Decoding file: %s as %s/%sxxxx.%s\n", + file, dump_folder, prefix, suffix); + if (!ReadAnimatedImage((const char*)file, &image, 0, NULL)) { + WFPRINTF(stderr, "Error decoding file: %s\n Aborting.\n", file); + error = 1; + break; + } + for (i = 0; !error && i < image.num_frames; ++i) { + W_CHAR out_file[1024]; + WebPDecBuffer buffer; + WebPInitDecBuffer(&buffer); + buffer.colorspace = MODE_RGBA; + buffer.is_external_memory = 1; + buffer.width = image.canvas_width; + buffer.height = image.canvas_height; + buffer.u.RGBA.rgba = image.frames[i].rgba; + buffer.u.RGBA.stride = buffer.width * sizeof(uint32_t); + buffer.u.RGBA.size = buffer.u.RGBA.stride * buffer.height; + WSNPRINTF(out_file, sizeof(out_file), "%s/%s%.4d.%s", + dump_folder, prefix, i, suffix); + if (!WebPSaveImage(&buffer, format, (const char*)out_file)) { + WFPRINTF(stderr, "Error while saving image '%s'\n", out_file); + error = 1; + } + WebPFreeDecBuffer(&buffer); + } + ClearAnimatedImage(&image); + } + } + FREE_WARGV_AND_RETURN(error ? 1 : 0); +} diff --git a/internal/libwebp-1.2.1/examples/anim_util.c b/internal/libwebp-1.2.1/examples/anim_util.c new file mode 100644 index 0000000..97b32c0 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/anim_util.c @@ -0,0 +1,779 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Utilities for animated images + +#include "./anim_util.h" + +#include +#include +#include +#include + +#if defined(WEBP_HAVE_GIF) +#include +#endif +#include "webp/format_constants.h" +#include "webp/decode.h" +#include "webp/demux.h" +#include "../imageio/imageio_util.h" +#include "./gifdec.h" +#include "./unicode.h" +#include "./unicode_gif.h" + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + +static const int kNumChannels = 4; + +// ----------------------------------------------------------------------------- +// Common utilities. + +#if defined(WEBP_HAVE_GIF) +// Returns true if the frame covers the full canvas. +static int IsFullFrame(int width, int height, + int canvas_width, int canvas_height) { + return (width == canvas_width && height == canvas_height); +} +#endif // WEBP_HAVE_GIF + +static int CheckSizeForOverflow(uint64_t size) { + return (size == (size_t)size); +} + +static int AllocateFrames(AnimatedImage* const image, uint32_t num_frames) { + uint32_t i; + uint8_t* mem = NULL; + DecodedFrame* frames = NULL; + const uint64_t rgba_size = + (uint64_t)image->canvas_width * kNumChannels * image->canvas_height; + const uint64_t total_size = (uint64_t)num_frames * rgba_size * sizeof(*mem); + const uint64_t total_frame_size = (uint64_t)num_frames * sizeof(*frames); + if (!CheckSizeForOverflow(total_size) || + !CheckSizeForOverflow(total_frame_size)) { + return 0; + } + mem = (uint8_t*)WebPMalloc((size_t)total_size); + frames = (DecodedFrame*)WebPMalloc((size_t)total_frame_size); + + if (mem == NULL || frames == NULL) { + WebPFree(mem); + WebPFree(frames); + return 0; + } + WebPFree(image->raw_mem); + image->num_frames = num_frames; + image->frames = frames; + for (i = 0; i < num_frames; ++i) { + frames[i].rgba = mem + i * rgba_size; + frames[i].duration = 0; + frames[i].is_key_frame = 0; + } + image->raw_mem = mem; + return 1; +} + +void ClearAnimatedImage(AnimatedImage* const image) { + if (image != NULL) { + WebPFree(image->raw_mem); + WebPFree(image->frames); + image->num_frames = 0; + image->frames = NULL; + image->raw_mem = NULL; + } +} + +#if defined(WEBP_HAVE_GIF) +// Clear the canvas to transparent. +static void ZeroFillCanvas(uint8_t* rgba, + uint32_t canvas_width, uint32_t canvas_height) { + memset(rgba, 0, canvas_width * kNumChannels * canvas_height); +} + +// Clear given frame rectangle to transparent. +static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset, + int y_offset, int width, int height) { + int j; + assert(width * kNumChannels <= rgba_stride); + rgba += y_offset * rgba_stride + x_offset * kNumChannels; + for (j = 0; j < height; ++j) { + memset(rgba, 0, width * kNumChannels); + rgba += rgba_stride; + } +} + +// Copy width * height pixels from 'src' to 'dst'. +static void CopyCanvas(const uint8_t* src, uint8_t* dst, + uint32_t width, uint32_t height) { + assert(src != NULL && dst != NULL); + memcpy(dst, src, width * kNumChannels * height); +} + +// Copy pixels in the given rectangle from 'src' to 'dst' honoring the 'stride'. +static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride, + int x_offset, int y_offset, + int width, int height) { + int j; + const int width_in_bytes = width * kNumChannels; + const size_t offset = y_offset * stride + x_offset * kNumChannels; + assert(width_in_bytes <= stride); + src += offset; + dst += offset; + for (j = 0; j < height; ++j) { + memcpy(dst, src, width_in_bytes); + src += stride; + dst += stride; + } +} +#endif // WEBP_HAVE_GIF + +// Canonicalize all transparent pixels to transparent black to aid comparison. +static void CleanupTransparentPixels(uint32_t* rgba, + uint32_t width, uint32_t height) { + const uint32_t* const rgba_end = rgba + width * height; + while (rgba < rgba_end) { + const uint8_t alpha = (*rgba >> 24) & 0xff; + if (alpha == 0) { + *rgba = 0; + } + ++rgba; + } +} + +// Dump frame to a PAM file. Returns true on success. +static int DumpFrame(const char filename[], const char dump_folder[], + uint32_t frame_num, const uint8_t rgba[], + int canvas_width, int canvas_height) { + int ok = 0; + size_t max_len; + int y; + const W_CHAR* base_name = NULL; + W_CHAR* file_name = NULL; + FILE* f = NULL; + const char* row; + + if (dump_folder == NULL) dump_folder = (const char*)TO_W_CHAR("."); + + base_name = WSTRRCHR(filename, '/'); + base_name = (base_name == NULL) ? (const W_CHAR*)filename : base_name + 1; + max_len = WSTRLEN(dump_folder) + 1 + WSTRLEN(base_name) + + strlen("_frame_") + strlen(".pam") + 8; + file_name = (W_CHAR*)WebPMalloc(max_len * sizeof(*file_name)); + if (file_name == NULL) goto End; + + if (WSNPRINTF(file_name, max_len, "%s/%s_frame_%d.pam", + (const W_CHAR*)dump_folder, base_name, frame_num) < 0) { + fprintf(stderr, "Error while generating file name\n"); + goto End; + } + + f = WFOPEN(file_name, "wb"); + if (f == NULL) { + WFPRINTF(stderr, "Error opening file for writing: %s\n", file_name); + ok = 0; + goto End; + } + if (fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\n" + "DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n", + canvas_width, canvas_height) < 0) { + WFPRINTF(stderr, "Write error for file %s\n", file_name); + goto End; + } + row = (const char*)rgba; + for (y = 0; y < canvas_height; ++y) { + if (fwrite(row, canvas_width * kNumChannels, 1, f) != 1) { + WFPRINTF(stderr, "Error writing to file: %s\n", file_name); + goto End; + } + row += canvas_width * kNumChannels; + } + ok = 1; + End: + if (f != NULL) fclose(f); + WebPFree(file_name); + return ok; +} + +// ----------------------------------------------------------------------------- +// WebP Decoding. + +// Returns true if this is a valid WebP bitstream. +static int IsWebP(const WebPData* const webp_data) { + return (WebPGetInfo(webp_data->bytes, webp_data->size, NULL, NULL) != 0); +} + +// Read animated WebP bitstream 'webp_data' into 'AnimatedImage' struct. +static int ReadAnimatedWebP(const char filename[], + const WebPData* const webp_data, + AnimatedImage* const image, int dump_frames, + const char dump_folder[]) { + int ok = 0; + int dump_ok = 1; + uint32_t frame_index = 0; + int prev_frame_timestamp = 0; + WebPAnimDecoder* dec; + WebPAnimInfo anim_info; + + memset(image, 0, sizeof(*image)); + + dec = WebPAnimDecoderNew(webp_data, NULL); + if (dec == NULL) { + WFPRINTF(stderr, "Error parsing image: %s\n", (const W_CHAR*)filename); + goto End; + } + + if (!WebPAnimDecoderGetInfo(dec, &anim_info)) { + fprintf(stderr, "Error getting global info about the animation\n"); + goto End; + } + + // Animation properties. + image->canvas_width = anim_info.canvas_width; + image->canvas_height = anim_info.canvas_height; + image->loop_count = anim_info.loop_count; + image->bgcolor = anim_info.bgcolor; + + // Allocate frames. + if (!AllocateFrames(image, anim_info.frame_count)) return 0; + + // Decode frames. + while (WebPAnimDecoderHasMoreFrames(dec)) { + DecodedFrame* curr_frame; + uint8_t* curr_rgba; + uint8_t* frame_rgba; + int timestamp; + + if (!WebPAnimDecoderGetNext(dec, &frame_rgba, ×tamp)) { + fprintf(stderr, "Error decoding frame #%u\n", frame_index); + goto End; + } + assert(frame_index < anim_info.frame_count); + curr_frame = &image->frames[frame_index]; + curr_rgba = curr_frame->rgba; + curr_frame->duration = timestamp - prev_frame_timestamp; + curr_frame->is_key_frame = 0; // Unused. + memcpy(curr_rgba, frame_rgba, + image->canvas_width * kNumChannels * image->canvas_height); + + // Needed only because we may want to compare with GIF later. + CleanupTransparentPixels((uint32_t*)curr_rgba, + image->canvas_width, image->canvas_height); + + if (dump_frames && dump_ok) { + dump_ok = DumpFrame(filename, dump_folder, frame_index, curr_rgba, + image->canvas_width, image->canvas_height); + if (!dump_ok) { // Print error once, but continue decode loop. + fprintf(stderr, "Error dumping frames to %s\n", dump_folder); + } + } + + ++frame_index; + prev_frame_timestamp = timestamp; + } + ok = dump_ok; + if (ok) image->format = ANIM_WEBP; + + End: + WebPAnimDecoderDelete(dec); + return ok; +} + +// ----------------------------------------------------------------------------- +// GIF Decoding. + +#if defined(WEBP_HAVE_GIF) + +// Returns true if this is a valid GIF bitstream. +static int IsGIF(const WebPData* const data) { + return data->size > GIF_STAMP_LEN && + (!memcmp(GIF_STAMP, data->bytes, GIF_STAMP_LEN) || + !memcmp(GIF87_STAMP, data->bytes, GIF_STAMP_LEN) || + !memcmp(GIF89_STAMP, data->bytes, GIF_STAMP_LEN)); +} + +// GIFLIB_MAJOR is only defined in libgif >= 4.2.0. +#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) +# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR) +# define LOCAL_GIF_PREREQ(maj, min) \ + (LOCAL_GIF_VERSION >= (((maj) << 8) | (min))) +#else +# define LOCAL_GIF_VERSION 0 +# define LOCAL_GIF_PREREQ(maj, min) 0 +#endif + +#if !LOCAL_GIF_PREREQ(5, 0) + +// Added in v5.0 +typedef struct { + int DisposalMode; +#define DISPOSAL_UNSPECIFIED 0 // No disposal specified +#define DISPOSE_DO_NOT 1 // Leave image in place +#define DISPOSE_BACKGROUND 2 // Set area to background color +#define DISPOSE_PREVIOUS 3 // Restore to previous content + int UserInputFlag; // User confirmation required before disposal + int DelayTime; // Pre-display delay in 0.01sec units + int TransparentColor; // Palette index for transparency, -1 if none +#define NO_TRANSPARENT_COLOR -1 +} GraphicsControlBlock; + +static int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType* GifExtension, + GraphicsControlBlock* gcb) { + if (GifExtensionLength != 4) { + return GIF_ERROR; + } + gcb->DisposalMode = (GifExtension[0] >> 2) & 0x07; + gcb->UserInputFlag = (GifExtension[0] & 0x02) != 0; + gcb->DelayTime = GifExtension[1] | (GifExtension[2] << 8); + if (GifExtension[0] & 0x01) { + gcb->TransparentColor = (int)GifExtension[3]; + } else { + gcb->TransparentColor = NO_TRANSPARENT_COLOR; + } + return GIF_OK; +} + +static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex, + GraphicsControlBlock* gcb) { + int i; + if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) { + return GIF_ERROR; + } + gcb->DisposalMode = DISPOSAL_UNSPECIFIED; + gcb->UserInputFlag = 0; + gcb->DelayTime = 0; + gcb->TransparentColor = NO_TRANSPARENT_COLOR; + + for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) { + ExtensionBlock* ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; + if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { + return DGifExtensionToGCB( + ep->ByteCount, (const GifByteType*)ep->Bytes, gcb); + } + } + return GIF_ERROR; +} + +#define CONTINUE_EXT_FUNC_CODE 0x00 + +// Signature was changed in v5.0 +#define DGifOpenFileName(a, b) DGifOpenFileName(a) + +#endif // !LOCAL_GIF_PREREQ(5, 0) + +// Signature changed in v5.1 +#if !LOCAL_GIF_PREREQ(5, 1) +#define DGifCloseFile(a, b) DGifCloseFile(a) +#endif + +static int IsKeyFrameGIF(const GifImageDesc* prev_desc, int prev_dispose, + const DecodedFrame* const prev_frame, + int canvas_width, int canvas_height) { + if (prev_frame == NULL) return 1; + if (prev_dispose == DISPOSE_BACKGROUND) { + if (IsFullFrame(prev_desc->Width, prev_desc->Height, + canvas_width, canvas_height)) { + return 1; + } + if (prev_frame->is_key_frame) return 1; + } + return 0; +} + +static int GetTransparentIndexGIF(GifFileType* gif) { + GraphicsControlBlock first_gcb; + memset(&first_gcb, 0, sizeof(first_gcb)); + DGifSavedExtensionToGCB(gif, 0, &first_gcb); + return first_gcb.TransparentColor; +} + +static uint32_t GetBackgroundColorGIF(GifFileType* gif) { + const int transparent_index = GetTransparentIndexGIF(gif); + const ColorMapObject* const color_map = gif->SColorMap; + if (transparent_index != NO_TRANSPARENT_COLOR && + gif->SBackGroundColor == transparent_index) { + return 0x00000000; // Special case: transparent black. + } else if (color_map == NULL || color_map->Colors == NULL + || gif->SBackGroundColor >= color_map->ColorCount) { + return 0xffffffff; // Invalid: assume white. + } else { + const GifColorType color = color_map->Colors[gif->SBackGroundColor]; + return (0xffu << 24) | + (color.Red << 16) | + (color.Green << 8) | + (color.Blue << 0); + } +} + +// Find appropriate app extension and get loop count from the next extension. +// We use Chrome's interpretation of the 'loop_count' semantics: +// if not present -> loop once +// if present and loop_count == 0, return 0 ('infinite'). +// if present and loop_count != 0, it's the number of *extra* loops +// so we need to return loop_count + 1 as total loop number. +static uint32_t GetLoopCountGIF(const GifFileType* const gif) { + int i; + for (i = 0; i < gif->ImageCount; ++i) { + const SavedImage* const image = &gif->SavedImages[i]; + int j; + for (j = 0; (j + 1) < image->ExtensionBlockCount; ++j) { + const ExtensionBlock* const eb1 = image->ExtensionBlocks + j; + const ExtensionBlock* const eb2 = image->ExtensionBlocks + j + 1; + const char* const signature = (const char*)eb1->Bytes; + const int signature_is_ok = + (eb1->Function == APPLICATION_EXT_FUNC_CODE) && + (eb1->ByteCount == 11) && + (!memcmp(signature, "NETSCAPE2.0", 11) || + !memcmp(signature, "ANIMEXTS1.0", 11)); + if (signature_is_ok && + eb2->Function == CONTINUE_EXT_FUNC_CODE && eb2->ByteCount >= 3 && + eb2->Bytes[0] == 1) { + const uint32_t extra_loop = ((uint32_t)(eb2->Bytes[2]) << 8) + + ((uint32_t)(eb2->Bytes[1]) << 0); + return (extra_loop > 0) ? extra_loop + 1 : 0; + } + } + } + return 1; // Default. +} + +// Get duration of 'n'th frame in milliseconds. +static int GetFrameDurationGIF(GifFileType* gif, int n) { + GraphicsControlBlock gcb; + memset(&gcb, 0, sizeof(gcb)); + DGifSavedExtensionToGCB(gif, n, &gcb); + return gcb.DelayTime * 10; +} + +// Returns true if frame 'target' completely covers 'covered'. +static int CoversFrameGIF(const GifImageDesc* const target, + const GifImageDesc* const covered) { + return target->Left <= covered->Left && + covered->Left + covered->Width <= target->Left + target->Width && + target->Top <= covered->Top && + covered->Top + covered->Height <= target->Top + target->Height; +} + +static void RemapPixelsGIF(const uint8_t* const src, + const ColorMapObject* const cmap, + int transparent_color, int len, uint8_t* dst) { + int i; + for (i = 0; i < len; ++i) { + if (src[i] != transparent_color) { + // If a pixel in the current frame is transparent, we don't modify it, so + // that we can see-through the corresponding pixel from an earlier frame. + const GifColorType c = cmap->Colors[src[i]]; + dst[4 * i + 0] = c.Red; + dst[4 * i + 1] = c.Green; + dst[4 * i + 2] = c.Blue; + dst[4 * i + 3] = 0xff; + } + } +} + +static int ReadFrameGIF(const SavedImage* const gif_image, + const ColorMapObject* cmap, int transparent_color, + int out_stride, uint8_t* const dst) { + const GifImageDesc* image_desc = &gif_image->ImageDesc; + const uint8_t* in; + uint8_t* out; + int j; + + if (image_desc->ColorMap) cmap = image_desc->ColorMap; + + if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) { + fprintf(stderr, "Potentially corrupt color map.\n"); + return 0; + } + + in = (const uint8_t*)gif_image->RasterBits; + out = dst + image_desc->Top * out_stride + image_desc->Left * kNumChannels; + + for (j = 0; j < image_desc->Height; ++j) { + RemapPixelsGIF(in, cmap, transparent_color, image_desc->Width, out); + in += image_desc->Width; + out += out_stride; + } + return 1; +} + +// Read animated GIF bitstream from 'filename' into 'AnimatedImage' struct. +static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image, + int dump_frames, const char dump_folder[]) { + uint32_t frame_count; + uint32_t canvas_width, canvas_height; + uint32_t i; + int gif_error; + GifFileType* gif; + + gif = DGifOpenFileUnicode((const W_CHAR*)filename, NULL); + if (gif == NULL) { + WFPRINTF(stderr, "Could not read file: %s.\n", (const W_CHAR*)filename); + return 0; + } + + gif_error = DGifSlurp(gif); + if (gif_error != GIF_OK) { + WFPRINTF(stderr, "Could not parse image: %s.\n", (const W_CHAR*)filename); + GIFDisplayError(gif, gif_error); + DGifCloseFile(gif, NULL); + return 0; + } + + // Animation properties. + image->canvas_width = (uint32_t)gif->SWidth; + image->canvas_height = (uint32_t)gif->SHeight; + if (image->canvas_width > MAX_CANVAS_SIZE || + image->canvas_height > MAX_CANVAS_SIZE) { + fprintf(stderr, "Invalid canvas dimension: %d x %d\n", + image->canvas_width, image->canvas_height); + DGifCloseFile(gif, NULL); + return 0; + } + image->loop_count = GetLoopCountGIF(gif); + image->bgcolor = GetBackgroundColorGIF(gif); + + frame_count = (uint32_t)gif->ImageCount; + if (frame_count == 0) { + DGifCloseFile(gif, NULL); + return 0; + } + + if (image->canvas_width == 0 || image->canvas_height == 0) { + image->canvas_width = gif->SavedImages[0].ImageDesc.Width; + image->canvas_height = gif->SavedImages[0].ImageDesc.Height; + gif->SavedImages[0].ImageDesc.Left = 0; + gif->SavedImages[0].ImageDesc.Top = 0; + if (image->canvas_width == 0 || image->canvas_height == 0) { + fprintf(stderr, "Invalid canvas size in GIF.\n"); + DGifCloseFile(gif, NULL); + return 0; + } + } + // Allocate frames. + AllocateFrames(image, frame_count); + + canvas_width = image->canvas_width; + canvas_height = image->canvas_height; + + // Decode and reconstruct frames. + for (i = 0; i < frame_count; ++i) { + const int canvas_width_in_bytes = canvas_width * kNumChannels; + const SavedImage* const curr_gif_image = &gif->SavedImages[i]; + GraphicsControlBlock curr_gcb; + DecodedFrame* curr_frame; + uint8_t* curr_rgba; + + memset(&curr_gcb, 0, sizeof(curr_gcb)); + DGifSavedExtensionToGCB(gif, i, &curr_gcb); + + curr_frame = &image->frames[i]; + curr_rgba = curr_frame->rgba; + curr_frame->duration = GetFrameDurationGIF(gif, i); + // Force frames with a small or no duration to 100ms to be consistent + // with web browsers and other transcoding tools (like gif2webp itself). + if (curr_frame->duration <= 10) curr_frame->duration = 100; + + if (i == 0) { // Initialize as transparent. + curr_frame->is_key_frame = 1; + ZeroFillCanvas(curr_rgba, canvas_width, canvas_height); + } else { + DecodedFrame* const prev_frame = &image->frames[i - 1]; + const GifImageDesc* const prev_desc = &gif->SavedImages[i - 1].ImageDesc; + GraphicsControlBlock prev_gcb; + memset(&prev_gcb, 0, sizeof(prev_gcb)); + DGifSavedExtensionToGCB(gif, i - 1, &prev_gcb); + + curr_frame->is_key_frame = + IsKeyFrameGIF(prev_desc, prev_gcb.DisposalMode, prev_frame, + canvas_width, canvas_height); + + if (curr_frame->is_key_frame) { // Initialize as transparent. + ZeroFillCanvas(curr_rgba, canvas_width, canvas_height); + } else { + int prev_frame_disposed, curr_frame_opaque; + int prev_frame_completely_covered; + // Initialize with previous canvas. + uint8_t* const prev_rgba = image->frames[i - 1].rgba; + CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height); + + // Dispose previous frame rectangle. + prev_frame_disposed = + (prev_gcb.DisposalMode == DISPOSE_BACKGROUND || + prev_gcb.DisposalMode == DISPOSE_PREVIOUS); + curr_frame_opaque = + (curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR); + prev_frame_completely_covered = + curr_frame_opaque && + CoversFrameGIF(&curr_gif_image->ImageDesc, prev_desc); + + if (prev_frame_disposed && !prev_frame_completely_covered) { + switch (prev_gcb.DisposalMode) { + case DISPOSE_BACKGROUND: { + ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes, + prev_desc->Left, prev_desc->Top, + prev_desc->Width, prev_desc->Height); + break; + } + case DISPOSE_PREVIOUS: { + int src_frame_num = i - 2; + while (src_frame_num >= 0) { + GraphicsControlBlock src_frame_gcb; + memset(&src_frame_gcb, 0, sizeof(src_frame_gcb)); + DGifSavedExtensionToGCB(gif, src_frame_num, &src_frame_gcb); + if (src_frame_gcb.DisposalMode != DISPOSE_PREVIOUS) break; + --src_frame_num; + } + if (src_frame_num >= 0) { + // Restore pixels inside previous frame rectangle to + // corresponding pixels in source canvas. + uint8_t* const src_frame_rgba = + image->frames[src_frame_num].rgba; + CopyFrameRectangle(src_frame_rgba, curr_rgba, + canvas_width_in_bytes, + prev_desc->Left, prev_desc->Top, + prev_desc->Width, prev_desc->Height); + } else { + // Source canvas doesn't exist. So clear previous frame + // rectangle to background. + ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes, + prev_desc->Left, prev_desc->Top, + prev_desc->Width, prev_desc->Height); + } + break; + } + default: + break; // Nothing to do. + } + } + } + } + + // Decode current frame. + if (!ReadFrameGIF(curr_gif_image, gif->SColorMap, curr_gcb.TransparentColor, + canvas_width_in_bytes, curr_rgba)) { + DGifCloseFile(gif, NULL); + return 0; + } + + if (dump_frames) { + if (!DumpFrame(filename, dump_folder, i, curr_rgba, + canvas_width, canvas_height)) { + DGifCloseFile(gif, NULL); + return 0; + } + } + } + image->format = ANIM_GIF; + DGifCloseFile(gif, NULL); + return 1; +} + +#else + +static int IsGIF(const WebPData* const data) { + (void)data; + return 0; +} + +static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image, + int dump_frames, const char dump_folder[]) { + (void)filename; + (void)image; + (void)dump_frames; + (void)dump_folder; + fprintf(stderr, "GIF support not compiled. Please install the libgif-dev " + "package before building.\n"); + return 0; +} + +#endif // WEBP_HAVE_GIF + +// ----------------------------------------------------------------------------- + +int ReadAnimatedImage(const char filename[], AnimatedImage* const image, + int dump_frames, const char dump_folder[]) { + int ok = 0; + WebPData webp_data; + + WebPDataInit(&webp_data); + memset(image, 0, sizeof(*image)); + + if (!ImgIoUtilReadFile(filename, &webp_data.bytes, &webp_data.size)) { + WFPRINTF(stderr, "Error reading file: %s\n", (const W_CHAR*)filename); + return 0; + } + + if (IsWebP(&webp_data)) { + ok = ReadAnimatedWebP(filename, &webp_data, image, dump_frames, + dump_folder); + } else if (IsGIF(&webp_data)) { + ok = ReadAnimatedGIF(filename, image, dump_frames, dump_folder); + } else { + WFPRINTF(stderr, + "Unknown file type: %s. Supported file types are WebP and GIF\n", + (const W_CHAR*)filename); + ok = 0; + } + if (!ok) ClearAnimatedImage(image); + WebPDataClear(&webp_data); + return ok; +} + +static void Accumulate(double v1, double v2, double* const max_diff, + double* const sse) { + const double diff = fabs(v1 - v2); + if (diff > *max_diff) *max_diff = diff; + *sse += diff * diff; +} + +void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[], + uint32_t width, uint32_t height, int premultiply, + int* const max_diff, double* const psnr) { + const uint32_t stride = width * kNumChannels; + const int kAlphaChannel = kNumChannels - 1; + double f_max_diff = 0.; + double sse = 0.; + uint32_t x, y; + for (y = 0; y < height; ++y) { + for (x = 0; x < stride; x += kNumChannels) { + int k; + const size_t offset = (size_t)y * stride + x; + const int alpha1 = rgba1[offset + kAlphaChannel]; + const int alpha2 = rgba2[offset + kAlphaChannel]; + Accumulate(alpha1, alpha2, &f_max_diff, &sse); + if (!premultiply) { + for (k = 0; k < kAlphaChannel; ++k) { + Accumulate(rgba1[offset + k], rgba2[offset + k], &f_max_diff, &sse); + } + } else { + // premultiply R/G/B channels with alpha value + for (k = 0; k < kAlphaChannel; ++k) { + Accumulate(rgba1[offset + k] * alpha1 / 255., + rgba2[offset + k] * alpha2 / 255., + &f_max_diff, &sse); + } + } + } + } + *max_diff = (int)f_max_diff; + if (*max_diff == 0) { + *psnr = 99.; // PSNR when images are identical. + } else { + sse /= stride * height; + *psnr = 4.3429448 * log(255. * 255. / sse); + } +} + +void GetAnimatedImageVersions(int* const decoder_version, + int* const demux_version) { + *decoder_version = WebPGetDecoderVersion(); + *demux_version = WebPGetDemuxVersion(); +} diff --git a/internal/libwebp-1.2.1/examples/anim_util.h b/internal/libwebp-1.2.1/examples/anim_util.h new file mode 100644 index 0000000..574e032 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/anim_util.h @@ -0,0 +1,73 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Utilities for animated images + +#ifndef WEBP_EXAMPLES_ANIM_UTIL_H_ +#define WEBP_EXAMPLES_ANIM_UTIL_H_ + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include "webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + ANIM_GIF, + ANIM_WEBP +} AnimatedFileFormat; + +typedef struct { + uint8_t* rgba; // Decoded and reconstructed full frame. + int duration; // Frame duration in milliseconds. + int is_key_frame; // True if this frame is a key-frame. +} DecodedFrame; + +typedef struct { + AnimatedFileFormat format; + uint32_t canvas_width; + uint32_t canvas_height; + uint32_t bgcolor; + uint32_t loop_count; + DecodedFrame* frames; + uint32_t num_frames; + void* raw_mem; +} AnimatedImage; + +// Deallocate everything in 'image' (but not the object itself). +void ClearAnimatedImage(AnimatedImage* const image); + +// Read animated image file into 'AnimatedImage' struct. +// If 'dump_frames' is true, dump frames to 'dump_folder'. +// Previous content of 'image' is obliterated. +// Upon successful return, content of 'image' must be deleted by +// calling 'ClearAnimatedImage'. +int ReadAnimatedImage(const char filename[], AnimatedImage* const image, + int dump_frames, const char dump_folder[]); + +// Given two RGBA buffers, calculate max pixel difference and PSNR. +// If 'premultiply' is true, R/G/B values will be pre-multiplied by the +// transparency before comparison. +void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[], + uint32_t width, uint32_t height, int premultiply, + int* const max_diff, double* const psnr); + +// Return library versions used by anim_util. +void GetAnimatedImageVersions(int* const decoder_version, + int* const demux_version); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_EXAMPLES_ANIM_UTIL_H_ diff --git a/internal/libwebp-1.2.1/examples/cwebp.c b/internal/libwebp-1.2.1/examples/cwebp.c new file mode 100644 index 0000000..927063a --- /dev/null +++ b/internal/libwebp-1.2.1/examples/cwebp.c @@ -0,0 +1,1239 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// simple command line calling the WebPEncode function. +// Encodes a raw .YUV into WebP bitstream +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include "../examples/example_util.h" +#include "../imageio/image_dec.h" +#include "../imageio/imageio_util.h" +#include "../imageio/webpdec.h" +#include "./stopwatch.h" +#include "./unicode.h" +#include "webp/encode.h" + +#ifndef WEBP_DLL +#ifdef __cplusplus +extern "C" { +#endif + +extern void* VP8GetCPUInfo; // opaque forward declaration. + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // WEBP_DLL + +//------------------------------------------------------------------------------ + +static int verbose = 0; + +static int ReadYUV(const uint8_t* const data, size_t data_size, + WebPPicture* const pic) { + const int use_argb = pic->use_argb; + const int uv_width = (pic->width + 1) / 2; + const int uv_height = (pic->height + 1) / 2; + const int y_plane_size = pic->width * pic->height; + const int uv_plane_size = uv_width * uv_height; + const size_t expected_data_size = y_plane_size + 2 * uv_plane_size; + + if (data_size != expected_data_size) { + fprintf(stderr, + "input data doesn't have the expected size (%d instead of %d)\n", + (int)data_size, (int)expected_data_size); + return 0; + } + + pic->use_argb = 0; + if (!WebPPictureAlloc(pic)) return 0; + ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride, + pic->width, pic->height); + ImgIoUtilCopyPlane(data + y_plane_size, uv_width, + pic->u, pic->uv_stride, uv_width, uv_height); + ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width, + pic->v, pic->uv_stride, uv_width, uv_height); + return use_argb ? WebPPictureYUVAToARGB(pic) : 1; +} + +#ifdef HAVE_WINCODEC_H + +static int ReadPicture(const char* const filename, WebPPicture* const pic, + int keep_alpha, Metadata* const metadata) { + int ok = 0; + const uint8_t* data = NULL; + size_t data_size = 0; + if (pic->width != 0 && pic->height != 0) { + ok = ImgIoUtilReadFile(filename, &data, &data_size); + ok = ok && ReadYUV(data, data_size, pic); + } else { + // If no size specified, try to decode it using WIC. + ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata); + if (!ok) { + ok = ImgIoUtilReadFile(filename, &data, &data_size); + ok = ok && ReadWebP(data, data_size, pic, keep_alpha, metadata); + } + } + if (!ok) { + WFPRINTF(stderr, "Error! Could not process file %s\n", + (const W_CHAR*)filename); + } + WebPFree((void*)data); + return ok; +} + +#else // !HAVE_WINCODEC_H + +static int ReadPicture(const char* const filename, WebPPicture* const pic, + int keep_alpha, Metadata* const metadata) { + const uint8_t* data = NULL; + size_t data_size = 0; + int ok = 0; + + ok = ImgIoUtilReadFile(filename, &data, &data_size); + if (!ok) goto End; + + if (pic->width == 0 || pic->height == 0) { + WebPImageReader reader = WebPGuessImageReader(data, data_size); + ok = reader(data, data_size, pic, keep_alpha, metadata); + } else { + // If image size is specified, infer it as YUV format. + ok = ReadYUV(data, data_size, pic); + } + End: + if (!ok) { + WFPRINTF(stderr, "Error! Could not process file %s\n", + (const W_CHAR*)filename); + } + WebPFree((void*)data); + return ok; +} + +#endif // !HAVE_WINCODEC_H + +static void AllocExtraInfo(WebPPicture* const pic) { + const int mb_w = (pic->width + 15) / 16; + const int mb_h = (pic->height + 15) / 16; + pic->extra_info = + (uint8_t*)WebPMalloc(mb_w * mb_h * sizeof(*pic->extra_info)); +} + +static void PrintByteCount(const int bytes[4], int total_size, + int* const totals) { + int s; + int total = 0; + for (s = 0; s < 4; ++s) { + fprintf(stderr, "| %7d ", bytes[s]); + total += bytes[s]; + if (totals) totals[s] += bytes[s]; + } + fprintf(stderr, "| %7d (%.1f%%)\n", total, 100.f * total / total_size); +} + +static void PrintPercents(const int counts[4]) { + int s; + const int total = counts[0] + counts[1] + counts[2] + counts[3]; + for (s = 0; s < 4; ++s) { + fprintf(stderr, "| %3d%%", (int)(100. * counts[s] / total + .5)); + } + fprintf(stderr, "| %7d\n", total); +} + +static void PrintValues(const int values[4]) { + int s; + for (s = 0; s < 4; ++s) { + fprintf(stderr, "| %7d ", values[s]); + } + fprintf(stderr, "|\n"); +} + +static void PrintFullLosslessInfo(const WebPAuxStats* const stats, + const char* const description) { + fprintf(stderr, "Lossless-%s compressed size: %d bytes\n", + description, stats->lossless_size); + fprintf(stderr, " * Header size: %d bytes, image data size: %d\n", + stats->lossless_hdr_size, stats->lossless_data_size); + if (stats->lossless_features) { + fprintf(stderr, " * Lossless features used:"); + if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION"); + if (stats->lossless_features & 2) fprintf(stderr, " CROSS-COLOR-TRANSFORM"); + if (stats->lossless_features & 4) fprintf(stderr, " SUBTRACT-GREEN"); + if (stats->lossless_features & 8) fprintf(stderr, " PALETTE"); + fprintf(stderr, "\n"); + } + fprintf(stderr, " * Precision Bits: histogram=%d transform=%d cache=%d\n", + stats->histogram_bits, stats->transform_bits, stats->cache_bits); + if (stats->palette_size > 0) { + fprintf(stderr, " * Palette size: %d\n", stats->palette_size); + } +} + +static void PrintExtraInfoLossless(const WebPPicture* const pic, + int short_output, + const char* const file_name) { + const WebPAuxStats* const stats = pic->stats; + if (short_output) { + fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]); + } else { + WFPRINTF(stderr, "File: %s\n", (const W_CHAR*)file_name); + fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height); + fprintf(stderr, "Output: %d bytes (%.2f bpp)\n", stats->coded_size, + 8.f * stats->coded_size / pic->width / pic->height); + PrintFullLosslessInfo(stats, "ARGB"); + } +} + +static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output, + int full_details, + const char* const file_name) { + const WebPAuxStats* const stats = pic->stats; + if (short_output) { + fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]); + } else { + const int num_i4 = stats->block_count[0]; + const int num_i16 = stats->block_count[1]; + const int num_skip = stats->block_count[2]; + const int total = num_i4 + num_i16; + WFPRINTF(stderr, "File: %s\n", (const W_CHAR*)file_name); + fprintf(stderr, "Dimension: %d x %d%s\n", + pic->width, pic->height, + stats->alpha_data_size ? " (with alpha)" : ""); + fprintf(stderr, "Output: " + "%d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f %2.2f dB\n" + " (%.2f bpp)\n", + stats->coded_size, + stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3], + 8.f * stats->coded_size / pic->width / pic->height); + if (total > 0) { + int totals[4] = { 0, 0, 0, 0 }; + fprintf(stderr, "block count: intra4: %6d (%.2f%%)\n" + " intra16: %6d (%.2f%%)\n" + " skipped: %6d (%.2f%%)\n", + num_i4, 100.f * num_i4 / total, + num_i16, 100.f * num_i16 / total, + num_skip, 100.f * num_skip / total); + fprintf(stderr, "bytes used: header: %6d (%.1f%%)\n" + " mode-partition: %6d (%.1f%%)\n", + stats->header_bytes[0], + 100.f * stats->header_bytes[0] / stats->coded_size, + stats->header_bytes[1], + 100.f * stats->header_bytes[1] / stats->coded_size); + if (stats->alpha_data_size > 0) { + fprintf(stderr, " transparency: %6d (%.1f dB)\n", + stats->alpha_data_size, stats->PSNR[4]); + } + fprintf(stderr, " Residuals bytes " + "|segment 1|segment 2|segment 3" + "|segment 4| total\n"); + if (full_details) { + fprintf(stderr, " intra4-coeffs: "); + PrintByteCount(stats->residual_bytes[0], stats->coded_size, totals); + fprintf(stderr, " intra16-coeffs: "); + PrintByteCount(stats->residual_bytes[1], stats->coded_size, totals); + fprintf(stderr, " chroma coeffs: "); + PrintByteCount(stats->residual_bytes[2], stats->coded_size, totals); + } + fprintf(stderr, " macroblocks: "); + PrintPercents(stats->segment_size); + fprintf(stderr, " quantizer: "); + PrintValues(stats->segment_quant); + fprintf(stderr, " filter level: "); + PrintValues(stats->segment_level); + if (full_details) { + fprintf(stderr, "------------------+---------"); + fprintf(stderr, "+---------+---------+---------+-----------------\n"); + fprintf(stderr, " segments total: "); + PrintByteCount(totals, stats->coded_size, NULL); + } + } + if (stats->lossless_size > 0) { + PrintFullLosslessInfo(stats, "alpha"); + } + } +} + +static void PrintMapInfo(const WebPPicture* const pic) { + if (pic->extra_info != NULL) { + const int mb_w = (pic->width + 15) / 16; + const int mb_h = (pic->height + 15) / 16; + const int type = pic->extra_info_type; + int x, y; + for (y = 0; y < mb_h; ++y) { + for (x = 0; x < mb_w; ++x) { + const int c = pic->extra_info[x + y * mb_w]; + if (type == 1) { // intra4/intra16 + fprintf(stderr, "%c", "+."[c]); + } else if (type == 2) { // segments + fprintf(stderr, "%c", ".-*X"[c]); + } else if (type == 3) { // quantizers + fprintf(stderr, "%.2d ", c); + } else if (type == 6 || type == 7) { + fprintf(stderr, "%3d ", c); + } else { + fprintf(stderr, "0x%.2x ", c); + } + } + fprintf(stderr, "\n"); + } + } +} + +//------------------------------------------------------------------------------ + +static int MyWriter(const uint8_t* data, size_t data_size, + const WebPPicture* const pic) { + FILE* const out = (FILE*)pic->custom_ptr; + return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1; +} + +// Dumps a picture as a PGM file using the IMC4 layout. +static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) { + int y; + const int uv_width = (picture->width + 1) / 2; + const int uv_height = (picture->height + 1) / 2; + const int stride = (picture->width + 1) & ~1; + const uint8_t* src_y = picture->y; + const uint8_t* src_u = picture->u; + const uint8_t* src_v = picture->v; + const uint8_t* src_a = picture->a; + const int alpha_height = + WebPPictureHasTransparency(picture) ? picture->height : 0; + const int height = picture->height + uv_height + alpha_height; + FILE* const f = WFOPEN(PGM_name, "wb"); + if (f == NULL) return 0; + fprintf(f, "P5\n%d %d\n255\n", stride, height); + for (y = 0; y < picture->height; ++y) { + if (fwrite(src_y, picture->width, 1, f) != 1) return 0; + if (picture->width & 1) fputc(0, f); // pad + src_y += picture->y_stride; + } + for (y = 0; y < uv_height; ++y) { + if (fwrite(src_u, uv_width, 1, f) != 1) return 0; + if (fwrite(src_v, uv_width, 1, f) != 1) return 0; + src_u += picture->uv_stride; + src_v += picture->uv_stride; + } + for (y = 0; y < alpha_height; ++y) { + if (fwrite(src_a, picture->width, 1, f) != 1) return 0; + if (picture->width & 1) fputc(0, f); // pad + src_a += picture->a_stride; + } + fclose(f); + return 1; +} + +// ----------------------------------------------------------------------------- +// Metadata writing. + +enum { + METADATA_EXIF = (1 << 0), + METADATA_ICC = (1 << 1), + METADATA_XMP = (1 << 2), + METADATA_ALL = METADATA_EXIF | METADATA_ICC | METADATA_XMP +}; + +static const int kChunkHeaderSize = 8; +static const int kTagSize = 4; + +static void PrintMetadataInfo(const Metadata* const metadata, + int metadata_written) { + if (metadata == NULL || metadata_written == 0) return; + + fprintf(stderr, "Metadata:\n"); + if (metadata_written & METADATA_ICC) { + fprintf(stderr, " * ICC profile: %6d bytes\n", (int)metadata->iccp.size); + } + if (metadata_written & METADATA_EXIF) { + fprintf(stderr, " * EXIF data: %6d bytes\n", (int)metadata->exif.size); + } + if (metadata_written & METADATA_XMP) { + fprintf(stderr, " * XMP data: %6d bytes\n", (int)metadata->xmp.size); + } +} + +// Outputs, in little endian, 'num' bytes from 'val' to 'out'. +static int WriteLE(FILE* const out, uint32_t val, int num) { + uint8_t buf[4]; + int i; + for (i = 0; i < num; ++i) { + buf[i] = (uint8_t)(val & 0xff); + val >>= 8; + } + return (fwrite(buf, num, 1, out) == 1); +} + +static int WriteLE24(FILE* const out, uint32_t val) { + return WriteLE(out, val, 3); +} + +static int WriteLE32(FILE* const out, uint32_t val) { + return WriteLE(out, val, 4); +} + +static int WriteMetadataChunk(FILE* const out, const char fourcc[4], + const MetadataPayload* const payload) { + const uint8_t zero = 0; + const size_t need_padding = payload->size & 1; + int ok = (fwrite(fourcc, kTagSize, 1, out) == 1); + ok = ok && WriteLE32(out, (uint32_t)payload->size); + ok = ok && (fwrite(payload->bytes, payload->size, 1, out) == 1); + return ok && (fwrite(&zero, need_padding, need_padding, out) == need_padding); +} + +// Sets 'flag' in 'vp8x_flags' and updates 'metadata_size' with the size of the +// chunk if there is metadata and 'keep' is true. +static int UpdateFlagsAndSize(const MetadataPayload* const payload, + int keep, int flag, + uint32_t* vp8x_flags, uint64_t* metadata_size) { + if (keep && payload->bytes != NULL && payload->size > 0) { + *vp8x_flags |= flag; + *metadata_size += kChunkHeaderSize + payload->size + (payload->size & 1); + return 1; + } + return 0; +} + +// Writes a WebP file using the image contained in 'memory_writer' and the +// metadata from 'metadata'. Metadata is controlled by 'keep_metadata' and the +// availability in 'metadata'. Returns true on success. +// For details see doc/webp-container-spec.txt#extended-file-format. +static int WriteWebPWithMetadata(FILE* const out, + const WebPPicture* const picture, + const WebPMemoryWriter* const memory_writer, + const Metadata* const metadata, + int keep_metadata, + int* const metadata_written) { + const char kVP8XHeader[] = "VP8X\x0a\x00\x00\x00"; + const int kAlphaFlag = 0x10; + const int kEXIFFlag = 0x08; + const int kICCPFlag = 0x20; + const int kXMPFlag = 0x04; + const size_t kRiffHeaderSize = 12; + const size_t kMaxChunkPayload = ~0 - kChunkHeaderSize - 1; + const size_t kMinSize = kRiffHeaderSize + kChunkHeaderSize; + uint32_t flags = 0; + uint64_t metadata_size = 0; + const int write_exif = UpdateFlagsAndSize(&metadata->exif, + !!(keep_metadata & METADATA_EXIF), + kEXIFFlag, &flags, &metadata_size); + const int write_iccp = UpdateFlagsAndSize(&metadata->iccp, + !!(keep_metadata & METADATA_ICC), + kICCPFlag, &flags, &metadata_size); + const int write_xmp = UpdateFlagsAndSize(&metadata->xmp, + !!(keep_metadata & METADATA_XMP), + kXMPFlag, &flags, &metadata_size); + uint8_t* webp = memory_writer->mem; + size_t webp_size = memory_writer->size; + + *metadata_written = 0; + + if (webp_size < kMinSize) return 0; + if (webp_size - kChunkHeaderSize + metadata_size > kMaxChunkPayload) { + fprintf(stderr, "Error! Addition of metadata would exceed " + "container size limit.\n"); + return 0; + } + + if (metadata_size > 0) { + const int kVP8XChunkSize = 18; + const int has_vp8x = !memcmp(webp + kRiffHeaderSize, "VP8X", kTagSize); + const uint32_t riff_size = (uint32_t)(webp_size - kChunkHeaderSize + + (has_vp8x ? 0 : kVP8XChunkSize) + + metadata_size); + // RIFF + int ok = (fwrite(webp, kTagSize, 1, out) == 1); + // RIFF size (file header size is not recorded) + ok = ok && WriteLE32(out, riff_size); + webp += kChunkHeaderSize; + webp_size -= kChunkHeaderSize; + // WEBP + ok = ok && (fwrite(webp, kTagSize, 1, out) == 1); + webp += kTagSize; + webp_size -= kTagSize; + if (has_vp8x) { // update the existing VP8X flags + webp[kChunkHeaderSize] |= (uint8_t)(flags & 0xff); + ok = ok && (fwrite(webp, kVP8XChunkSize, 1, out) == 1); + webp += kVP8XChunkSize; + webp_size -= kVP8XChunkSize; + } else { + const int is_lossless = !memcmp(webp, "VP8L", kTagSize); + if (is_lossless) { + // Presence of alpha is stored in the 37th bit (29th after the + // signature) of VP8L data. + if (webp[kChunkHeaderSize + 4] & (1 << 4)) flags |= kAlphaFlag; + } + ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1); + ok = ok && WriteLE32(out, flags); + ok = ok && WriteLE24(out, picture->width - 1); + ok = ok && WriteLE24(out, picture->height - 1); + } + if (write_iccp) { + ok = ok && WriteMetadataChunk(out, "ICCP", &metadata->iccp); + *metadata_written |= METADATA_ICC; + } + // Image + ok = ok && (fwrite(webp, webp_size, 1, out) == 1); + if (write_exif) { + ok = ok && WriteMetadataChunk(out, "EXIF", &metadata->exif); + *metadata_written |= METADATA_EXIF; + } + if (write_xmp) { + ok = ok && WriteMetadataChunk(out, "XMP ", &metadata->xmp); + *metadata_written |= METADATA_XMP; + } + return ok; + } + + // No metadata, just write the original image file. + return (fwrite(webp, webp_size, 1, out) == 1); +} + +//------------------------------------------------------------------------------ + +static int ProgressReport(int percent, const WebPPicture* const picture) { + fprintf(stderr, "[%s]: %3d %% \r", + (char*)picture->user_data, percent); + return 1; // all ok +} + +//------------------------------------------------------------------------------ + +static void HelpShort(void) { + printf("Usage:\n\n"); + printf(" cwebp [options] -q quality input.png -o output.webp\n\n"); + printf("where quality is between 0 (poor) to 100 (very good).\n"); + printf("Typical value is around 80.\n\n"); + printf("Try -longhelp for an exhaustive list of advanced options.\n"); +} + +static void HelpLong(void) { + printf("Usage:\n"); + printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n"); + printf("If input size (-s) for an image is not specified, it is\n" + "assumed to be a PNG, JPEG, TIFF or WebP file.\n"); + printf("Note: Animated PNG and WebP files are not supported.\n"); +#ifdef HAVE_WINCODEC_H + printf("Windows builds can take as input any of the files handled by WIC.\n"); +#endif + printf("\nOptions:\n"); + printf(" -h / -help ............. short help\n"); + printf(" -H / -longhelp ......... long help\n"); + printf(" -q ............. quality factor (0:small..100:big), " + "default=75\n"); + printf(" -alpha_q ......... transparency-compression quality (0..100)," + "\n default=100\n"); + printf(" -preset ....... preset setting, one of:\n"); + printf(" default, photo, picture,\n"); + printf(" drawing, icon, text\n"); + printf(" -preset must come first, as it overwrites other parameters\n"); + printf(" -z ............... activates lossless preset with given\n" + " level in [0:fast, ..., 9:slowest]\n"); + printf("\n"); + printf(" -m ............... compression method (0=fast, 6=slowest), " + "default=4\n"); + printf(" -segments ........ number of segments to use (1..4), " + "default=4\n"); + printf(" -size ............ target size (in bytes)\n"); + printf(" -psnr .......... target PSNR (in dB. typically: 42)\n"); + printf("\n"); + printf(" -s ......... input size (width x height) for YUV\n"); + printf(" -sns ............. spatial noise shaping (0:off, 100:max), " + "default=50\n"); + printf(" -f ............... filter strength (0=off..100), " + "default=60\n"); + printf(" -sharpness ....... " + "filter sharpness (0:most .. 7:least sharp), default=0\n"); + printf(" -strong ................ use strong filter instead " + "of simple (default)\n"); + printf(" -nostrong .............. use simple filter instead of strong\n"); + printf(" -sharp_yuv ............. use sharper (and slower) RGB->YUV " + "conversion\n"); + printf(" -partition_limit . limit quality to fit the 512k limit on\n"); + printf(" " + "the first partition (0=no degradation ... 100=full)\n"); + printf(" -pass ............ analysis pass number (1..10)\n"); + printf(" -qrange .... specifies the permissible quality range\n" + " (default: 0 100)\n"); + printf(" -crop .. crop picture with the given rectangle\n"); + printf(" -resize ........ resize picture (after any cropping)\n"); + printf(" -mt .................... use multi-threading if available\n"); + printf(" -low_memory ............ reduce memory usage (slower encoding)\n"); + printf(" -map ............. print map of extra info\n"); + printf(" -print_psnr ............ prints averaged PSNR distortion\n"); + printf(" -print_ssim ............ prints averaged SSIM distortion\n"); + printf(" -print_lsim ............ prints local-similarity distortion\n"); + printf(" -d .......... dump the compressed output (PGM file)\n"); + printf(" -alpha_method .... transparency-compression method (0..1), " + "default=1\n"); + printf(" -alpha_filter . predictive filtering for alpha plane,\n"); + printf(" one of: none, fast (default) or best\n"); + printf(" -exact ................. preserve RGB values in transparent area, " + "default=off\n"); + printf(" -blend_alpha ..... blend colors against background color\n" + " expressed as RGB values written in\n" + " hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n" + " green=0xe0 and blue=0xd0\n"); + printf(" -noalpha ............... discard any transparency information\n"); + printf(" -lossless .............. encode image losslessly, default=off\n"); + printf(" -near_lossless ... use near-lossless image\n" + " preprocessing (0..100=off), " + "default=100\n"); + printf(" -hint ......... specify image characteristics hint,\n"); + printf(" one of: photo, picture or graph\n"); + + printf("\n"); + printf(" -metadata ..... comma separated list of metadata to\n"); + printf(" "); + printf("copy from the input to the output if present.\n"); + printf(" " + "Valid values: all, none (default), exif, icc, xmp\n"); + + printf("\n"); + printf(" -short ................. condense printed message\n"); + printf(" -quiet ................. don't print anything\n"); + printf(" -version ............... print version number and exit\n"); +#ifndef WEBP_DLL + printf(" -noasm ................. disable all assembly optimizations\n"); +#endif + printf(" -v ..................... verbose, e.g. print encoding/decoding " + "times\n"); + printf(" -progress .............. report encoding progress\n"); + printf("\n"); + printf("Experimental Options:\n"); + printf(" -jpeg_like ............. roughly match expected JPEG size\n"); + printf(" -af .................... auto-adjust filter strength\n"); + printf(" -pre ............. pre-processing filter\n"); + printf("\n"); +} + +//------------------------------------------------------------------------------ +// Error messages + +static const char* const kErrorMessages[VP8_ENC_ERROR_LAST] = { + "OK", + "OUT_OF_MEMORY: Out of memory allocating objects", + "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer", + "NULL_PARAMETER: NULL parameter passed to function", + "INVALID_CONFIGURATION: configuration is invalid", + "BAD_DIMENSION: Bad picture dimension. Maximum width and height " + "allowed is 16383 pixels.", + "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n" + "To reduce the size of this partition, try using less segments " + "with the -segments option, and eventually reduce the number of " + "header bits using -partition_limit. More details are available " + "in the manual (`man cwebp`)", + "PARTITION_OVERFLOW: Partition is too big to fit 16M", + "BAD_WRITE: Picture writer returned an I/O error", + "FILE_TOO_BIG: File would be too big to fit in 4G", + "USER_ABORT: encoding abort requested by user" +}; + +//------------------------------------------------------------------------------ + +int main(int argc, const char* argv[]) { + int return_value = -1; + const char* in_file = NULL, *out_file = NULL, *dump_file = NULL; + FILE* out = NULL; + int c; + int short_output = 0; + int quiet = 0; + int keep_alpha = 1; + int blend_alpha = 0; + uint32_t background_color = 0xffffffu; + int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0; + int resize_w = 0, resize_h = 0; + int lossless_preset = 6; + int use_lossless_preset = -1; // -1=unset, 0=don't use, 1=use it + int show_progress = 0; + int keep_metadata = 0; + int metadata_written = 0; + WebPPicture picture; + int print_distortion = -1; // -1=off, 0=PSNR, 1=SSIM, 2=LSIM + WebPPicture original_picture; // when PSNR or SSIM is requested + WebPConfig config; + WebPAuxStats stats; + WebPMemoryWriter memory_writer; + int use_memory_writer; + Metadata metadata; + Stopwatch stop_watch; + + INIT_WARGV(argc, argv); + + MetadataInit(&metadata); + WebPMemoryWriterInit(&memory_writer); + if (!WebPPictureInit(&picture) || + !WebPPictureInit(&original_picture) || + !WebPConfigInit(&config)) { + fprintf(stderr, "Error! Version mismatch!\n"); + FREE_WARGV_AND_RETURN(-1); + } + + if (argc == 1) { + HelpShort(); + FREE_WARGV_AND_RETURN(0); + } + + for (c = 1; c < argc; ++c) { + int parse_error = 0; + if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { + HelpShort(); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) { + HelpLong(); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-o") && c + 1 < argc) { + out_file = (const char*)GET_WARGV(argv, ++c); + } else if (!strcmp(argv[c], "-d") && c + 1 < argc) { + dump_file = (const char*)GET_WARGV(argv, ++c); + config.show_compressed = 1; + } else if (!strcmp(argv[c], "-print_psnr")) { + config.show_compressed = 1; + print_distortion = 0; + } else if (!strcmp(argv[c], "-print_ssim")) { + config.show_compressed = 1; + print_distortion = 1; + } else if (!strcmp(argv[c], "-print_lsim")) { + config.show_compressed = 1; + print_distortion = 2; + } else if (!strcmp(argv[c], "-short")) { + ++short_output; + } else if (!strcmp(argv[c], "-s") && c + 2 < argc) { + picture.width = ExUtilGetInt(argv[++c], 0, &parse_error); + picture.height = ExUtilGetInt(argv[++c], 0, &parse_error); + if (picture.width > WEBP_MAX_DIMENSION || picture.width < 0 || + picture.height > WEBP_MAX_DIMENSION || picture.height < 0) { + fprintf(stderr, + "Specified dimension (%d x %d) is out of range.\n", + picture.width, picture.height); + goto Error; + } + } else if (!strcmp(argv[c], "-m") && c + 1 < argc) { + config.method = ExUtilGetInt(argv[++c], 0, &parse_error); + use_lossless_preset = 0; // disable -z option + } else if (!strcmp(argv[c], "-q") && c + 1 < argc) { + config.quality = ExUtilGetFloat(argv[++c], &parse_error); + use_lossless_preset = 0; // disable -z option + } else if (!strcmp(argv[c], "-z") && c + 1 < argc) { + lossless_preset = ExUtilGetInt(argv[++c], 0, &parse_error); + if (use_lossless_preset != 0) use_lossless_preset = 1; + } else if (!strcmp(argv[c], "-alpha_q") && c + 1 < argc) { + config.alpha_quality = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-alpha_method") && c + 1 < argc) { + config.alpha_compression = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-alpha_cleanup")) { + // This flag is obsolete, does opposite of -exact. + config.exact = 0; + } else if (!strcmp(argv[c], "-exact")) { + config.exact = 1; + } else if (!strcmp(argv[c], "-blend_alpha") && c + 1 < argc) { + blend_alpha = 1; + // background color is given in hex with an optional '0x' prefix + background_color = ExUtilGetInt(argv[++c], 16, &parse_error); + background_color = background_color & 0x00ffffffu; + } else if (!strcmp(argv[c], "-alpha_filter") && c + 1 < argc) { + ++c; + if (!strcmp(argv[c], "none")) { + config.alpha_filtering = 0; + } else if (!strcmp(argv[c], "fast")) { + config.alpha_filtering = 1; + } else if (!strcmp(argv[c], "best")) { + config.alpha_filtering = 2; + } else { + fprintf(stderr, "Error! Unrecognized alpha filter: %s\n", argv[c]); + goto Error; + } + } else if (!strcmp(argv[c], "-noalpha")) { + keep_alpha = 0; + } else if (!strcmp(argv[c], "-lossless")) { + config.lossless = 1; + } else if (!strcmp(argv[c], "-near_lossless") && c + 1 < argc) { + config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error); + config.lossless = 1; // use near-lossless only with lossless + } else if (!strcmp(argv[c], "-hint") && c + 1 < argc) { + ++c; + if (!strcmp(argv[c], "photo")) { + config.image_hint = WEBP_HINT_PHOTO; + } else if (!strcmp(argv[c], "picture")) { + config.image_hint = WEBP_HINT_PICTURE; + } else if (!strcmp(argv[c], "graph")) { + config.image_hint = WEBP_HINT_GRAPH; + } else { + fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]); + goto Error; + } + } else if (!strcmp(argv[c], "-size") && c + 1 < argc) { + config.target_size = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-psnr") && c + 1 < argc) { + config.target_PSNR = ExUtilGetFloat(argv[++c], &parse_error); + } else if (!strcmp(argv[c], "-sns") && c + 1 < argc) { + config.sns_strength = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-f") && c + 1 < argc) { + config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-af")) { + config.autofilter = 1; + } else if (!strcmp(argv[c], "-jpeg_like")) { + config.emulate_jpeg_size = 1; + } else if (!strcmp(argv[c], "-mt")) { + ++config.thread_level; // increase thread level + } else if (!strcmp(argv[c], "-low_memory")) { + config.low_memory = 1; + } else if (!strcmp(argv[c], "-strong")) { + config.filter_type = 1; + } else if (!strcmp(argv[c], "-nostrong")) { + config.filter_type = 0; + } else if (!strcmp(argv[c], "-sharpness") && c + 1 < argc) { + config.filter_sharpness = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-sharp_yuv")) { + config.use_sharp_yuv = 1; + } else if (!strcmp(argv[c], "-pass") && c + 1 < argc) { + config.pass = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-qrange") && c + 2 < argc) { + config.qmin = ExUtilGetInt(argv[++c], 0, &parse_error); + config.qmax = ExUtilGetInt(argv[++c], 0, &parse_error); + if (config.qmin < 0) config.qmin = 0; + if (config.qmax > 100) config.qmax = 100; + } else if (!strcmp(argv[c], "-pre") && c + 1 < argc) { + config.preprocessing = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-segments") && c + 1 < argc) { + config.segments = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-partition_limit") && c + 1 < argc) { + config.partition_limit = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-map") && c + 1 < argc) { + picture.extra_info_type = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-crop") && c + 4 < argc) { + crop = 1; + crop_x = ExUtilGetInt(argv[++c], 0, &parse_error); + crop_y = ExUtilGetInt(argv[++c], 0, &parse_error); + crop_w = ExUtilGetInt(argv[++c], 0, &parse_error); + crop_h = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-resize") && c + 2 < argc) { + resize_w = ExUtilGetInt(argv[++c], 0, &parse_error); + resize_h = ExUtilGetInt(argv[++c], 0, &parse_error); +#ifndef WEBP_DLL + } else if (!strcmp(argv[c], "-noasm")) { + VP8GetCPUInfo = NULL; +#endif + } else if (!strcmp(argv[c], "-version")) { + const int version = WebPGetEncoderVersion(); + printf("%d.%d.%d\n", + (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-progress")) { + show_progress = 1; + } else if (!strcmp(argv[c], "-quiet")) { + quiet = 1; + } else if (!strcmp(argv[c], "-preset") && c + 1 < argc) { + WebPPreset preset; + ++c; + if (!strcmp(argv[c], "default")) { + preset = WEBP_PRESET_DEFAULT; + } else if (!strcmp(argv[c], "photo")) { + preset = WEBP_PRESET_PHOTO; + } else if (!strcmp(argv[c], "picture")) { + preset = WEBP_PRESET_PICTURE; + } else if (!strcmp(argv[c], "drawing")) { + preset = WEBP_PRESET_DRAWING; + } else if (!strcmp(argv[c], "icon")) { + preset = WEBP_PRESET_ICON; + } else if (!strcmp(argv[c], "text")) { + preset = WEBP_PRESET_TEXT; + } else { + fprintf(stderr, "Error! Unrecognized preset: %s\n", argv[c]); + goto Error; + } + if (!WebPConfigPreset(&config, preset, config.quality)) { + fprintf(stderr, "Error! Could initialize configuration with preset.\n"); + goto Error; + } + } else if (!strcmp(argv[c], "-metadata") && c + 1 < argc) { + static const struct { + const char* option; + int flag; + } kTokens[] = { + { "all", METADATA_ALL }, + { "none", 0 }, + { "exif", METADATA_EXIF }, + { "icc", METADATA_ICC }, + { "xmp", METADATA_XMP }, + }; + const size_t kNumTokens = sizeof(kTokens) / sizeof(kTokens[0]); + const char* start = argv[++c]; + const char* const end = start + strlen(start); + + while (start < end) { + size_t i; + const char* token = strchr(start, ','); + if (token == NULL) token = end; + + for (i = 0; i < kNumTokens; ++i) { + if ((size_t)(token - start) == strlen(kTokens[i].option) && + !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) { + if (kTokens[i].flag != 0) { + keep_metadata |= kTokens[i].flag; + } else { + keep_metadata = 0; + } + break; + } + } + if (i == kNumTokens) { + fprintf(stderr, "Error! Unknown metadata type '%.*s'\n", + (int)(token - start), start); + FREE_WARGV_AND_RETURN(-1); + } + start = token + 1; + } +#ifdef HAVE_WINCODEC_H + if (keep_metadata != 0 && keep_metadata != METADATA_ICC) { + // TODO(jzern): remove when -metadata is supported on all platforms. + fprintf(stderr, "Warning: only ICC profile extraction is currently" + " supported on this platform!\n"); + } +#endif + } else if (!strcmp(argv[c], "-v")) { + verbose = 1; + } else if (!strcmp(argv[c], "--")) { + if (c + 1 < argc) in_file = (const char*)GET_WARGV(argv, ++c); + break; + } else if (argv[c][0] == '-') { + fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]); + HelpLong(); + FREE_WARGV_AND_RETURN(-1); + } else { + in_file = (const char*)GET_WARGV(argv, c); + } + + if (parse_error) { + HelpLong(); + FREE_WARGV_AND_RETURN(-1); + } + } + if (in_file == NULL) { + fprintf(stderr, "No input file specified!\n"); + HelpShort(); + goto Error; + } + + if (use_lossless_preset == 1) { + if (!WebPConfigLosslessPreset(&config, lossless_preset)) { + fprintf(stderr, "Invalid lossless preset (-z %d)\n", lossless_preset); + goto Error; + } + } + + // Check for unsupported command line options for lossless mode and log + // warning for such options. + if (!quiet && config.lossless == 1) { + if (config.target_size > 0 || config.target_PSNR > 0) { + fprintf(stderr, "Encoding for specified size or PSNR is not supported" + " for lossless encoding. Ignoring such option(s)!\n"); + } + if (config.partition_limit > 0) { + fprintf(stderr, "Partition limit option is not required for lossless" + " encoding. Ignoring this option!\n"); + } + } + // If a target size or PSNR was given, but somehow the -pass option was + // omitted, force a reasonable value. + if (config.target_size > 0 || config.target_PSNR > 0) { + if (config.pass == 1) config.pass = 6; + } + + if (!WebPValidateConfig(&config)) { + fprintf(stderr, "Error! Invalid configuration.\n"); + goto Error; + } + + // Read the input. We need to decide if we prefer ARGB or YUVA + // samples, depending on the expected compression mode (this saves + // some conversion steps). + picture.use_argb = (config.lossless || config.use_sharp_yuv || + config.preprocessing > 0 || + crop || (resize_w | resize_h) > 0); + if (verbose) { + StopwatchReset(&stop_watch); + } + if (!ReadPicture(in_file, &picture, keep_alpha, + (keep_metadata == 0) ? NULL : &metadata)) { + WFPRINTF(stderr, "Error! Cannot read input picture file '%s'\n", + (const W_CHAR*)in_file); + goto Error; + } + picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL; + + if (blend_alpha) { + WebPBlendAlpha(&picture, background_color); + } + + if (verbose) { + const double read_time = StopwatchReadAndReset(&stop_watch); + fprintf(stderr, "Time to read input: %.3fs\n", read_time); + } + // The bitstream should be kept in memory when metadata must be appended + // before writing it to a file/stream, and/or when the near-losslessly encoded + // bitstream must be decoded for distortion computation (lossy will modify the + // 'picture' but not the lossless pipeline). + // Otherwise directly write the bitstream to a file. + use_memory_writer = (out_file != NULL && keep_metadata) || + (!quiet && print_distortion >= 0 && config.lossless && + config.near_lossless < 100); + + // Open the output + if (out_file != NULL) { + const int use_stdout = !WSTRCMP(out_file, "-"); + out = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : WFOPEN(out_file, "wb"); + if (out == NULL) { + WFPRINTF(stderr, "Error! Cannot open output file '%s'\n", + (const W_CHAR*)out_file); + goto Error; + } else { + if (!short_output && !quiet) { + WFPRINTF(stderr, "Saving file '%s'\n", (const W_CHAR*)out_file); + } + } + if (use_memory_writer) { + picture.writer = WebPMemoryWrite; + picture.custom_ptr = (void*)&memory_writer; + } else { + picture.writer = MyWriter; + picture.custom_ptr = (void*)out; + } + } else { + out = NULL; + if (use_memory_writer) { + picture.writer = WebPMemoryWrite; + picture.custom_ptr = (void*)&memory_writer; + } + if (!quiet && !short_output) { + fprintf(stderr, "No output file specified (no -o flag). Encoding will\n"); + fprintf(stderr, "be performed, but its results discarded.\n\n"); + } + } + if (!quiet) { + picture.stats = &stats; + picture.user_data = (void*)in_file; + } + + // Crop & resize. + if (verbose) { + StopwatchReset(&stop_watch); + } + if (crop != 0) { + // We use self-cropping using a view. + if (!WebPPictureView(&picture, crop_x, crop_y, crop_w, crop_h, &picture)) { + fprintf(stderr, "Error! Cannot crop picture\n"); + goto Error; + } + } + if ((resize_w | resize_h) > 0) { + WebPPicture picture_no_alpha; + if (config.exact) { + // If -exact, we can't premultiply RGB by A otherwise RGB is lost if A=0. + // We rescale an opaque copy and assemble scaled A and non-premultiplied + // RGB channels. This is slower but it's a very uncommon use case. Color + // leak at sharp alpha edges is possible. + if (!WebPPictureCopy(&picture, &picture_no_alpha)) { + fprintf(stderr, "Error! Cannot copy temporary picture\n"); + goto Error; + } + + // We enforced picture.use_argb = 1 above. Now, remove the alpha values. + { + int x, y; + uint32_t* argb_no_alpha = picture_no_alpha.argb; + for (y = 0; y < picture_no_alpha.height; ++y) { + for (x = 0; x < picture_no_alpha.width; ++x) { + argb_no_alpha[x] |= 0xff000000; // Opaque copy. + } + argb_no_alpha += picture_no_alpha.argb_stride; + } + } + + if (!WebPPictureRescale(&picture_no_alpha, resize_w, resize_h)) { + fprintf(stderr, "Error! Cannot resize temporary picture\n"); + goto Error; + } + } + + if (!WebPPictureRescale(&picture, resize_w, resize_h)) { + fprintf(stderr, "Error! Cannot resize picture\n"); + goto Error; + } + + if (config.exact) { // Put back the alpha information. + int x, y; + uint32_t* argb_no_alpha = picture_no_alpha.argb; + uint32_t* argb = picture.argb; + for (y = 0; y < picture_no_alpha.height; ++y) { + for (x = 0; x < picture_no_alpha.width; ++x) { + argb[x] = (argb[x] & 0xff000000) | (argb_no_alpha[x] & 0x00ffffff); + } + argb_no_alpha += picture_no_alpha.argb_stride; + argb += picture.argb_stride; + } + WebPPictureFree(&picture_no_alpha); + } + } + if (verbose && (crop != 0 || (resize_w | resize_h) > 0)) { + const double preproc_time = StopwatchReadAndReset(&stop_watch); + fprintf(stderr, "Time to crop/resize picture: %.3fs\n", preproc_time); + } + + if (picture.extra_info_type > 0) { + AllocExtraInfo(&picture); + } + // Save original picture for later comparison. Only for lossy as lossless does + // not modify 'picture' (even near-lossless). + if (print_distortion >= 0 && !config.lossless && + !WebPPictureCopy(&picture, &original_picture)) { + fprintf(stderr, "Error! Cannot copy temporary picture\n"); + goto Error; + } + + // Compress. + if (verbose) { + StopwatchReset(&stop_watch); + } + if (!WebPEncode(&config, &picture)) { + fprintf(stderr, "Error! Cannot encode picture as WebP\n"); + fprintf(stderr, "Error code: %d (%s)\n", + picture.error_code, kErrorMessages[picture.error_code]); + goto Error; + } + if (verbose) { + const double encode_time = StopwatchReadAndReset(&stop_watch); + fprintf(stderr, "Time to encode picture: %.3fs\n", encode_time); + } + + // Get the decompressed image for the lossless pipeline. + if (!quiet && print_distortion >= 0 && config.lossless) { + if (config.near_lossless == 100) { + // Pure lossless: image was not modified, make 'original_picture' a view + // of 'picture' by copying all members except the freeable pointers. + original_picture = picture; + original_picture.memory_ = original_picture.memory_argb_ = NULL; + } else { + // Decode the bitstream stored in 'memory_writer' to get the altered image + // to 'picture'; save the 'original_picture' beforehand. + assert(use_memory_writer); + original_picture = picture; + if (!WebPPictureInit(&picture)) { // Do not free 'picture'. + fprintf(stderr, "Error! Version mismatch!\n"); + goto Error; + } + + picture.use_argb = 1; + if (!ReadWebP(memory_writer.mem, memory_writer.size, &picture, + /*keep_alpha=*/WebPPictureHasTransparency(&picture), + /*metadata=*/NULL)) { + fprintf(stderr, "Error! Cannot decode encoded WebP bitstream\n"); + fprintf(stderr, "Error code: %d (%s)\n", picture.error_code, + kErrorMessages[picture.error_code]); + goto Error; + } + picture.stats = original_picture.stats; + } + original_picture.stats = NULL; + } + + // Write the YUV planes to a PGM file. Only available for lossy. + if (dump_file) { + if (picture.use_argb) { + fprintf(stderr, "Warning: can't dump file (-d option) " + "in lossless mode.\n"); + } else if (!DumpPicture(&picture, dump_file)) { + WFPRINTF(stderr, "Warning, couldn't dump picture %s\n", + (const W_CHAR*)dump_file); + } + } + + if (use_memory_writer && out != NULL && + !WriteWebPWithMetadata(out, &picture, &memory_writer, &metadata, + keep_metadata, &metadata_written)) { + fprintf(stderr, "Error writing WebP file!\n"); + goto Error; + } + + if (out == NULL && keep_metadata) { + // output is disabled, just display the metadata stats. + const struct { + const MetadataPayload* const payload; + int flag; + } *iter, info[] = {{&metadata.exif, METADATA_EXIF}, + {&metadata.iccp, METADATA_ICC}, + {&metadata.xmp, METADATA_XMP}, + {NULL, 0}}; + uint32_t unused1 = 0; + uint64_t unused2 = 0; + + for (iter = info; iter->payload != NULL; ++iter) { + if (UpdateFlagsAndSize(iter->payload, !!(keep_metadata & iter->flag), + /*flag=*/0, &unused1, &unused2)) { + metadata_written |= iter->flag; + } + } + } + + if (!quiet) { + if (!short_output || print_distortion < 0) { + if (config.lossless) { + PrintExtraInfoLossless(&picture, short_output, in_file); + } else { + PrintExtraInfoLossy(&picture, short_output, config.low_memory, in_file); + } + } + if (!short_output && picture.extra_info_type > 0) { + PrintMapInfo(&picture); + } + if (print_distortion >= 0) { // print distortion + static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" }; + float values[5]; + if (!WebPPictureDistortion(&picture, &original_picture, + print_distortion, values)) { + fprintf(stderr, "Error while computing the distortion.\n"); + goto Error; + } + if (!short_output) { + fprintf(stderr, "%s: ", distortion_names[print_distortion]); + fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f Total:%.2f\n", + values[0], values[1], values[2], values[3], values[4]); + } else { + fprintf(stderr, "%7d %.4f\n", picture.stats->coded_size, values[4]); + } + } + if (!short_output) { + PrintMetadataInfo(&metadata, metadata_written); + } + } + return_value = 0; + + Error: + WebPMemoryWriterClear(&memory_writer); + WebPFree(picture.extra_info); + MetadataFree(&metadata); + WebPPictureFree(&picture); + WebPPictureFree(&original_picture); + if (out != NULL && out != stdout) { + fclose(out); + } + + FREE_WARGV_AND_RETURN(return_value); +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/examples/dwebp.c b/internal/libwebp-1.2.1/examples/dwebp.c new file mode 100644 index 0000000..5cfb0fb --- /dev/null +++ b/internal/libwebp-1.2.1/examples/dwebp.c @@ -0,0 +1,421 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Command-line tool for decoding a WebP image. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include "../examples/example_util.h" +#include "../imageio/image_enc.h" +#include "../imageio/webpdec.h" +#include "./stopwatch.h" +#include "./unicode.h" + +static int verbose = 0; +static int quiet = 0; +#ifndef WEBP_DLL +#ifdef __cplusplus +extern "C" { +#endif + +extern void* VP8GetCPUInfo; // opaque forward declaration. + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // WEBP_DLL + + +static int SaveOutput(const WebPDecBuffer* const buffer, + WebPOutputFileFormat format, const char* const out_file) { + const int use_stdout = (out_file != NULL) && !WSTRCMP(out_file, "-"); + int ok = 1; + Stopwatch stop_watch; + + if (verbose) { + StopwatchReset(&stop_watch); + } + ok = WebPSaveImage(buffer, format, out_file); + + if (ok) { + if (!quiet) { + if (use_stdout) { + fprintf(stderr, "Saved to stdout\n"); + } else { + WFPRINTF(stderr, "Saved file %s\n", (const W_CHAR*)out_file); + } + } + if (verbose) { + const double write_time = StopwatchReadAndReset(&stop_watch); + fprintf(stderr, "Time to write output: %.3fs\n", write_time); + } + } else { + if (use_stdout) { + fprintf(stderr, "Error writing to stdout !!\n"); + } else { + WFPRINTF(stderr, "Error writing file %s !!\n", (const W_CHAR*)out_file); + } + } + return ok; +} + +static void Help(void) { + printf("Usage: dwebp in_file [options] [-o out_file]\n\n" + "Decodes the WebP image file to PNG format [Default].\n" + "Note: Animated WebP files are not supported.\n\n" + "Use following options to convert into alternate image formats:\n" + " -pam ......... save the raw RGBA samples as a color PAM\n" + " -ppm ......... save the raw RGB samples as a color PPM\n" + " -bmp ......... save as uncompressed BMP format\n" + " -tiff ........ save as uncompressed TIFF format\n" + " -pgm ......... save the raw YUV samples as a grayscale PGM\n" + " file with IMC4 layout\n" + " -yuv ......... save the raw YUV samples in flat layout\n" + "\n" + " Other options are:\n" + " -version ..... print version number and exit\n" + " -nofancy ..... don't use the fancy YUV420 upscaler\n" + " -nofilter .... disable in-loop filtering\n" + " -nodither .... disable dithering\n" + " -dither .. dithering strength (in 0..100)\n" + " -alpha_dither use alpha-plane dithering if needed\n" + " -mt .......... use multi-threading\n" + " -crop ... crop output with the given rectangle\n" + " -resize ......... scale the output (*after* any cropping)\n" + " -flip ........ flip the output vertically\n" + " -alpha ....... only save the alpha plane\n" + " -incremental . use incremental decoding (useful for tests)\n" + " -h ........... this help message\n" + " -v ........... verbose (e.g. print encoding/decoding times)\n" + " -quiet ....... quiet mode, don't print anything\n" +#ifndef WEBP_DLL + " -noasm ....... disable all assembly optimizations\n" +#endif + ); +} + +static const char* const kFormatType[] = { + "unspecified", "lossy", "lossless" +}; + +static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config, + WebPOutputFileFormat format, + int use_external_memory) { + uint8_t* external_buffer = NULL; + WebPDecBuffer* const output_buffer = &config->output; + int w = config->input.width; + int h = config->input.height; + if (config->options.use_scaling) { + w = config->options.scaled_width; + h = config->options.scaled_height; + } else if (config->options.use_cropping) { + w = config->options.crop_width; + h = config->options.crop_height; + } + if (format >= RGB && format <= rgbA_4444) { + const int bpp = (format == RGB || format == BGR) ? 3 + : (format == RGBA_4444 || format == rgbA_4444 || + format == RGB_565) ? 2 + : 4; + uint32_t stride = bpp * w + 7; // <- just for exercising + external_buffer = (uint8_t*)WebPMalloc(stride * h); + if (external_buffer == NULL) return NULL; + output_buffer->u.RGBA.stride = stride; + output_buffer->u.RGBA.size = stride * h; + output_buffer->u.RGBA.rgba = external_buffer; + } else { // YUV and YUVA + const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace); + uint8_t* tmp; + uint32_t stride = w + 3; + uint32_t uv_stride = (w + 1) / 2 + 13; + uint32_t total_size = stride * h * (has_alpha ? 2 : 1) + + 2 * uv_stride * (h + 1) / 2; + assert(format >= YUV && format <= YUVA); + external_buffer = (uint8_t*)WebPMalloc(total_size); + if (external_buffer == NULL) return NULL; + tmp = external_buffer; + output_buffer->u.YUVA.y = tmp; + output_buffer->u.YUVA.y_stride = stride; + output_buffer->u.YUVA.y_size = stride * h; + tmp += output_buffer->u.YUVA.y_size; + if (has_alpha) { + output_buffer->u.YUVA.a = tmp; + output_buffer->u.YUVA.a_stride = stride; + output_buffer->u.YUVA.a_size = stride * h; + tmp += output_buffer->u.YUVA.a_size; + } else { + output_buffer->u.YUVA.a = NULL; + output_buffer->u.YUVA.a_stride = 0; + } + output_buffer->u.YUVA.u = tmp; + output_buffer->u.YUVA.u_stride = uv_stride; + output_buffer->u.YUVA.u_size = uv_stride * (h + 1) / 2; + tmp += output_buffer->u.YUVA.u_size; + + output_buffer->u.YUVA.v = tmp; + output_buffer->u.YUVA.v_stride = uv_stride; + output_buffer->u.YUVA.v_size = uv_stride * (h + 1) / 2; + tmp += output_buffer->u.YUVA.v_size; + assert(tmp <= external_buffer + total_size); + } + output_buffer->is_external_memory = use_external_memory; + return external_buffer; +} + +int main(int argc, const char* argv[]) { + int ok = 0; + const char* in_file = NULL; + const char* out_file = NULL; + + WebPDecoderConfig config; + WebPDecBuffer* const output_buffer = &config.output; + WebPBitstreamFeatures* const bitstream = &config.input; + WebPOutputFileFormat format = PNG; + uint8_t* external_buffer = NULL; + int use_external_memory = 0; + const uint8_t* data = NULL; + + int incremental = 0; + int c; + + INIT_WARGV(argc, argv); + + if (!WebPInitDecoderConfig(&config)) { + fprintf(stderr, "Library version mismatch!\n"); + FREE_WARGV_AND_RETURN(-1); + } + + for (c = 1; c < argc; ++c) { + int parse_error = 0; + if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { + Help(); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-o") && c < argc - 1) { + out_file = (const char*)GET_WARGV(argv, ++c); + } else if (!strcmp(argv[c], "-alpha")) { + format = ALPHA_PLANE_ONLY; + } else if (!strcmp(argv[c], "-nofancy")) { + config.options.no_fancy_upsampling = 1; + } else if (!strcmp(argv[c], "-nofilter")) { + config.options.bypass_filtering = 1; + } else if (!strcmp(argv[c], "-pam")) { + format = PAM; + } else if (!strcmp(argv[c], "-ppm")) { + format = PPM; + } else if (!strcmp(argv[c], "-bmp")) { + format = BMP; + } else if (!strcmp(argv[c], "-tiff")) { + format = TIFF; + } else if (!strcmp(argv[c], "-quiet")) { + quiet = 1; + } else if (!strcmp(argv[c], "-version")) { + const int version = WebPGetDecoderVersion(); + printf("%d.%d.%d\n", + (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-pgm")) { + format = PGM; + } else if (!strcmp(argv[c], "-yuv")) { + format = RAW_YUV; + } else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) { + const char* const fmt = argv[++c]; + if (!strcmp(fmt, "RGB")) format = RGB; + else if (!strcmp(fmt, "RGBA")) format = RGBA; + else if (!strcmp(fmt, "BGR")) format = BGR; + else if (!strcmp(fmt, "BGRA")) format = BGRA; + else if (!strcmp(fmt, "ARGB")) format = ARGB; + else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444; + else if (!strcmp(fmt, "RGB_565")) format = RGB_565; + else if (!strcmp(fmt, "rgbA")) format = rgbA; + else if (!strcmp(fmt, "bgrA")) format = bgrA; + else if (!strcmp(fmt, "Argb")) format = Argb; + else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444; + else if (!strcmp(fmt, "YUV")) format = YUV; + else if (!strcmp(fmt, "YUVA")) format = YUVA; + else { + fprintf(stderr, "Can't parse pixel_format %s\n", fmt); + parse_error = 1; + } + } else if (!strcmp(argv[c], "-external_memory") && c < argc - 1) { + use_external_memory = ExUtilGetInt(argv[++c], 0, &parse_error); + parse_error |= (use_external_memory > 2 || use_external_memory < 0); + if (parse_error) { + fprintf(stderr, "Can't parse 'external_memory' value %s\n", argv[c]); + } + } else if (!strcmp(argv[c], "-mt")) { + config.options.use_threads = 1; + } else if (!strcmp(argv[c], "-alpha_dither")) { + config.options.alpha_dithering_strength = 100; + } else if (!strcmp(argv[c], "-nodither")) { + config.options.dithering_strength = 0; + } else if (!strcmp(argv[c], "-dither") && c < argc - 1) { + config.options.dithering_strength = + ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-crop") && c < argc - 4) { + config.options.use_cropping = 1; + config.options.crop_left = ExUtilGetInt(argv[++c], 0, &parse_error); + config.options.crop_top = ExUtilGetInt(argv[++c], 0, &parse_error); + config.options.crop_width = ExUtilGetInt(argv[++c], 0, &parse_error); + config.options.crop_height = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if ((!strcmp(argv[c], "-scale") || !strcmp(argv[c], "-resize")) && + c < argc - 2) { // '-scale' is left for compatibility + config.options.use_scaling = 1; + config.options.scaled_width = ExUtilGetInt(argv[++c], 0, &parse_error); + config.options.scaled_height = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-flip")) { + config.options.flip = 1; + } else if (!strcmp(argv[c], "-v")) { + verbose = 1; +#ifndef WEBP_DLL + } else if (!strcmp(argv[c], "-noasm")) { + VP8GetCPUInfo = NULL; +#endif + } else if (!strcmp(argv[c], "-incremental")) { + incremental = 1; + } else if (!strcmp(argv[c], "--")) { + if (c < argc - 1) in_file = (const char*)GET_WARGV(argv, ++c); + break; + } else if (argv[c][0] == '-') { + fprintf(stderr, "Unknown option '%s'\n", argv[c]); + Help(); + FREE_WARGV_AND_RETURN(-1); + } else { + in_file = (const char*)GET_WARGV(argv, c); + } + + if (parse_error) { + Help(); + FREE_WARGV_AND_RETURN(-1); + } + } + + if (in_file == NULL) { + fprintf(stderr, "missing input file!!\n"); + Help(); + FREE_WARGV_AND_RETURN(-1); + } + + if (quiet) verbose = 0; + + { + VP8StatusCode status = VP8_STATUS_OK; + size_t data_size = 0; + if (!LoadWebP(in_file, &data, &data_size, bitstream)) { + FREE_WARGV_AND_RETURN(-1); + } + + switch (format) { + case PNG: +#ifdef HAVE_WINCODEC_H + output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; +#else + output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB; +#endif + break; + case PAM: + output_buffer->colorspace = MODE_RGBA; + break; + case PPM: + output_buffer->colorspace = MODE_RGB; // drops alpha for PPM + break; + case BMP: + output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; + break; + case TIFF: + output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB; + break; + case PGM: + case RAW_YUV: + output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; + break; + case ALPHA_PLANE_ONLY: + output_buffer->colorspace = MODE_YUVA; + break; + // forced modes: + case RGB: output_buffer->colorspace = MODE_RGB; break; + case RGBA: output_buffer->colorspace = MODE_RGBA; break; + case BGR: output_buffer->colorspace = MODE_BGR; break; + case BGRA: output_buffer->colorspace = MODE_BGRA; break; + case ARGB: output_buffer->colorspace = MODE_ARGB; break; + case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break; + case RGB_565: output_buffer->colorspace = MODE_RGB_565; break; + case rgbA: output_buffer->colorspace = MODE_rgbA; break; + case bgrA: output_buffer->colorspace = MODE_bgrA; break; + case Argb: output_buffer->colorspace = MODE_Argb; break; + case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break; + case YUV: output_buffer->colorspace = MODE_YUV; break; + case YUVA: output_buffer->colorspace = MODE_YUVA; break; + default: goto Exit; + } + + if (use_external_memory > 0 && format >= RGB) { + external_buffer = AllocateExternalBuffer(&config, format, + use_external_memory); + if (external_buffer == NULL) goto Exit; + } + + { + Stopwatch stop_watch; + if (verbose) StopwatchReset(&stop_watch); + + if (incremental) { + status = DecodeWebPIncremental(data, data_size, &config); + } else { + status = DecodeWebP(data, data_size, &config); + } + if (verbose) { + const double decode_time = StopwatchReadAndReset(&stop_watch); + fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time); + } + } + + ok = (status == VP8_STATUS_OK); + if (!ok) { + PrintWebPError(in_file, status); + goto Exit; + } + } + + if (out_file != NULL) { + if (!quiet) { + WFPRINTF(stderr, "Decoded %s.", (const W_CHAR*)in_file); + fprintf(stderr, " Dimensions: %d x %d %s. Format: %s. Now saving...\n", + output_buffer->width, output_buffer->height, + bitstream->has_alpha ? " (with alpha)" : "", + kFormatType[bitstream->format]); + } + ok = SaveOutput(output_buffer, format, out_file); + } else { + if (!quiet) { + WFPRINTF(stderr, "File %s can be decoded ", (const W_CHAR*)in_file); + fprintf(stderr, "(dimensions: %d x %d %s. Format: %s).\n", + output_buffer->width, output_buffer->height, + bitstream->has_alpha ? " (with alpha)" : "", + kFormatType[bitstream->format]); + fprintf(stderr, "Nothing written; " + "use -o flag to save the result as e.g. PNG.\n"); + } + } + Exit: + WebPFreeDecBuffer(output_buffer); + WebPFree((void*)external_buffer); + WebPFree((void*)data); + FREE_WARGV_AND_RETURN(ok ? 0 : -1); +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/examples/example_util.c b/internal/libwebp-1.2.1/examples/example_util.c new file mode 100644 index 0000000..b043654 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/example_util.c @@ -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. +// ----------------------------------------------------------------------------- +// +// Utility functions used by the example programs. +// + +#include "./example_util.h" + +#include +#include +#include +#include + +#include "webp/mux_types.h" +#include "../imageio/imageio_util.h" + +//------------------------------------------------------------------------------ +// String parsing + +uint32_t ExUtilGetUInt(const char* const v, int base, int* const error) { + char* end = NULL; + const uint32_t n = (v != NULL) ? (uint32_t)strtoul(v, &end, base) : 0u; + if (end == v && error != NULL && !*error) { + *error = 1; + fprintf(stderr, "Error! '%s' is not an integer.\n", + (v != NULL) ? v : "(null)"); + } + return n; +} + +int ExUtilGetInt(const char* const v, int base, int* const error) { + return (int)ExUtilGetUInt(v, base, error); +} + +int ExUtilGetInts(const char* v, int base, int max_output, int output[]) { + int n, error = 0; + for (n = 0; v != NULL && n < max_output; ++n) { + const int value = ExUtilGetInt(v, base, &error); + if (error) return -1; + output[n] = value; + v = strchr(v, ','); + if (v != NULL) ++v; // skip over the trailing ',' + } + return n; +} + +float ExUtilGetFloat(const char* const v, int* const error) { + char* end = NULL; + const float f = (v != NULL) ? (float)strtod(v, &end) : 0.f; + if (end == v && error != NULL && !*error) { + *error = 1; + fprintf(stderr, "Error! '%s' is not a floating point number.\n", + (v != NULL) ? v : "(null)"); + } + return f; +} + +//------------------------------------------------------------------------------ + +static void ResetCommandLineArguments(int argc, const char* argv[], + CommandLineArguments* const args) { + assert(args != NULL); + args->argc_ = argc; + args->argv_ = argv; + args->own_argv_ = 0; + WebPDataInit(&args->argv_data_); +} + +void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args) { + if (args != NULL) { + if (args->own_argv_) { + WebPFree((void*)args->argv_); + WebPDataClear(&args->argv_data_); + } + ResetCommandLineArguments(0, NULL, args); + } +} + +#define MAX_ARGC 16384 +int ExUtilInitCommandLineArguments(int argc, const char* argv[], + CommandLineArguments* const args) { + if (args == NULL || argv == NULL) return 0; + ResetCommandLineArguments(argc, argv, args); + if (argc == 1 && argv[0][0] != '-') { + char* cur; + const char sep[] = " \t\r\n\f\v"; + +#if defined(_WIN32) && defined(_UNICODE) + fprintf(stderr, + "Error: Reading arguments from a file is a feature unavailable " + "with Unicode binaries.\n"); + return 0; +#endif + + if (!ExUtilReadFileToWebPData(argv[0], &args->argv_data_)) { + return 0; + } + args->own_argv_ = 1; + args->argv_ = (const char**)WebPMalloc(MAX_ARGC * sizeof(*args->argv_)); + if (args->argv_ == NULL) return 0; + + argc = 0; + for (cur = strtok((char*)args->argv_data_.bytes, sep); + cur != NULL; + cur = strtok(NULL, sep)) { + if (argc == MAX_ARGC) { + fprintf(stderr, "ERROR: Arguments limit %d reached\n", MAX_ARGC); + return 0; + } + assert(strlen(cur) != 0); + args->argv_[argc++] = cur; + } + args->argc_ = argc; + } + return 1; +} + +//------------------------------------------------------------------------------ + +int ExUtilReadFileToWebPData(const char* const filename, + WebPData* const webp_data) { + const uint8_t* data; + size_t size; + if (webp_data == NULL) return 0; + if (!ImgIoUtilReadFile(filename, &data, &size)) return 0; + webp_data->bytes = data; + webp_data->size = size; + return 1; +} diff --git a/internal/libwebp-1.2.1/examples/example_util.h b/internal/libwebp-1.2.1/examples/example_util.h new file mode 100644 index 0000000..fe762a4 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/example_util.h @@ -0,0 +1,70 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Utility functions used by the example programs. +// + +#ifndef WEBP_EXAMPLES_EXAMPLE_UTIL_H_ +#define WEBP_EXAMPLES_EXAMPLE_UTIL_H_ + +#include "webp/types.h" +#include "webp/mux_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// String parsing + +// Parses 'v' using strto(ul|l|d)(). If error is non-NULL, '*error' is set to +// true on failure while on success it is left unmodified to allow chaining of +// calls. An error is only printed on the first occurrence. +uint32_t ExUtilGetUInt(const char* const v, int base, int* const error); +int ExUtilGetInt(const char* const v, int base, int* const error); +float ExUtilGetFloat(const char* const v, int* const error); + +// This variant of ExUtilGetInt() will parse multiple integers from a +// comma-separated list. Up to 'max_output' integers are parsed. +// The result is placed in the output[] array, and the number of integers +// actually parsed is returned, or -1 if an error occurred. +int ExUtilGetInts(const char* v, int base, int max_output, int output[]); + +// Reads a file named 'filename' into a WebPData structure. The content of +// webp_data is overwritten. Returns false in case of error. +int ExUtilReadFileToWebPData(const char* const filename, + WebPData* const webp_data); + +//------------------------------------------------------------------------------ +// Command-line arguments + +typedef struct { + int argc_; + const char** argv_; + WebPData argv_data_; + int own_argv_; +} CommandLineArguments; + +// Initializes the structure from the command-line parameters. If there is +// only one parameter and it does not start with a '-', then it is assumed to +// be a file name. This file will be read and tokenized into command-line +// arguments. The content of 'args' is overwritten. +// Returns false in case of error (memory allocation failure, non +// existing file, too many arguments, ...). +int ExUtilInitCommandLineArguments(int argc, const char* argv[], + CommandLineArguments* const args); + +// Deallocate all memory and reset 'args'. +void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_EXAMPLES_EXAMPLE_UTIL_H_ diff --git a/internal/libwebp-1.2.1/examples/gif2webp.c b/internal/libwebp-1.2.1/examples/gif2webp.c new file mode 100644 index 0000000..78d84c6 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/gif2webp.c @@ -0,0 +1,606 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// simple tool to convert animated GIFs to WebP +// +// Authors: Skal (pascal.massimino@gmail.com) +// Urvang (urvang@google.com) + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#ifdef WEBP_HAVE_GIF + +#if defined(HAVE_UNISTD_H) && HAVE_UNISTD_H +#include +#endif + +#include +#include "webp/encode.h" +#include "webp/mux.h" +#include "../examples/example_util.h" +#include "../imageio/imageio_util.h" +#include "./gifdec.h" +#include "./unicode.h" +#include "./unicode_gif.h" + +#if !defined(STDIN_FILENO) +#define STDIN_FILENO 0 +#endif + +//------------------------------------------------------------------------------ + +static int transparent_index = GIF_INDEX_INVALID; // Opaque by default. + +static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = { + "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA", + "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA" +}; + +static const char* ErrorString(WebPMuxError err) { + assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA); + return kErrorMessages[-err]; +} + +enum { + METADATA_ICC = (1 << 0), + METADATA_XMP = (1 << 1), + METADATA_ALL = METADATA_ICC | METADATA_XMP +}; + +//------------------------------------------------------------------------------ + +static void Help(void) { + printf("Usage:\n"); + printf(" gif2webp [options] gif_file -o webp_file\n"); + printf("Options:\n"); + printf(" -h / -help ............. this help\n"); + printf(" -lossy ................. encode image using lossy compression\n"); + printf(" -mixed ................. for each frame in the image, pick lossy\n" + " or lossless compression heuristically\n"); + printf(" -q ............. quality factor (0:small..100:big)\n"); + printf(" -m ............... compression method (0=fast, 6=slowest)\n"); + printf(" -min_size .............. minimize output size (default:off)\n" + " lossless compression by default; can be\n" + " combined with -q, -m, -lossy or -mixed\n" + " options\n"); + printf(" -kmin ............ min distance between key frames\n"); + printf(" -kmax ............ max distance between key frames\n"); + printf(" -f ............... filter strength (0=off..100)\n"); + printf(" -metadata ..... comma separated list of metadata to\n"); + printf(" "); + printf("copy from the input to the output if present\n"); + printf(" "); + printf("Valid values: all, none, icc, xmp (default)\n"); + printf(" -loop_compatibility .... use compatibility mode for Chrome\n"); + printf(" version prior to M62 (inclusive)\n"); + printf(" -mt .................... use multi-threading if available\n"); + printf("\n"); + printf(" -version ............... print version number and exit\n"); + printf(" -v ..................... verbose\n"); + printf(" -quiet ................. don't print anything\n"); + printf("\n"); +} + +//------------------------------------------------------------------------------ + +int main(int argc, const char* argv[]) { + int verbose = 0; + int gif_error = GIF_ERROR; + WebPMuxError err = WEBP_MUX_OK; + int ok = 0; + const W_CHAR* in_file = NULL, *out_file = NULL; + GifFileType* gif = NULL; + int frame_duration = 0; + int frame_timestamp = 0; + GIFDisposeMethod orig_dispose = GIF_DISPOSE_NONE; + + WebPPicture frame; // Frame rectangle only (not disposed). + WebPPicture curr_canvas; // Not disposed. + WebPPicture prev_canvas; // Disposed. + + WebPAnimEncoder* enc = NULL; + WebPAnimEncoderOptions enc_options; + WebPConfig config; + + int frame_number = 0; // Whether we are processing the first frame. + int done; + int c; + int quiet = 0; + WebPData webp_data; + + int keep_metadata = METADATA_XMP; // ICC not output by default. + WebPData icc_data; + int stored_icc = 0; // Whether we have already stored an ICC profile. + WebPData xmp_data; + int stored_xmp = 0; // Whether we have already stored an XMP profile. + int loop_count = 0; // default: infinite + int stored_loop_count = 0; // Whether we have found an explicit loop count. + int loop_compatibility = 0; + WebPMux* mux = NULL; + + int default_kmin = 1; // Whether to use default kmin value. + int default_kmax = 1; + + INIT_WARGV(argc, argv); + + if (!WebPConfigInit(&config) || !WebPAnimEncoderOptionsInit(&enc_options) || + !WebPPictureInit(&frame) || !WebPPictureInit(&curr_canvas) || + !WebPPictureInit(&prev_canvas)) { + fprintf(stderr, "Error! Version mismatch!\n"); + FREE_WARGV_AND_RETURN(-1); + } + config.lossless = 1; // Use lossless compression by default. + + WebPDataInit(&webp_data); + WebPDataInit(&icc_data); + WebPDataInit(&xmp_data); + + if (argc == 1) { + Help(); + FREE_WARGV_AND_RETURN(0); + } + + for (c = 1; c < argc; ++c) { + int parse_error = 0; + if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { + Help(); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-o") && c < argc - 1) { + out_file = GET_WARGV(argv, ++c); + } else if (!strcmp(argv[c], "-lossy")) { + config.lossless = 0; + } else if (!strcmp(argv[c], "-mixed")) { + enc_options.allow_mixed = 1; + config.lossless = 0; + } else if (!strcmp(argv[c], "-loop_compatibility")) { + loop_compatibility = 1; + } else if (!strcmp(argv[c], "-q") && c < argc - 1) { + config.quality = ExUtilGetFloat(argv[++c], &parse_error); + } else if (!strcmp(argv[c], "-m") && c < argc - 1) { + config.method = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-min_size")) { + enc_options.minimize_size = 1; + } else if (!strcmp(argv[c], "-kmax") && c < argc - 1) { + enc_options.kmax = ExUtilGetInt(argv[++c], 0, &parse_error); + default_kmax = 0; + } else if (!strcmp(argv[c], "-kmin") && c < argc - 1) { + enc_options.kmin = ExUtilGetInt(argv[++c], 0, &parse_error); + default_kmin = 0; + } else if (!strcmp(argv[c], "-f") && c < argc - 1) { + config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-metadata") && c < argc - 1) { + static const struct { + const char* option; + int flag; + } kTokens[] = { + { "all", METADATA_ALL }, + { "none", 0 }, + { "icc", METADATA_ICC }, + { "xmp", METADATA_XMP }, + }; + const size_t kNumTokens = sizeof(kTokens) / sizeof(*kTokens); + const char* start = argv[++c]; + const char* const end = start + strlen(start); + + keep_metadata = 0; + while (start < end) { + size_t i; + const char* token = strchr(start, ','); + if (token == NULL) token = end; + + for (i = 0; i < kNumTokens; ++i) { + if ((size_t)(token - start) == strlen(kTokens[i].option) && + !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) { + if (kTokens[i].flag != 0) { + keep_metadata |= kTokens[i].flag; + } else { + keep_metadata = 0; + } + break; + } + } + if (i == kNumTokens) { + fprintf(stderr, "Error! Unknown metadata type '%.*s'\n", + (int)(token - start), start); + Help(); + FREE_WARGV_AND_RETURN(-1); + } + start = token + 1; + } + } else if (!strcmp(argv[c], "-mt")) { + ++config.thread_level; + } else if (!strcmp(argv[c], "-version")) { + const int enc_version = WebPGetEncoderVersion(); + const int mux_version = WebPGetMuxVersion(); + printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n", + (enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff, + enc_version & 0xff, (mux_version >> 16) & 0xff, + (mux_version >> 8) & 0xff, mux_version & 0xff); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-quiet")) { + quiet = 1; + enc_options.verbose = 0; + } else if (!strcmp(argv[c], "-v")) { + verbose = 1; + enc_options.verbose = 1; + } else if (!strcmp(argv[c], "--")) { + if (c < argc - 1) in_file = GET_WARGV(argv, ++c); + break; + } else if (argv[c][0] == '-') { + fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]); + Help(); + FREE_WARGV_AND_RETURN(-1); + } else { + in_file = GET_WARGV(argv, c); + } + + if (parse_error) { + Help(); + FREE_WARGV_AND_RETURN(-1); + } + } + + // Appropriate default kmin, kmax values for lossy and lossless. + if (default_kmin) { + enc_options.kmin = config.lossless ? 9 : 3; + } + if (default_kmax) { + enc_options.kmax = config.lossless ? 17 : 5; + } + + if (!WebPValidateConfig(&config)) { + fprintf(stderr, "Error! Invalid configuration.\n"); + goto End; + } + + if (in_file == NULL) { + fprintf(stderr, "No input file specified!\n"); + Help(); + goto End; + } + + // Start the decoder object + gif = DGifOpenFileUnicode(in_file, &gif_error); + if (gif == NULL) goto End; + + // Loop over GIF images + done = 0; + do { + GifRecordType type; + if (DGifGetRecordType(gif, &type) == GIF_ERROR) goto End; + + switch (type) { + case IMAGE_DESC_RECORD_TYPE: { + GIFFrameRect gif_rect; + GifImageDesc* const image_desc = &gif->Image; + + if (!DGifGetImageDesc(gif)) goto End; + + if (frame_number == 0) { + if (verbose) { + printf("Canvas screen: %d x %d\n", gif->SWidth, gif->SHeight); + } + // Fix some broken GIF global headers that report + // 0 x 0 screen dimension. + if (gif->SWidth == 0 || gif->SHeight == 0) { + image_desc->Left = 0; + image_desc->Top = 0; + gif->SWidth = image_desc->Width; + gif->SHeight = image_desc->Height; + if (gif->SWidth <= 0 || gif->SHeight <= 0) { + goto End; + } + if (verbose) { + printf("Fixed canvas screen dimension to: %d x %d\n", + gif->SWidth, gif->SHeight); + } + } + // Allocate current buffer. + frame.width = gif->SWidth; + frame.height = gif->SHeight; + frame.use_argb = 1; + if (!WebPPictureAlloc(&frame)) goto End; + GIFClearPic(&frame, NULL); + WebPPictureCopy(&frame, &curr_canvas); + WebPPictureCopy(&frame, &prev_canvas); + + // Background color. + GIFGetBackgroundColor(gif->SColorMap, gif->SBackGroundColor, + transparent_index, + &enc_options.anim_params.bgcolor); + + // Initialize encoder. + enc = WebPAnimEncoderNew(curr_canvas.width, curr_canvas.height, + &enc_options); + if (enc == NULL) { + fprintf(stderr, + "Error! Could not create encoder object. Possibly due to " + "a memory error.\n"); + goto End; + } + } + + // Some even more broken GIF can have sub-rect with zero width/height. + if (image_desc->Width == 0 || image_desc->Height == 0) { + image_desc->Width = gif->SWidth; + image_desc->Height = gif->SHeight; + } + + if (!GIFReadFrame(gif, transparent_index, &gif_rect, &frame)) { + goto End; + } + // Blend frame rectangle with previous canvas to compose full canvas. + // Note that 'curr_canvas' is same as 'prev_canvas' at this point. + GIFBlendFrames(&frame, &gif_rect, &curr_canvas); + + if (!WebPAnimEncoderAdd(enc, &curr_canvas, frame_timestamp, &config)) { + fprintf(stderr, "Error while adding frame #%d: %s\n", frame_number, + WebPAnimEncoderGetError(enc)); + goto End; + } else { + ++frame_number; + } + + // Update canvases. + GIFDisposeFrame(orig_dispose, &gif_rect, &prev_canvas, &curr_canvas); + GIFCopyPixels(&curr_canvas, &prev_canvas); + + // Force frames with a small or no duration to 100ms to be consistent + // with web browsers and other transcoding tools. This also avoids + // incorrect durations between frames when padding frames are + // discarded. + if (frame_duration <= 10) { + frame_duration = 100; + } + + // Update timestamp (for next frame). + frame_timestamp += frame_duration; + + // In GIF, graphic control extensions are optional for a frame, so we + // may not get one before reading the next frame. To handle this case, + // we reset frame properties to reasonable defaults for the next frame. + orig_dispose = GIF_DISPOSE_NONE; + frame_duration = 0; + transparent_index = GIF_INDEX_INVALID; + break; + } + case EXTENSION_RECORD_TYPE: { + int extension; + GifByteType* data = NULL; + if (DGifGetExtension(gif, &extension, &data) == GIF_ERROR) { + goto End; + } + if (data == NULL) continue; + + switch (extension) { + case COMMENT_EXT_FUNC_CODE: { + break; // Do nothing for now. + } + case GRAPHICS_EXT_FUNC_CODE: { + if (!GIFReadGraphicsExtension(data, &frame_duration, &orig_dispose, + &transparent_index)) { + goto End; + } + break; + } + case PLAINTEXT_EXT_FUNC_CODE: { + break; + } + case APPLICATION_EXT_FUNC_CODE: { + if (data[0] != 11) break; // Chunk is too short + if (!memcmp(data + 1, "NETSCAPE2.0", 11) || + !memcmp(data + 1, "ANIMEXTS1.0", 11)) { + if (!GIFReadLoopCount(gif, &data, &loop_count)) { + goto End; + } + if (verbose) { + fprintf(stderr, "Loop count: %d\n", loop_count); + } + stored_loop_count = loop_compatibility ? (loop_count != 0) : 1; + } else { // An extension containing metadata. + // We only store the first encountered chunk of each type, and + // only if requested by the user. + const int is_xmp = (keep_metadata & METADATA_XMP) && + !stored_xmp && + !memcmp(data + 1, "XMP DataXMP", 11); + const int is_icc = (keep_metadata & METADATA_ICC) && + !stored_icc && + !memcmp(data + 1, "ICCRGBG1012", 11); + if (is_xmp || is_icc) { + if (!GIFReadMetadata(gif, &data, + is_xmp ? &xmp_data : &icc_data)) { + goto End; + } + if (is_icc) { + stored_icc = 1; + } else if (is_xmp) { + stored_xmp = 1; + } + } + } + break; + } + default: { + break; // skip + } + } + while (data != NULL) { + if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End; + } + break; + } + case TERMINATE_RECORD_TYPE: { + done = 1; + break; + } + default: { + if (verbose) { + fprintf(stderr, "Skipping over unknown record type %d\n", type); + } + break; + } + } + } while (!done); + + // Last NULL frame. + if (!WebPAnimEncoderAdd(enc, NULL, frame_timestamp, NULL)) { + fprintf(stderr, "Error flushing WebP muxer.\n"); + fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc)); + } + + if (!WebPAnimEncoderAssemble(enc, &webp_data)) { + fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc)); + goto End; + } + // If there's only one frame, we don't need to handle loop count. + if (frame_number == 1) { + loop_count = 0; + } else if (!loop_compatibility) { + if (!stored_loop_count) { + // if no loop-count element is seen, the default is '1' (loop-once) + // and we need to signal it explicitly in WebP. Note however that + // in case there's a single frame, we still don't need to store it. + if (frame_number > 1) { + stored_loop_count = 1; + loop_count = 1; + } + } else if (loop_count > 0 && loop_count < 65535) { + // adapt GIF's semantic to WebP's (except in the infinite-loop case) + loop_count += 1; + } + } + // loop_count of 0 is the default (infinite), so no need to signal it + if (loop_count == 0) stored_loop_count = 0; + + if (stored_loop_count || stored_icc || stored_xmp) { + // Re-mux to add loop count and/or metadata as needed. + mux = WebPMuxCreate(&webp_data, 1); + if (mux == NULL) { + fprintf(stderr, "ERROR: Could not re-mux to add loop count/metadata.\n"); + goto End; + } + WebPDataClear(&webp_data); + + if (stored_loop_count) { // Update loop count. + WebPMuxAnimParams new_params; + err = WebPMuxGetAnimationParams(mux, &new_params); + if (err != WEBP_MUX_OK) { + fprintf(stderr, "ERROR (%s): Could not fetch loop count.\n", + ErrorString(err)); + goto End; + } + new_params.loop_count = loop_count; + err = WebPMuxSetAnimationParams(mux, &new_params); + if (err != WEBP_MUX_OK) { + fprintf(stderr, "ERROR (%s): Could not update loop count.\n", + ErrorString(err)); + goto End; + } + } + + if (stored_icc) { // Add ICCP chunk. + err = WebPMuxSetChunk(mux, "ICCP", &icc_data, 1); + if (verbose) { + fprintf(stderr, "ICC size: %d\n", (int)icc_data.size); + } + if (err != WEBP_MUX_OK) { + fprintf(stderr, "ERROR (%s): Could not set ICC chunk.\n", + ErrorString(err)); + goto End; + } + } + + if (stored_xmp) { // Add XMP chunk. + err = WebPMuxSetChunk(mux, "XMP ", &xmp_data, 1); + if (verbose) { + fprintf(stderr, "XMP size: %d\n", (int)xmp_data.size); + } + if (err != WEBP_MUX_OK) { + fprintf(stderr, "ERROR (%s): Could not set XMP chunk.\n", + ErrorString(err)); + goto End; + } + } + + err = WebPMuxAssemble(mux, &webp_data); + if (err != WEBP_MUX_OK) { + fprintf(stderr, "ERROR (%s): Could not assemble when re-muxing to add " + "loop count/metadata.\n", ErrorString(err)); + goto End; + } + } + + if (out_file != NULL) { + if (!ImgIoUtilWriteFile((const char*)out_file, webp_data.bytes, + webp_data.size)) { + WFPRINTF(stderr, "Error writing output file: %s\n", out_file); + goto End; + } + if (!quiet) { + if (!WSTRCMP(out_file, "-")) { + fprintf(stderr, "Saved %d bytes to STDIO\n", + (int)webp_data.size); + } else { + WFPRINTF(stderr, "Saved output file (%d bytes): %s\n", + (int)webp_data.size, out_file); + } + } + } else { + if (!quiet) { + fprintf(stderr, "Nothing written; use -o flag to save the result " + "(%d bytes).\n", (int)webp_data.size); + } + } + + // All OK. + ok = 1; + gif_error = GIF_OK; + + End: + WebPDataClear(&icc_data); + WebPDataClear(&xmp_data); + WebPMuxDelete(mux); + WebPDataClear(&webp_data); + WebPPictureFree(&frame); + WebPPictureFree(&curr_canvas); + WebPPictureFree(&prev_canvas); + WebPAnimEncoderDelete(enc); + + if (gif_error != GIF_OK) { + GIFDisplayError(gif, gif_error); + } + if (gif != NULL) { +#if LOCAL_GIF_PREREQ(5,1) + DGifCloseFile(gif, &gif_error); +#else + DGifCloseFile(gif); +#endif + } + + FREE_WARGV_AND_RETURN(!ok); +} + +#else // !WEBP_HAVE_GIF + +int main(int argc, const char* argv[]) { + fprintf(stderr, "GIF support not enabled in %s.\n", argv[0]); + (void)argc; + return 0; +} + +#endif + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/examples/gifdec.c b/internal/libwebp-1.2.1/examples/gifdec.c new file mode 100644 index 0000000..99ee996 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/gifdec.c @@ -0,0 +1,416 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// GIF decode. + +#include "./gifdec.h" + +#include + +#ifdef WEBP_HAVE_GIF +#include +#include +#include + +#include "webp/encode.h" +#include "webp/mux_types.h" + +#define GIF_TRANSPARENT_COLOR 0x00000000u +#define GIF_WHITE_COLOR 0xffffffffu +#define GIF_TRANSPARENT_MASK 0x01 +#define GIF_DISPOSE_MASK 0x07 +#define GIF_DISPOSE_SHIFT 2 + +// from utils/utils.h +#ifdef __cplusplus +extern "C" { +#endif +extern void WebPCopyPlane(const uint8_t* src, int src_stride, + uint8_t* dst, int dst_stride, + int width, int height); +extern void WebPCopyPixels(const WebPPicture* const src, + WebPPicture* const dst); +#ifdef __cplusplus +} +#endif + +void GIFGetBackgroundColor(const ColorMapObject* const color_map, + int bgcolor_index, int transparent_index, + uint32_t* const bgcolor) { + if (transparent_index != GIF_INDEX_INVALID && + bgcolor_index == transparent_index) { + *bgcolor = GIF_TRANSPARENT_COLOR; // Special case. + } else if (color_map == NULL || color_map->Colors == NULL + || bgcolor_index >= color_map->ColorCount) { + *bgcolor = GIF_WHITE_COLOR; + fprintf(stderr, + "GIF decode warning: invalid background color index. Assuming " + "white background.\n"); + } else { + const GifColorType color = color_map->Colors[bgcolor_index]; + *bgcolor = (0xffu << 24) + | (color.Red << 16) + | (color.Green << 8) + | (color.Blue << 0); + } +} + +int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration, + GIFDisposeMethod* const dispose, + int* const transparent_index) { + const int flags = buf[1]; + const int dispose_raw = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK; + const int duration_raw = buf[2] | (buf[3] << 8); // In 10 ms units. + if (buf[0] != 4) return 0; + *duration = duration_raw * 10; // Duration is in 1 ms units. + switch (dispose_raw) { + case 3: + *dispose = GIF_DISPOSE_RESTORE_PREVIOUS; + break; + case 2: + *dispose = GIF_DISPOSE_BACKGROUND; + break; + case 1: + case 0: + default: + *dispose = GIF_DISPOSE_NONE; + break; + } + *transparent_index = + (flags & GIF_TRANSPARENT_MASK) ? buf[4] : GIF_INDEX_INVALID; + return 1; +} + +static int Remap(const GifFileType* const gif, const uint8_t* const src, + int len, int transparent_index, uint32_t* dst) { + int i; + const GifColorType* colors; + const ColorMapObject* const cmap = + gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap; + if (cmap == NULL) return 1; + if (cmap->Colors == NULL || cmap->ColorCount <= 0) return 0; + colors = cmap->Colors; + + for (i = 0; i < len; ++i) { + if (src[i] == transparent_index) { + dst[i] = GIF_TRANSPARENT_COLOR; + } else if (src[i] < cmap->ColorCount) { + const GifColorType c = colors[src[i]]; + dst[i] = c.Blue | (c.Green << 8) | (c.Red << 16) | (0xffu << 24); + } else { + return 0; + } + } + return 1; +} + +int GIFReadFrame(GifFileType* const gif, int transparent_index, + GIFFrameRect* const gif_rect, WebPPicture* const picture) { + WebPPicture sub_image; + const GifImageDesc* const image_desc = &gif->Image; + uint32_t* dst = NULL; + uint8_t* tmp = NULL; + const GIFFrameRect rect = { + image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height + }; + const uint64_t memory_needed = 4 * rect.width * (uint64_t)rect.height; + int ok = 0; + *gif_rect = rect; + + if (memory_needed != (size_t)memory_needed || memory_needed > (4ULL << 32)) { + fprintf(stderr, "Image is too large (%d x %d).", rect.width, rect.height); + return 0; + } + + // Use a view for the sub-picture: + if (!WebPPictureView(picture, rect.x_offset, rect.y_offset, + rect.width, rect.height, &sub_image)) { + fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n", + rect.width, rect.height, rect.x_offset, rect.y_offset); + return 0; + } + dst = sub_image.argb; + + tmp = (uint8_t*)WebPMalloc(rect.width * sizeof(*tmp)); + if (tmp == NULL) goto End; + + if (image_desc->Interlace) { // Interlaced image. + // We need 4 passes, with the following offsets and jumps. + const int interlace_offsets[] = { 0, 4, 2, 1 }; + const int interlace_jumps[] = { 8, 8, 4, 2 }; + int pass; + for (pass = 0; pass < 4; ++pass) { + const size_t stride = (size_t)sub_image.argb_stride; + int y = interlace_offsets[pass]; + uint32_t* row = dst + y * stride; + const size_t jump = interlace_jumps[pass] * stride; + for (; y < rect.height; y += interlace_jumps[pass], row += jump) { + if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End; + if (!Remap(gif, tmp, rect.width, transparent_index, row)) goto End; + } + } + } else { // Non-interlaced image. + int y; + uint32_t* ptr = dst; + for (y = 0; y < rect.height; ++y, ptr += sub_image.argb_stride) { + if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End; + if (!Remap(gif, tmp, rect.width, transparent_index, ptr)) goto End; + } + } + ok = 1; + + End: + if (!ok) picture->error_code = sub_image.error_code; + WebPPictureFree(&sub_image); + WebPFree(tmp); + return ok; +} + +int GIFReadLoopCount(GifFileType* const gif, GifByteType** const buf, + int* const loop_count) { + assert(!memcmp(*buf + 1, "NETSCAPE2.0", 11) || + !memcmp(*buf + 1, "ANIMEXTS1.0", 11)); + if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) { + return 0; + } + if (*buf == NULL) { + return 0; // Loop count sub-block missing. + } + if ((*buf)[0] < 3 || (*buf)[1] != 1) { + return 0; // wrong size/marker + } + *loop_count = (*buf)[2] | ((*buf)[3] << 8); + return 1; +} + +int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf, + WebPData* const metadata) { + const int is_xmp = !memcmp(*buf + 1, "XMP DataXMP", 11); + const int is_icc = !memcmp(*buf + 1, "ICCRGBG1012", 11); + assert(is_xmp || is_icc); + (void)is_icc; // silence unused warning. + // Construct metadata from sub-blocks. + // Usual case (including ICC profile): In each sub-block, the + // first byte specifies its size in bytes (0 to 255) and the + // rest of the bytes contain the data. + // Special case for XMP data: In each sub-block, the first byte + // is also part of the XMP payload. XMP in GIF also has a 257 + // byte padding data. See the XMP specification for details. + while (1) { + WebPData subblock; + const uint8_t* tmp; + if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) { + return 0; + } + if (*buf == NULL) break; // Finished. + subblock.size = is_xmp ? (*buf)[0] + 1 : (*buf)[0]; + assert(subblock.size > 0); + subblock.bytes = is_xmp ? *buf : *buf + 1; + // Note: We store returned value in 'tmp' first, to avoid + // leaking old memory in metadata->bytes on error. + tmp = (uint8_t*)realloc((void*)metadata->bytes, + metadata->size + subblock.size); + if (tmp == NULL) { + return 0; + } + memcpy((void*)(tmp + metadata->size), + subblock.bytes, subblock.size); + metadata->bytes = tmp; + metadata->size += subblock.size; + } + if (is_xmp) { + // XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00. + const size_t xmp_pading_size = 257; + if (metadata->size > xmp_pading_size) { + metadata->size -= xmp_pading_size; + } + } + return 1; +} + +static void ClearRectangle(WebPPicture* const picture, + int left, int top, int width, int height) { + int i, j; + const size_t stride = picture->argb_stride; + uint32_t* dst = picture->argb + top * stride + left; + for (j = 0; j < height; ++j, dst += stride) { + for (i = 0; i < width; ++i) dst[i] = GIF_TRANSPARENT_COLOR; + } +} + +void GIFClearPic(WebPPicture* const pic, const GIFFrameRect* const rect) { + if (rect != NULL) { + ClearRectangle(pic, rect->x_offset, rect->y_offset, + rect->width, rect->height); + } else { + ClearRectangle(pic, 0, 0, pic->width, pic->height); + } +} + +void GIFCopyPixels(const WebPPicture* const src, WebPPicture* const dst) { + WebPCopyPixels(src, dst); +} + +void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect, + const WebPPicture* const prev_canvas, + WebPPicture* const curr_canvas) { + assert(rect != NULL); + if (dispose == GIF_DISPOSE_BACKGROUND) { + GIFClearPic(curr_canvas, rect); + } else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) { + const size_t src_stride = prev_canvas->argb_stride; + const uint32_t* const src = prev_canvas->argb + rect->x_offset + + rect->y_offset * src_stride; + const size_t dst_stride = curr_canvas->argb_stride; + uint32_t* const dst = curr_canvas->argb + rect->x_offset + + rect->y_offset * dst_stride; + assert(prev_canvas != NULL); + WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride), + (uint8_t*)dst, (int)(4 * dst_stride), + 4 * rect->width, rect->height); + } +} + +void GIFBlendFrames(const WebPPicture* const src, + const GIFFrameRect* const rect, WebPPicture* const dst) { + int i, j; + const size_t src_stride = src->argb_stride; + const size_t dst_stride = dst->argb_stride; + assert(src->width == dst->width && src->height == dst->height); + for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) { + for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) { + const uint32_t src_pixel = src->argb[j * src_stride + i]; + const int src_alpha = src_pixel >> 24; + if (src_alpha != 0) { + dst->argb[j * dst_stride + i] = src_pixel; + } + } + } +} + +void GIFDisplayError(const GifFileType* const gif, int gif_error) { + // libgif 4.2.0 has retired PrintGifError() and added GifErrorString(). +#if LOCAL_GIF_PREREQ(4,2) +#if LOCAL_GIF_PREREQ(5,0) + // Static string actually, hence the const char* cast. + const char* error_str = (const char*)GifErrorString( + (gif == NULL) ? gif_error : gif->Error); +#else + const char* error_str = (const char*)GifErrorString(); + (void)gif; +#endif + if (error_str == NULL) error_str = "Unknown error"; + fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str); +#else + (void)gif; + fprintf(stderr, "GIFLib Error %d: ", gif_error); + PrintGifError(); + fprintf(stderr, "\n"); +#endif +} + +#else // !WEBP_HAVE_GIF + +static void ErrorGIFNotAvailable() { + fprintf(stderr, "GIF support not compiled. Please install the libgif-dev " + "package before building.\n"); +} + +void GIFGetBackgroundColor(const struct ColorMapObject* const color_map, + int bgcolor_index, int transparent_index, + uint32_t* const bgcolor) { + (void)color_map; + (void)bgcolor_index; + (void)transparent_index; + (void)bgcolor; + ErrorGIFNotAvailable(); +} + +int GIFReadGraphicsExtension(const GifByteType* const data, int* const duration, + GIFDisposeMethod* const dispose, + int* const transparent_index) { + (void)data; + (void)duration; + (void)dispose; + (void)transparent_index; + ErrorGIFNotAvailable(); + return 0; +} + +int GIFReadFrame(struct GifFileType* const gif, int transparent_index, + GIFFrameRect* const gif_rect, + struct WebPPicture* const picture) { + (void)gif; + (void)transparent_index; + (void)gif_rect; + (void)picture; + ErrorGIFNotAvailable(); + return 0; +} + +int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf, + int* const loop_count) { + (void)gif; + (void)buf; + (void)loop_count; + ErrorGIFNotAvailable(); + return 0; +} + +int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf, + struct WebPData* const metadata) { + (void)gif; + (void)buf; + (void)metadata; + ErrorGIFNotAvailable(); + return 0; +} + +void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect, + const struct WebPPicture* const prev_canvas, + struct WebPPicture* const curr_canvas) { + (void)dispose; + (void)rect; + (void)prev_canvas; + (void)curr_canvas; + ErrorGIFNotAvailable(); +} + +void GIFBlendFrames(const struct WebPPicture* const src, + const GIFFrameRect* const rect, + struct WebPPicture* const dst) { + (void)src; + (void)rect; + (void)dst; + ErrorGIFNotAvailable(); +} + +void GIFDisplayError(const struct GifFileType* const gif, int gif_error) { + (void)gif; + (void)gif_error; + ErrorGIFNotAvailable(); +} + +void GIFClearPic(struct WebPPicture* const pic, + const GIFFrameRect* const rect) { + (void)pic; + (void)rect; + ErrorGIFNotAvailable(); +} + +void GIFCopyPixels(const struct WebPPicture* const src, + struct WebPPicture* const dst) { + (void)src; + (void)dst; + ErrorGIFNotAvailable(); +} + +#endif // WEBP_HAVE_GIF + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/examples/gifdec.h b/internal/libwebp-1.2.1/examples/gifdec.h new file mode 100644 index 0000000..5eba9dd --- /dev/null +++ b/internal/libwebp-1.2.1/examples/gifdec.h @@ -0,0 +1,116 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// GIF decode. + +#ifndef WEBP_EXAMPLES_GIFDEC_H_ +#define WEBP_EXAMPLES_GIFDEC_H_ + +#include +#include "webp/types.h" + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#ifdef WEBP_HAVE_GIF +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// GIFLIB_MAJOR is only defined in libgif >= 4.2.0. +#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) +# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR) +# define LOCAL_GIF_PREREQ(maj, min) \ + (LOCAL_GIF_VERSION >= (((maj) << 8) | (min))) +#else +# define LOCAL_GIF_VERSION 0 +# define LOCAL_GIF_PREREQ(maj, min) 0 +#endif + +#define GIF_INDEX_INVALID (-1) + +typedef enum GIFDisposeMethod { + GIF_DISPOSE_NONE, + GIF_DISPOSE_BACKGROUND, + GIF_DISPOSE_RESTORE_PREVIOUS +} GIFDisposeMethod; + +typedef struct { + int x_offset, y_offset, width, height; +} GIFFrameRect; + +struct WebPData; +struct WebPPicture; + +#ifndef WEBP_HAVE_GIF +struct ColorMapObject; +struct GifFileType; +typedef unsigned char GifByteType; +#endif + +// Given the index of background color and transparent color, returns the +// corresponding background color (in BGRA format) in 'bgcolor'. +void GIFGetBackgroundColor(const struct ColorMapObject* const color_map, + int bgcolor_index, int transparent_index, + uint32_t* const bgcolor); + +// Parses the given graphics extension data to get frame duration (in 1ms +// units), dispose method and transparent color index. +// Returns true on success. +int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration, + GIFDisposeMethod* const dispose, + int* const transparent_index); + +// Reads the next GIF frame from 'gif' into 'picture'. Also, returns the GIF +// frame dimensions and offsets in 'rect'. +// Returns true on success. +int GIFReadFrame(struct GifFileType* const gif, int transparent_index, + GIFFrameRect* const gif_rect, + struct WebPPicture* const picture); + +// Parses loop count from the given Netscape extension data. +int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf, + int* const loop_count); + +// Parses the given ICC or XMP extension data and stores it into 'metadata'. +// Returns true on success. +int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf, + struct WebPData* const metadata); + +// Dispose the pixels within 'rect' of 'curr_canvas' based on 'dispose' method +// and 'prev_canvas'. +void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect, + const struct WebPPicture* const prev_canvas, + struct WebPPicture* const curr_canvas); + +// Given 'src' picture and its frame rectangle 'rect', blend it into 'dst'. +void GIFBlendFrames(const struct WebPPicture* const src, + const GIFFrameRect* const rect, + struct WebPPicture* const dst); + +// Prints an error string based on 'gif_error'. +void GIFDisplayError(const struct GifFileType* const gif, int gif_error); + +// In the given 'pic', clear the pixels in 'rect' to transparent color. +void GIFClearPic(struct WebPPicture* const pic, const GIFFrameRect* const rect); + +// Copy pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are assumed +// to be already allocated. +void GIFCopyPixels(const struct WebPPicture* const src, + struct WebPPicture* const dst); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_EXAMPLES_GIFDEC_H_ diff --git a/internal/libwebp-1.2.1/examples/img2webp.c b/internal/libwebp-1.2.1/examples/img2webp.c new file mode 100644 index 0000000..3f1e59b --- /dev/null +++ b/internal/libwebp-1.2.1/examples/img2webp.c @@ -0,0 +1,324 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// generate an animated WebP out of a sequence of images +// (PNG, JPEG, ...) +// +// Example usage: +// img2webp -o out.webp -q 40 -mixed -duration 40 input??.png +// +// Author: skal@google.com (Pascal Massimino) + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include "../examples/example_util.h" +#include "../imageio/image_dec.h" +#include "../imageio/imageio_util.h" +#include "./stopwatch.h" +#include "./unicode.h" +#include "webp/encode.h" +#include "webp/mux.h" + +//------------------------------------------------------------------------------ + +static void Help(void) { + printf("Usage:\n\n"); + printf(" img2webp [file-level options] [image files...] " + "[per-frame options...]\n"); + printf("\n"); + + printf("File-level options (only used at the start of compression):\n"); + printf(" -min_size ............ minimize size\n"); + printf(" -loop .......... loop count (default: 0, = infinite loop)\n"); + printf(" -kmax .......... maximum number of frame between key-frames\n" + " (0=only keyframes)\n"); + printf(" -kmin .......... minimum number of frame between key-frames\n" + " (0=disable key-frames altogether)\n"); + printf(" -mixed ............... use mixed lossy/lossless automatic mode\n"); + printf(" -v ................... verbose mode\n"); + printf(" -h ................... this help\n"); + printf(" -version ............. print version number and exit\n"); + printf("\n"); + + printf("Per-frame options (only used for subsequent images input):\n"); + printf(" -d ............. frame duration in ms (default: 100)\n"); + printf(" -lossless ........... use lossless mode (default)\n"); + printf(" -lossy ... ........... use lossy mode\n"); + printf(" -q ........... quality\n"); + printf(" -m ............. method to use\n"); + + printf("\n"); + printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n" + " -d 80 in2.tiff -o out.webp\n"); + printf("\nNote: if a single file name is passed as the argument, the " + "arguments will be\n"); + printf("tokenized from this file. The file name must not start with " + "the character '-'.\n"); +} + +//------------------------------------------------------------------------------ + +static int ReadImage(const char filename[], WebPPicture* const pic) { + const uint8_t* data = NULL; + size_t data_size = 0; + WebPImageReader reader; + int ok; +#ifdef HAVE_WINCODEC_H + // Try to decode the file using WIC falling back to the other readers for + // e.g., WebP. + ok = ReadPictureWithWIC(filename, pic, 1, NULL); + if (ok) return 1; +#endif + if (!ImgIoUtilReadFile(filename, &data, &data_size)) return 0; + reader = WebPGuessImageReader(data, data_size); + ok = reader(data, data_size, pic, 1, NULL); + WebPFree((void*)data); + return ok; +} + +static int SetLoopCount(int loop_count, WebPData* const webp_data) { + int ok = 1; + WebPMuxError err; + uint32_t features; + WebPMuxAnimParams new_params; + WebPMux* const mux = WebPMuxCreate(webp_data, 1); + if (mux == NULL) return 0; + + err = WebPMuxGetFeatures(mux, &features); + ok = (err == WEBP_MUX_OK); + if (!ok || !(features & ANIMATION_FLAG)) goto End; + + err = WebPMuxGetAnimationParams(mux, &new_params); + ok = (err == WEBP_MUX_OK); + if (ok) { + new_params.loop_count = loop_count; + err = WebPMuxSetAnimationParams(mux, &new_params); + ok = (err == WEBP_MUX_OK); + } + if (ok) { + WebPDataClear(webp_data); + err = WebPMuxAssemble(mux, webp_data); + ok = (err == WEBP_MUX_OK); + } + + End: + WebPMuxDelete(mux); + if (!ok) { + fprintf(stderr, "Error during loop-count setting\n"); + } + return ok; +} + +//------------------------------------------------------------------------------ + +int main(int argc, const char* argv[]) { + const char* output = NULL; + WebPAnimEncoder* enc = NULL; + int verbose = 0; + int pic_num = 0; + int duration = 100; + int timestamp_ms = 0; + int loop_count = 0; + int width = 0, height = 0; + WebPAnimEncoderOptions anim_config; + WebPConfig config; + WebPPicture pic; + WebPData webp_data; + int c; + int have_input = 0; + CommandLineArguments cmd_args; + int ok; + + INIT_WARGV(argc, argv); + + ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args); + if (!ok) FREE_WARGV_AND_RETURN(1); + + argc = cmd_args.argc_; + argv = cmd_args.argv_; + + WebPDataInit(&webp_data); + if (!WebPAnimEncoderOptionsInit(&anim_config) || + !WebPConfigInit(&config) || + !WebPPictureInit(&pic)) { + fprintf(stderr, "Library version mismatch!\n"); + ok = 0; + goto End; + } + + // 1st pass of option parsing + for (c = 0; ok && c < argc; ++c) { + if (argv[c][0] == '-') { + int parse_error = 0; + if (!strcmp(argv[c], "-o") && c + 1 < argc) { + argv[c] = NULL; + output = (const char*)GET_WARGV_SHIFTED(argv, ++c); + } else if (!strcmp(argv[c], "-kmin") && c + 1 < argc) { + argv[c] = NULL; + anim_config.kmin = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-kmax") && c + 1 < argc) { + argv[c] = NULL; + anim_config.kmax = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-loop") && c + 1 < argc) { + argv[c] = NULL; + loop_count = ExUtilGetInt(argv[++c], 0, &parse_error); + if (loop_count < 0) { + fprintf(stderr, "Invalid non-positive loop-count (%d)\n", loop_count); + parse_error = 1; + } + } else if (!strcmp(argv[c], "-min_size")) { + anim_config.minimize_size = 1; + } else if (!strcmp(argv[c], "-mixed")) { + anim_config.allow_mixed = 1; + config.lossless = 0; + } else if (!strcmp(argv[c], "-v")) { + verbose = 1; + } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { + Help(); + goto End; + } else if (!strcmp(argv[c], "-version")) { + const int enc_version = WebPGetEncoderVersion(); + const int mux_version = WebPGetMuxVersion(); + printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n", + (enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff, + enc_version & 0xff, (mux_version >> 16) & 0xff, + (mux_version >> 8) & 0xff, mux_version & 0xff); + goto End; + } else { + continue; + } + ok = !parse_error; + if (!ok) goto End; + argv[c] = NULL; // mark option as 'parsed' during 1st pass + } else { + have_input |= 1; + } + } + if (!have_input) { + fprintf(stderr, "No input file(s) for generating animation!\n"); + goto End; + } + + // image-reading pass + pic_num = 0; + config.lossless = 1; + for (c = 0; ok && c < argc; ++c) { + if (argv[c] == NULL) continue; + if (argv[c][0] == '-') { // parse local options + int parse_error = 0; + if (!strcmp(argv[c], "-lossy")) { + if (!anim_config.allow_mixed) config.lossless = 0; + } else if (!strcmp(argv[c], "-lossless")) { + if (!anim_config.allow_mixed) config.lossless = 1; + } else if (!strcmp(argv[c], "-q") && c + 1 < argc) { + config.quality = ExUtilGetFloat(argv[++c], &parse_error); + } else if (!strcmp(argv[c], "-m") && c + 1 < argc) { + config.method = ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-d") && c + 1 < argc) { + duration = ExUtilGetInt(argv[++c], 0, &parse_error); + if (duration <= 0) { + fprintf(stderr, "Invalid negative duration (%d)\n", duration); + parse_error = 1; + } + } else { + parse_error = 1; // shouldn't be here. + fprintf(stderr, "Unknown option [%s]\n", argv[c]); + } + ok = !parse_error; + if (!ok) goto End; + continue; + } + + if (ok) { + ok = WebPValidateConfig(&config); + if (!ok) { + fprintf(stderr, "Invalid configuration.\n"); + goto End; + } + } + + // read next input image + pic.use_argb = 1; + ok = ReadImage((const char*)GET_WARGV_SHIFTED(argv, c), &pic); + if (!ok) goto End; + + if (enc == NULL) { + width = pic.width; + height = pic.height; + enc = WebPAnimEncoderNew(width, height, &anim_config); + ok = (enc != NULL); + if (!ok) { + fprintf(stderr, "Could not create WebPAnimEncoder object.\n"); + } + } + + if (ok) { + ok = (width == pic.width && height == pic.height); + if (!ok) { + fprintf(stderr, "Frame #%d dimension mismatched! " + "Got %d x %d. Was expecting %d x %d.\n", + pic_num, pic.width, pic.height, width, height); + } + } + + if (ok) { + ok = WebPAnimEncoderAdd(enc, &pic, timestamp_ms, &config); + if (!ok) { + fprintf(stderr, "Error while adding frame #%d\n", pic_num); + } + } + WebPPictureFree(&pic); + if (!ok) goto End; + + if (verbose) { + WFPRINTF(stderr, "Added frame #%3d at time %4d (file: %s)\n", + pic_num, timestamp_ms, GET_WARGV_SHIFTED(argv, c)); + } + timestamp_ms += duration; + ++pic_num; + } + + // add a last fake frame to signal the last duration + ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL); + ok = ok && WebPAnimEncoderAssemble(enc, &webp_data); + if (!ok) { + fprintf(stderr, "Error during final animation assembly.\n"); + } + + End: + // free resources + WebPAnimEncoderDelete(enc); + + if (ok && loop_count > 0) { // Re-mux to add loop count. + ok = SetLoopCount(loop_count, &webp_data); + } + + if (ok) { + if (output != NULL) { + ok = ImgIoUtilWriteFile(output, webp_data.bytes, webp_data.size); + if (ok) WFPRINTF(stderr, "output file: %s ", (const W_CHAR*)output); + } else { + fprintf(stderr, "[no output file specified] "); + } + } + + if (ok) { + fprintf(stderr, "[%d frames, %u bytes].\n", + pic_num, (unsigned int)webp_data.size); + } + WebPDataClear(&webp_data); + ExUtilDeleteCommandLineArguments(&cmd_args); + FREE_WARGV_AND_RETURN(ok ? 0 : 1); +} diff --git a/internal/libwebp-1.2.1/examples/stopwatch.h b/internal/libwebp-1.2.1/examples/stopwatch.h new file mode 100644 index 0000000..f1b0fac --- /dev/null +++ b/internal/libwebp-1.2.1/examples/stopwatch.h @@ -0,0 +1,63 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Helper functions to measure elapsed time. +// +// Author: Mikolaj Zalewski (mikolajz@google.com) + +#ifndef WEBP_EXAMPLES_STOPWATCH_H_ +#define WEBP_EXAMPLES_STOPWATCH_H_ + +#include "webp/types.h" + +#if defined _WIN32 && !defined __GNUC__ +#include + +typedef LARGE_INTEGER Stopwatch; + +static WEBP_INLINE void StopwatchReset(Stopwatch* watch) { + QueryPerformanceCounter(watch); +} + +static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) { + const LARGE_INTEGER old_value = *watch; + LARGE_INTEGER freq; + if (!QueryPerformanceCounter(watch)) + return 0.0; + if (!QueryPerformanceFrequency(&freq)) + return 0.0; + if (freq.QuadPart == 0) + return 0.0; + return (watch->QuadPart - old_value.QuadPart) / (double)freq.QuadPart; +} + + +#else /* !_WIN32 */ +#include // memcpy +#include + +typedef struct timeval Stopwatch; + +static WEBP_INLINE void StopwatchReset(Stopwatch* watch) { + gettimeofday(watch, NULL); +} + +static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) { + struct timeval old_value; + double delta_sec, delta_usec; + memcpy(&old_value, watch, sizeof(old_value)); + gettimeofday(watch, NULL); + delta_sec = (double)watch->tv_sec - old_value.tv_sec; + delta_usec = (double)watch->tv_usec - old_value.tv_usec; + return delta_sec + delta_usec / 1000000.0; +} + +#endif /* _WIN32 */ + +#endif // WEBP_EXAMPLES_STOPWATCH_H_ diff --git a/internal/libwebp-1.2.1/examples/test.webp b/internal/libwebp-1.2.1/examples/test.webp new file mode 100644 index 0000000000000000000000000000000000000000..3e4bca1d8500ec465177ebac7c30d6cfde656c10 GIT binary patch literal 4880 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 Cu81!H literal 0 HcmV?d00001 diff --git a/internal/libwebp-1.2.1/examples/test_ref.ppm b/internal/libwebp-1.2.1/examples/test_ref.ppm new file mode 100644 index 0000000..97719f0 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/test_ref.ppm @@ -0,0 +1,4 @@ +P6 +128 128 +255 +ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎûÎûÎûÎûÏäŭÏäŭÏäŭÏäŭŭŭŭŭÒäŝÒäŝÒäŝÒäŝċŝċŝċŝċŝċŝċŝċŝċŝÒäŝÒäŝÒäŝÒäŝÓċ˙Óċ˙Óċ˙Óċ˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙ÔèŭÔèŭÔèŭÔèŭĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎûÎûÎûÎûÏäŭÏäŭÏäŭÏäŭÏäŭŭŭŭÒäŝÒäŝÒäŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÒäŝÒäŝÒäŝÒäŝÓċ˙Óċ˙Óċ˙Óċ˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙ÔèŭÔèŭÔèŭÔèŭĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎûÎûÎûÎûÏäŭÏäŭÏäŭÏäŭÏäûÏäûÏäûÏäûċŭċŭċŭċŭċŝċŝċŝċŝċŝċŝċŝċŝÒäŝÒäŝÒäŝÒäŝÓċ˙Óċ˙Óċ˙Óċ˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙ÔèŭÔèŭÔèŭÔèŭĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎûÎûÎûÎûÏäŭÏäŭÏäŭÏäŭÎċûÎċûÎċûÎċûÏĉŭÏĉŭÏĉŭÏĉŭċŝċŝċŝċŝċŝċŝċŝċŝÒäŝÒäŝÒäŝÒäŝÓċ˙Óċ˙Óċ˙Óċ˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙ÔèŭÔèŭÔèŭÔèŭĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷Ïċ÷ËáûËáûËáûËáûÌâŭÌâŭÌâŭÌâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎŝÏä˙Ïä˙Ïä˙Ïä˙Ïä˙Ïä˙Ïä˙Ïä˙ÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŝÎċûÎċùÎċùÎċùÏĉúÏĉúÏĉúÏĉŭċŝċŝċŝċŝċŝċŝċŝċŝÓċ˙Óċ˙Óċ˙Óċ˙Ôĉ˙Ôĉ˙Ôĉ˙Ôĉ˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙ĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÖê˙Öê˙Öê˙Öê˙ÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷ËáûËáûËáûËáûÌâŭÌâŭÌâŭÌâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎŝÏä˙Ïä˙Ïä˙Ïä˙Ïä˙Ïä˙Ïä˙Ïä˙ÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŭÒèûÒèûÒèûÒèûĉúĉúĉúĉúċŭċŝċŝċŝċŝċŝċŝċŝÓċ˙Óċ˙Óċ˙Óċ˙Ôĉ˙Ôĉ˙Ôĉ˙Ôĉ˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙ĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÖê˙Öê˙Öê˙Öê˙ÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷ËáûËáûËáûËáûÌâŭÌâŭÌâŭÌâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎŝÏä˙Ïä˙Ïä˙Ïä˙Ïä˙Ïä˙Ïä˙Ïä˙ÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŭĉúÒċĝÒċĝÒċĝÒċĝÒċĝÒċĝĉúċŭċŝċŝċŝċŝċŝċŝċŝÓċ˙Óċ˙Óċ˙Óċ˙Ôĉ˙Ôĉ˙Ôĉ˙Ôĉ˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙ĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÖê˙Öê˙Öê˙Öê˙ÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷ËáûËáûËáûËáûÌâŭÌâŭÌâŭÌâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎŝÎŝÎŝÎŝÏä˙Ïä˙Ïä˙Ïä˙Ïä˙˙˙˙ÏäŭÏäŭÏäŭŭÒäŝÒċŭÒċŭÒċŭÏáóÏáóÏáóÏáñÔċöÔċöÔċöÔċĝÒċúÒċŭÒċŭċŭċŭċŭċŭċŝÓċ˙Óċ˙Óċ˙Óċ˙Ôĉ˙Ôĉ˙Ôĉ˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ôè˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙Ġé˙ĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÖê˙Öê˙Öê˙Öê˙ÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷ÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎûÎûÎûÎûÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŝċŝċŝċŝċŝÒĉ˙ċŝÒäŝŭÎáúÒâûáúĠċ˙ÎáŭÔĉ˙Ôĉ˙ÓŭÏßĝÉÚ·Ĉ܇–މ—ݐ›­ħğΟŞğݳĊÌÖèÖáòÓáñÖèĝÔċö×éûÒċĝÒċúÖê˙ĉúÒĉŝÓĉŝÓĉŝÓĉŝÓĉŝÔè˙Ôè˙Ôè˙ÔèŭĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÖê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Ġë˙ĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭ×ëŝ×ëŝ×ëŝ×ëŝÖêŭÖêŭÖêŭÖêŭĠéûĠéûĠéûĠéûÔèúÔèúÔèúÔèúÓĉùÓĉùÓĉùÓĉùÒċĝÒċĝÒċĝÒċĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷ÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎûÎûÎûÎûÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÒäŝÒäŝÓŭ×è˙ĠŝÒßúĠä˙ÌÛĝÈÙòĞĊm{•dqˆhtŒlvŒs{…‹y‘‡Ÿ‘—¨¤Şş°ĥĈ½ĈĠÎÜêÓäóĠĉ÷ÔċöÖèúÓĉùäùÔêŝÓĉûÓĉûÓĉûÓĉûÔèŭÔèŭÔèŭÔèŭĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÖê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙ĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭ×ëŝ×ëŝ×ëŝ×ëŝÖêŭÖêŭÖêŭÖêŭĠéûĠéûĠéûĠéûÔèúÔèúÔèúÔèúÓĉùÓĉùÓĉùÓĉùÒċĝÒċĝÒċĝÒċĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷ÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎûÎûÎûÎûÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŝċŝċŝċŝċŝÌáùÎûÓċ˙Ġè˙ÒâûÓŭÔâŭÙĉ˙Óáûİ·Òlz•\j„mz”hqŒmw‚‹Ħ†‰ž‚–‡‹ž‚”°³Ċ¸½Ë•›İżËÙÓòÖĉö×ĉĝÔċöÖèúĠéûä÷ÓĉûÓĉûÓĉûÓĉûÔèŭÔèŭÔèŭÔèŭĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÖê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙ĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭ×ëŝ×ëŝ×ëŝ×ëŝÖêŭÖêŭÖêŭÖêŭĠéûĠéûĠéûĠéûÔèúÔèúÔèúÔèúÓĉùÓĉùÓĉùÓĉùÒċĝÒċĝÒċĝÒċĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷ÍâŭÍâŭÍâŭÍâŭÎŝÎŝÎŝÎŝÎûÎûÎûÎûÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŝċŝċŝċŝċŝÓè˙Òĉ˙ŭÏâûÓŭĠċ˙ħżÚŞr€›Wd~\i‚~‹¤˜˘½jq‡ŽŞz˜„…›œ²‚„š…†›‚…–—šİ²´ÁŻ³ż–ŸĴħ½ËĈÔäÔâòÙèùÔôÖèúĠĉùÓĉûÓĉûÓĉûÓĉûÔèŭÔèŭÔèŭÔèŭĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝĠéŝÖê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙ĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭĠëŭ×ëŝ×ëŝ×ëŝ×ëŝÖêŭÖêŭÖêŭÖêŭĠéûĠéûĠéûĠéûÔèúÔèúÔèúÔèúÓĉùÓĉùÓĉùÓĉùÒċĝÒċĝÒċĝÒċĝĉĝĉĝĉĝĉĝÏċ÷Ïċ÷Ïċ÷Ïċ÷ÎŝÎŝÎŝÎŝÏä˙Ïä˙Ïä˙Ïä˙ÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÓè˙ÉŜ÷Úì˙Ôĉ˙ÈÙòy‰£M[vXfVc}[deo‰ipŒ‡ŽŞfj‡ŒĴxz—}}˜~{•qo†‘—–݉ˆ˜——¤žĦ­ĈÌÚ½ĈĠ˜˘ħŻıËÖáò×ċ÷Úèù×ĉúÖĉŭÔèŭÔèŭÔèŭÔèŭÔèŭÔèŭÔèŭÖê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙ÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝ×í˙×í˙×í˙×í˙×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝÖêŭĠéûÔèúĠéûĠéûĠéûĠéûÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÎŝÎŝÎŝÎŝÏä˙Ïä˙Ïä˙Ïä˙ÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÒĉ˙ÒäŝĊ×ñfwM]wLZtMZsOXq_f€jq~žrv“rs“ˆ‰İ……¤ss‘vs}–ž²›šĴ€€šš¤¨²·ğNjž‘—¨ˆ‘Ħ­ĥÈÓŬïÚä÷ÛéúÖèúÔèŭÔèŭÔèŭÔèŭÔèŭÔèŭÔèŭÖê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙ÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝ×í˙×í˙×í˙×í˙×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝÖêŭĠéûÔèúĠéûĠéûĠéûĠéûÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÎŝÎŝÎŝÎŝÏä˙Ïä˙Ïä˙Ïä˙ÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÒĉ˙Ïäŭ½ÏéXk…IZsL\vP^yN[tbi‚sx‘Ž“Ĵ€‚Ÿ~€‚‚˘İ¨ËĦ}}›qq~—œ²œž­‚…‘ħ´ż¤¨²„¨Ĵş{€Ž‰Ÿ˜žŻ³ğÎ×ßò×ċ÷ÖèúÔèŭÔèŭÔèŭÔèŭÔèŭÔèŭÔèŭÖê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙ÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝ×í˙×í˙×í˙×í˙×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝÖêŭĠéûÔèúĠéûĠéûĠéûĠéûÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝÎŝÎŝÎŝÎŝÏä˙Ïä˙Ïä˙Ïä˙ÏäŭÏäŭÏäŭÏäŭċŝċŝċŝċŝċŝċŝċŝċŝċŝċŭċŭċŝċŝÍßù[m‡GWqLZtIWrFQmox“›Ÿ´Ħ¤¸‘•Şyzss¤¤Â½ıÙ²°Ì‰‹£lm„z{vx‰œžĞĞŻıÌÏڏ“›Œš›žİ˜›¨„‘•£Ş°ÀÔÚê×âó×ĉúÔèŭÔèŭÔèŭÔèŭÔèŭÔèŭÔèŭÖê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙Öê˙ÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝÖìŝ×í˙×í˙×í˙×í˙×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝÖêŭĠéûÔèúĠéûĠéûĠéûĠéûÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùÓĉùĉĝĉĝĉĝĉĝĉĝĉĝĉĝĉĝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÏċ˙Ïċ˙ÏĉŝÏĉŝÎúÏċùÖê˙ÒċŭşÍĉk~šJXvLWvLTqFNkQXtlqˆĈÈÚ£²áäﷷą„–£˘·ÎÍâŬÜïìëû¸¸Ċ£˘²yy†——£²²ğşşĈˆˆ‘•–œž¤­­·•˜£‚…‘ĦħĴħżĠŜíÖċ÷×éûÔèúÓĉùÔêŝÔêŝÔé˙ĠéŝÖê˙ÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÖêŭÖêŭÖêŭÖêŭ×ëŝ×ëŝ×ëŝ×ëŝ×í˙×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭÙíŭÙíŭÙíŭÙíŭ×ìû×ìû×ìû×ìû×íŭÖìûĠëúÔêùÔêùÔêùÔêùÔêùÓéĝÓéĝÓéĝÓéĝÒè÷Òè÷Òè÷Òè÷ĉöĉöĉöĉöĉöĉöĉöĉöċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÏċ˙Ïċ˙ÏĉŝċŭĠê˙äùÏĝĴÀĠVi‚IXvN\yHPpLSpHOlei†¨“•˘²²ğÚÛáÒÒۂ‘”“£şıÉÌÌÙ°°ğ——£ŒŒ˜yy†··À££ĴžžŞ‹›šĦŻ­´ŞŞ³ğğĊŞ­¸œŸŞšžŞ£İ·ÌÚêÒóÚëûÚí˙ĠéûÔêŝÔé˙ĠéŝÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÖêŭÖêŭÖêŭÖêŭ×ëŝ×ëŝ×ëŝ×ëŝ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìú×ïûÖíúĠìùÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöèôèôèôèôèôèôèôèôċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÏĉŝÏĉŝċŝċŭäûäû½ÍSc{FVpIWtS^}MUtLSpHOllp“•­ˆ‹šĦݎ‘—‰•jly…‡–wy†“Ÿikxjlysv…ˆ‹šÉÍׅˆ“xz‡œĤŻİ¨ħšš£­­·²ĥÀ•˜£ĤĞĥ‡Ž˜Ĥ²ÀÈÖĉ×è÷×éùÙêŭĠéŝĠéŝĠéŝÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÖêŭÖêŭÖêŭÖêŭ×ëŝ×ëŝ×ëŝ×ëŝ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìú×ïûÖíúĠìùÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöèôèôèôèôèôèôèôèôċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÏĉŝċŭċŭÒċŭÒċŭ£³ÌTd}IWqIWrQ]{P\zOWwLTqFMjcf„z–il~x{†y}…z~ˆ]_o~€¨´kmzikxehw^artwˆŽ~€‡‰˜sv…iit£˘ĞÂÂ̕•ž‰—}€‹‘–Ħ†‹•—žİÌĠâÖâŬëûÜëŭ×éûĠéûĠéŝÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÙêŭÖêŭÖêŭÖêŭÖêŭ×ëŝ×ëŝ×ëŝ×ëŝ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìú×ïûÖíúĠìùÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöèôèôèôèôèôèôèôèôċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÓè˙ÎúĠé˙ÈÜóqšIZrJXrIWrO[yJVtPXxT\{FNkV]zv}˜osX\pqv„fkw‚‡“fj{ˆŒ†‘sx„†‹˜y}Žhkei}X]kty‡‡‹œcewbbo†„°°ğ½½Èħ´½ÇËÓ·½ÄĦĤ­·ğĈĥşĊÀÇÒ×ïÙĉ÷ÙèùÖèúÙì˙ÖêŭÖêŭÖêŭÖêŭ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝÙì˙Ùì˙Ùì˙Ùì˙×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúĠìùĠìùĠìùĠìùÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöèôèôèôèôċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŭÎäĝÓĉûÄ×ï^o‡HXqM[vDQlP\xNZxIUsLTsMUtHPm]e‚krŽSZsSWoW[pfj{qt†txŒjm]arZ^lmq‚^bv[^sdh}fj{dhyil€ehy~~‹˜–£˘˘­İİ´˘­ğżÇŞ°·ħ·ÁĊËÌÏ×ĥşĊÁÈÓÚċóÚèĝÛìŭÖêŭÖêŭÖêŭÖêŭÖêŭ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝ×ëŝÙì˙Ùì˙Ùì˙Ùì˙×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúĠìùĠìùĠìùĠìùÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöèôèôèôèôċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŭĠéŝ²ĈÛTdzEUmM[tAOjWcMXwJSrS[zIQqJSr[c€^f„NWrLSoPTqINhW\sim‚X]tae}V\oX^qU[m]bwW\s]bykq„flV[pTWkqp€•“ĦŞŞĥ˜˜¤ˆŒ”Œ—…‹²ĥğ·¸ğÌËÏÚŬċ½ÁÌıÂÏÔâòÛêûĠéûÖêŭĠëŭĠëŭĠëŭÖìŝÖìŝÖìŝÖìŝ×ëŝ×ëŝ×ëŝ×ëŝÙì˙Ùì˙Ùì˙Ùì˙×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúĠìùĠìùĠìùĠìùÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöèôèôèôèôċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝċŝÒċŭ³ÄÚN^tIZpIWqHVqQ_zJXvLWvNZxEPoLWvQZyV^{W_}U\yOVsQUsTWt{€š\azSWqPUoPUlOTiV[pmr‡TXrOTmmr‡x}‘_d{X\qz}Œœœ¨›––˘z~ˆ†‰”{‰ww€Œ‹‘ĈĊÌÌÌĠŞ´°·ÁĊŜ×ċöÛëúÙêúÖêŭÖêŭĠëŭÖìŝÖìŝÖìŝÖìŝ×ëŝ×ëŝ×ëŝ×ëŝÙì˙Ùì˙Ùì˙Ùì˙×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×íŭ×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúĠìùĠìùĠìùĠìùÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöèôèôèôèôÒĉŝÒĉŝÒĉŝÒĉŝÓè˙Óè˙Óè˙Óè˙ÒäŝÔĉ˙Ġè˙ŭŭĠè˙Ġè˙¨¸ÒZhN[tN[tO]xQ_zM[vLZtL\vJZwBQoP^{Q]{W_W]~W[{UXwQUs\_}hl†TXrLOmIMkDGdDH_EI^‚‡œlq‹TXr~‚š„›TWtaeqv„ĤŞ´‡‹•~€qp€‡†–Œœ‚€‰‡”ĦŸİĊĊ³·ÁĦ°¤İ³ĊÌĠÙâìÜêúŜí˙ÚëûÖëúÖìû×íŭ×ŝÙñ˙×íŭÙíŭÙíŭÙíŭÙíŭÙíŭÙíŭÙíŭ×íŭ×ïû×ïû×ïûÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙúÙúÙúÙú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúĠìùĠìùĠìùĠìùĠìùÔëĝÔëĝÔëĝÓê÷Óê÷ÒéöÒéöÓê÷ÒéöÒéöÒéöÒéöèôèôèôÒĉŝÒĉŝÒĉŝÒĉŝÓè˙Óè˙Óè˙Óè˙ÒäŝÖé˙ÒäŝÓċ˙ŭŭ}İO_yNZvNVsLWsQ]yLWsN\wN\wHXrIZsHWtSa~S^}[abe†hi‹WXxW[yVZwZ^xTWtPTrOSqNQoBGaINc{€•^c}[_y–›²€…œX\yQVpjo}ˆ—˘¤ħ­°żwv†‰ˆ›‰†šš–މ‡•rp}——£żżË„‡˘­ğÁĈÉÚŬé÷ÙĉĝÜëŭŬï˙ÚïŝÙïŝÖïŭĠíû×íŭÙíŭÙíŭÙíŭÙíŭÙíŭÙíŭÙíŭ×ïû×ïû×ïû×ïûÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙúÙúÙúÙú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúĠìùĠìùĠìùĠìùĠìùĠìùĠìùÔëĝÓê÷Óê÷Óê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöèôèôÒĉŝÒĉŝÒĉŝÒĉŝÓè˙Óè˙Óè˙Óè˙Ôĉ˙ÓŭÖĉ˙Ġċ˙Ûë˙w‡ĦM]wDQlLWsLTqS[xV^{O[wS^zLWsBPkLZtO]xUa}Zbls_copž^bUXv\azW\vGJiHLjMPmFJbIObX^qZ^vy~•Ÿ¸w{UZq[_tty‡ty…‡‰–ž…„”‰ˆ˜£ĦħĴŞşğğÈvv‚‹ŸŸĞĤ޲İĴ´¤¨°ıÈÛĉôÛéùÛëúÙêùÛŝÙŭÖïûĠíú×íŭÙíŭÙíŭÙíŭÙíŭÙíŭÙíŭÙíŭ×ïû×ïû×ïû×ïûÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙúÙúÙúÙú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúĠìùĠìùĠìùĠìùĠìùĠìùĠìùĠìùÔëĝÓê÷Óê÷Óê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöèôÒĉŝÒĉŝÒĉŝÒĉŝÓè˙Óè˙Óè˙Ôè˙Öĉ˙ÔäŝÓŭ×è˙~Ž¨HXrHXrIWrIUqMUrQZwNVsQZwPXvNVsJVrS_yO\vU^y^eVZw^bsv“†ˆy}šUZs]byX]wPTqPTqMQkNSjPViLQdSWo~‚—ž¤·jp‚]bw[_trv‡UZhZ\kkm}€ššĤħŻ½ÀÌİİĥffsxx…‰‰•Œš–š˘˜œ¤ž£­ĈÏŜŬëûÜêúÛëúÙíûÖíúÙŭÚñŝÙíŭÙíŭÙíŭÙíŭÙíŭÙíŭÙíŭÙíŭ×ïû×ïû×ïû×ïûÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙúÙúÙúÙú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúĠìùĠìùĠìùĠìùĠìùĠìùĠìùĠìùÔëĝÔëĝÔëĝÓê÷Óê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÒĉŝÒĉŝÒĉŝÒĉŝÒĉŝÒĉŝÒĉŝÓĉŝÚê˙ÏŬĝÎÜ÷hvHVqHVqHVqGUpHTpOWtU]zLTqLTqOVsLSpS\wOXqV]wU\v]b{ptŽjoˆkmˆz}—kp‰X]wLPhNSjTWtOSpW\vLPhMQfPUjV[rae}U[mU[mkp…X]r^bv^bvehybdseerŸŸĞİĤ³˘ŸĴ‹‰škm}fixtw„Œ˜˜˘¤ħŸ³ÎÚèÜêĝÙéöŜñŭÙíùÙúĠìùÚïŝÚïŝÚïŝÚïŝÚïŝÚïŝÚïŝÚïŝÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñûÚñûÚñûÚñûÙúÙúÙúÙú×ïû×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúĠìùÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷ÒéöÒéöÒéöÒĉŝÒĉŝÒĉŝÒĉŝÒĉŝÒĉŝÒĉŝÓĉŝĠċ˙ÎÜ÷drFToIWrESmFToMXtS^zU]zHPmLSpJQoU\y\c€sw”[_w\ax^czdi€chchdf_bzSWoNSjX]r\ax\azOTmPUlae}QVmHMdLPjINhMQfdj}y~“ejVZojm‚€–‹œŽ‡‡“¨Ĥ°žž¨y{‹…‡˜€‚‘z}Œ}{Œœ‘‘ž¨Ş·•›İÀÉÙÙäòÙĉôÚê÷ÜïúÙíùÛòŭÚïŭÚïŝÚïŝÚïŝÚïŝÚïŝÚïŝÚïŝÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñûÚñûÚñûÚñûÙúÙúÙúÙú×ïû×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúĠìùÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷ÒéöÒéöÒĉŝÒĉŝÒĉŝÒĉŝÒĉŝÒĉŝÒĉŝÓĉŝĠŝw‚žHTpMXtO[wLWsNZvT_{P\xLTqIQoLSpOSq\_~il‹„ĦmpˆSVkdh}…ˆW[p[^sUZoNShV[r_h~^fzw“qx‘PWqPXockDJdGNjNUqFMiXawt}€ˆœV[rSUmtw‘Ĥ”–¨Ş››¤ħ°·‰‹xz‡‚”„†—‹ž}{Œ”“£œš¨´´Á‘–¤Ĥ°½ËÖâÜëöŬíĝŬù×íöÚïúÚïŭÚïŝÚïŝÚïŝÚïŝÚïŝÚïŝÚïŝÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñûÚñûÚñûÚñûÙúÙúÙúÙú×ïû×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷ÒéöÒĉ˙ÒĉŝÒĉŝÒĉŝÒĉŝÓĉŝÓĉŝĠċŝ}‹DOkIUqMXtJVrMXtP\xS^zLWvHPpGNkELiNQodh…ŒŞĤİěž³[^r{“ilSVkQUjMQfGLaJSf_h{ltˆW_vT[t^eJSiGNhNUqLSoFMjFMiIPjrzV^tLPhOQlxz“˜š°„…š‡†–˜¸·•”‚‚„†—„†—xw‰ts„Ÿ››¨´´ÁĞ­ş—œ¨İ°şÍÙâÛêòÚêôßòûÜòúÚïŭÚïŝÚïŝÚïŝÚïŝÚïŝÚïŝÚïŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÙŭÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñŝÚñûÚñûÚñûÚñûÚñûÙúÙúÙúÙú×ïù×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÔëĝÓê÷Óè˙ċŝÓċ˙Óċ˙ÒċŭÓûÔäúm{•LWsJSpJSpNVsT\yZbXa~NVvGOqFLoGMmIPlQXrbj~š˘´´şÍˆ˘fk€im‚\axNSjJOfQVmLPeV[p^cxchFIfMTqHOlFOjNUqZa}FIfQUrMPmTWtej„MQkMQkWZt}šxz“[\rbasfev˜–¤›‹˜ŽŒœŸœ°]\oa_r‹‰šŸŸĴÄÄğğǤ¤­Ħ¤ŻĤ­·Ë×ŜŬìôŬ÷ŬùÜïúÜíŭÜíŭÜíŭÜñ˙Üñ˙Ûò˙ÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóûÛòúÛòúÚñùÚñùÚñùÚñùÚñùÚñùÙĝÙúÙúÙú×ïù×ïù×ïù×ïù×ïù×ïù×ïù×ïùÖíĝÖíĝÖíĝÖíĝĠì÷Ġì÷Ġì÷Ġì÷ÔëöÔëöÔëöÓêôŭÖé˙ÓĉŝÔäŭÔäŭ×ċ˙p~—TazNVsLTsLTsNVvV^~V^~NVvEMlGMpNTwLSpFMi[cypxŒĤĥĞ´Ä^cx_d{\axW\sTXpNSjNSj\avioZ^sdi‚FIfGNkIOpGOlSZvfk…^c}}›[_yPTqei†OTmNSlbdŞ]_x]^t{zzyŒœšŞŽŒœ•Ÿ—“¤ž›Ż]Zm[Zlkj}›Ĵ³³żşı½ÄĴĴĥ¨­´ħğÄŬäßñĝÚêôŬíúŬíúÜïúÜïúÜñŭÜñŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöĠìôĠìôĠìôĠìôÔëóÔëóÔëóÓêò×éûÒöĠċû×ĉŭÖäŝ„ŞIVpW_}PXxMUwMUwPXzU]NVxGOqDLmNTwIOpBIfJQmLTjmv‰Ż·Épx‹W\schZ^vMQiSWoOTkPUjaez\btioae}AEbLSpLSpGOlJQkW\sswŒ£twX[vik†JMjQTopršMOh\]syx‹‰ˆ˜£Ħħ‹˜˜¨ˆšsp„ro‚a_r^]p‰Œ›ŻŻş³²ğ·ĥ½ÀÀɝ£ŞŻĥżżÌÓÛêòÜì÷ŬíúŬíúÜïúÜïúÜñŭÜñŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöĠìôĠìôĠìôĠìôÔëóÔëóÔëóÓêòĠċô×è÷Öċ÷×äùŒ˜°PZtS\wLTqQZyNVxNVxQZ{OWyHPrFNpIQqQWxW^{LSoMTmHOiS[qz‚–[cwX]wV[tHMf@E\INebf~PUjrx‹iodjzPUjHMfOVrHOlMUrFMiHMdX\qjm‚ei~š€‚›PSkik„wy‘lo‡TWl_av{zŽ˘Ÿ­ŽœĦœĞ“ŽŸŒŸĦrq„ZXkmpĦĦĴ–•žşıÀËËԛžĤžŻĴ·żËÖßÛêôŬíúŬíúÜïúÜïúÜñŭÜñŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÛòŭÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöĠìôĠìôĠìôĠìôÔëóÔëóÔëóÓêò•¤ŻŻÈ½ËÙz…–S\rV]wQXtV]zS[zT\{PXxLTsFNmIQqOWwT\{W^zQXtJQkELeELeNUoU]sGNhGLePTqNSlUZqMQipt‰[_tz€jq{}„ŽX^oHMdT[wOVsNVvSZwUWrHI_TUkrsˆ€•wxhi~st‰UXmMPeUXmst‰Œœ••˘­Ğı•“Ÿ¤ŸŻŞ·¨¤¸˘ž²ĦŸ²qp‚mp››ĤĦŸİĴв´´³·żŸ¤ŻĞ²ğŞħğÙäáïŭÜìùáó˙áó˙ÜñŭÜñŭÛòŭÛòŭÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöĠìôĠìôĠìôÔëóeqzit~NZeOXhPXlX]tX]w[^{QXvS[xS[xPXvNVsQZwT\yT\yMTmMTmBIcAHbMTmELeBIc@GcIMjOSpOTmjoˆrwŽhldj}MScPWaahrMScV[pT[tQXvOWtT[wVXqVWlijqs…actqs…ŒŽvx‰SVjTWlW[ohj{‘ĦĤĤ³œšĤ›¨ĥħÀ£ž°Ž‹žŒˆœ˘š˜Ğ“˘›š˜˘Œ“ııÂĊĊÎĤŞ´Ż³ĤĞ·ĥżÌáïŭŜïûáó˙áó˙ÜñŭÜñŭÛòŭÛòŭÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöĠìôĠìôĠìôÔë󀉔y‚bkxNTdV\oX\qW[p^aySZsU^yU^yQ[vS\wU^yS\wNWpFNdAI_BJa>E^GNhGNj7>Z;B_?Ba?B_BGaX]wINech}‰˘GM]DIWOUeafy_dyJSiMTmWawdl€[]ofevsr‚hfwfevml}š˜ĞžĦ²qtˆ_cxadx[]o€ĤĤ³›¨œšĤ­İ¸–‘£}yzw‹{z†…—•—ĤœŽ–Œ‹‘İݲÀÀɲĥÀ¸ğĈ´·Äœ˘°ÁÏŬÜìùáó˙ŜñŭÜñŭÜñŭÛòŭÛòŭÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöĠìôĠìôĠìôÔë󅌖t{†pt€lqhlzehyTVh_avZ^sXatU]sPXoQXrT[tNUoELeAHbDH_AF_@GaELh=Da=Da@Gd>A_>A^JNkHMfAF]PUjOTiOUhX^qsyŒejOTiS[qFNdLViT\pX\pZ\m^arihzdcvjiy‹‰š£˘´rsˆy}~“xz‰wy†„„——£‰‡”~{‰‡…“‰‡—‰†…—‹‰š”–£ŒŒ—š˜˘¸·ğğĊÇżżË½½ÈĥĥÁŞŻş›ĤÓáñŝÜïúŜñŭÜñŭÜñŭÛòŭÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝÜóŝŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭŬôŭÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöĠìôĠìôĠìôÔë󏓝•˜£ˆŒ–‹Ž˜ĈÉÔħħ½šš››¨sv‚hjyps…VZmUZqPUoLOlFIhFIhGHhEFeAEc@Db@Dd>Dd>Eb>Eb?FbHOkFMiAHbAHbBIcNVlXatS[m]eydl€U]sLSlJQkLSlTWtHJhOQj]^tcdyxz‰ħħ‡†–mp}~“‘”£žĦ­–˜‚…‘Žvv‚Œss€‹‹—Žž€……‘““Ÿİœ›¤­Ĵĥşı½Ĉ¤£Ĵı¸Á½Ĉ²ĥÀ—žİœ¨³ßïùáñûÛíùßòŝŬò˙ÚïúŜó˙ŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŬöúŬöúŬöúŬöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷ĠïöĠïöĠïöĠïöÔíôÔíôÔíôÓì󏐖‰‹›œ˘˘£İ³´şÙÚßÈÉÏĴ­³½Ä··À¨Ş·[]obc{bdMNmIMkJLkLMlIJjFGi@Dd@Dd>Dd>Eb?Fb>EaGNjDJfBIeAHd?FbFMfDLb@H\IQeckQZpIPjFMfOVrMPoLOmPSm]_x]^spr„……‘~}“~€‘vx‡‡‰–˘°£²prvx‡Ž……‘ss€ŽŽ›~~‹šŞš—¤œ›¤¤£ĴÓÒÛ¤£Ĵ­Ĵĥ³²ğ°°ıħĥÀ‚Œ–ÒŬéßïùáñŝáó˙ßô˙ŬòŝŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŬöúŬöúŬöúŬöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷ĠïöĠïöĠïöĠïöÔíôÔíôÔíôÓìóÁÂĈ¤İŒ”•˜—˜œŜßÎÏÓĊĈÉżÀÄ´ĥğ··Â{zcd}hh†VWyLMoILiJMjHJhEFe@Db@Db>Eb>Eb?Fb>EaELhAHdAHdAHd>Ea@GcELh>E^ELe]e{JSiGOcNVjV^rPUoNSlOQjX\qQShbdvom€{z{}‘mpQTc[]j…ˆ“‹š†ˆ—ŒŽ††“ddqbboyy†ˆˆ•ŒŒ˜œ““Ÿ›¨›š£žĤ²ħşžĤĤŻĦŸİš´ıʟŞĴĥÀâíùáñûŜñŭÛûŬòŝŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŬöúŬöúŬöúŬöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷ĠïöĠïöĠïöĠïöÔíôÔíôÔíôÓìóÉËÍ´ĥ¸Ĥ¨Ş‘“•‹ŒŽ·¸şÜŬßÚÛŬ¸ığ²³·ÇÇŞİızxdd€__NOoDFaDFaAD^?A^@Da@Db>Eb>Eb@Gc>EaDJf?Fb=D_@Gc>Ea@GcFMi@GcJQkXawPXoJSfXasPXlGLcQVm\_tQUjSTi\^pts„vt‡‚—ik}UWfikxx{†…‡”‡‰˜‘Ħ^^kzz‡‚‚‰‰–‡‡”‘‘ž””Ħ”‘ž£˘ĞĦŸİħ°ı†…Ž£˘Ğ˘ĦŞŸŸİ²ĥÀ­²½‹‘œÚíòŭáóŭŜó˙áö˙ŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜôŭŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŬöúŬöúŬöúŬöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ï÷×ï÷×ï÷×ï÷ĠïöĠïöĠïöĠïöÔíôÔíôÔíôÓìóİĞŞÀż‘”“ˆ‹‰mpoz}{ËÍÌßâáÖÖÖÒÒÔÁÀÇÀ˲ħĖ”Ğde~OOkHLaGJ_HJcFHa@E^=@]:A]=D_>Ea?Fb>Ea;B^>Ea?Fb@GcAHdMTmSZsHPfFNdS[qOWmQZpLTjJOi_d{JNc_cw]_qjl{llyŒy{VXjWZirtlpz€‚•—Ĥ˘¤³””Ħww„}}‰‹‹—††“””ĦĦĦ­““Ÿ›˜”“œ›š£ĈĊΣ˘ĞžĤŞİ²°Ż¸ßßéğğǔ˜£şÄÎŬìôáóŭßöŝÛòúŜôŭŜôŭŜôŭŜôŭßöŝßöŝßöŝßöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜ÷ûŜ÷ûŜ÷ûŜ÷ûŬöúŬöúŬöúŬöúÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ñĝ×ñĝ×ñĝ×ñĝÖ÷Ö÷Ö÷Ġïö~ħ²´‰‹‚…deh‘”Ż°²ÎÎθĥ·ÓÔÜÚŜÒ×°­şŞİıwvˆWXmOSdNQeNQeLOd@E\>B\:A]>Ea?Fb>Ea;B^=D_@Gc?Fb>Ea=D]=E[FNdDLbMUkS[qQZpT[tU\vV[rejTWlX\pcewjlyyy…ŽŽ›y{\^plo~y{ˆ‹Ž˜”–£vx‡~€••˘‘‘ž‚‚““Ÿ••˘‰‰–––£˜˜ŸŞĦŸİĤŻŸž¨ħ°ı£˘ĞݨħŞİ²ħ°ı´³½Ĵ°ş‹‘›ßëôÛíôŜôŭß÷˙ŜôŭŜôŭŜôŭŜôŭßöŝßöŝßöŝßöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜ÷ûŜ÷ûŜ÷ûŜ÷ûŬöúŬöúŬöúŬöúÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ñĝ×ñĝ×ñĝ×ñĝÖ÷Ö÷Ö÷ĠïöĦ˘”•˜‚„‡{}€dei…†‰˜šššœ›˜šĊÀÁÛÙÜÓÔÍÌĈĊÌ°°ıeisV[fTXfQUfOSfAF]>B\;B^?Fb>Ea;B^=D_=D_?Fb>Ea=D_;B\BJaEMa@H^MUkIQhJSiNUoV^tdi~{”W[odhysv…z}Œ˘˘Żqp€VXjxzŒrt„vx…€„Ž“•˘fixdfvŽŽ›ww„‡‡”££°Ë——¤››¨œšĤš˜˘Ż­·ÁÀɤ­´³½­Ĵĥ°Ż¸Ĥ£ĞÍÉÔ½½Ĉž£­İĥ½Ŝ÷ŜôúŬöúŜôŭŜôŭŜôŭŜôŭßöŝßöŝßöŝßöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜöŝŜ÷ûŜ÷ûŜ÷ûŜ÷ûŬöúŬöúŬöúŬöúÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ñĝ×ñĝ×ñĝ×ñĝÖ÷Ö÷Ö÷Ġïö°ħ´°ħ´Ĥގ“{}€efjmor_^cŻĴ°ĠÓ֗•˜Ĥ¤¨”Œ‹•–œ‰‰“^blV[fSWePTeBF[?D[?B_>Ea;B^=D_=D_>Ea:A];B^=D_>EaFNdAI_;DZJQkHOiBIc;B\AHbV[pafy]arhlzy{‹ž­jiywy‹bdvjl{}Œfiv~€oq~zz‡˜˜¤ŽŽšœ‡‡“~~‰““ŸĞи““žœ›¤ĦŸİ˜—Ħ£˘ĞœŞİ²Ÿž¨ĥ´ğ˘ŞÒ×ÀÁÇŻ´ğ°ĥÚéßñ÷öŭŜôúŜôŭŜôŭŜôŭßöŝßöûßöûßöûŜöŝŜöŝŜöŝŜöŝŜöŝŜ÷ûŜ÷ûŜ÷ûŜ÷ûŜ÷ûŜ÷ûŜ÷ûŬöúŬöúŬöúŬöúÜôùÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙĝÙĝÙĝÙĝ×ñĝ×ñĝ×ñĝ×ñĝÖ÷Ö÷Ö÷Ġïöĥ´ı¨ĤĞŸž£š˜~}zy~‹‰ŽtsxqptĞŞŻÄÂɍŒ“VU^WV_ljwyy…lpzTWbSVaUWfGJ^GIb@Da>Eb?Fc>Gb=Fa;E];E];E_;E_;E_?Ha?Ha?Ha?Hc@Id>Gb>GbAHdMQiNSh_ctqv„‡‰–Žvt…sr‚zy‰kjzvt…~Žyy†••˘““Ÿ––˘ħ°ıݨŻžĤ•”€‰”‘žŽ›ĦžĞ˘ĦŞ—–Ÿ‹‰£˘İÏÖŞİ°¤Ğ°Ż³ħ°´ŞĞŻ³¸ş²ĥğ°ĥ½´ğÂÖáĉßïöáóúŬöúŜ÷ûŜ÷ûßĝŭáùŭâĝŭá÷ûá÷ŭá÷ŭá÷ŭá÷ŭâĝŝâĝŭâĝŭâĝŭáùŭáùŭßĝûŜ÷úŜ÷úŜ÷úŜ÷úŜ÷úŬöùŬöúŬöúŬöúÜôùÜôùÜôùÜôùÛóĝÛóĝÛóĝÛóĝÚò÷Úò÷Úò÷Úò÷×ñö×ñö×ñö×ñöÖôÖôÖôĠïórqv¤İ›šžœ›Ÿ{z“‘–„‚‡wvz€†¤Ğşı˜—Ħa^k_]jb_mdbpss\_hNQ\\^kNQeGIbDGd>A_?Fc>Fc=Fa;E];E];E_;Da;Da>Gb>G_>Gb>Gb=Fa=Fa?GdELhUZsOTi]ar€…“‚…‘sv‚yxˆkjzom~dcs~Ž‰ˆ˜~~‹}}‰šš““œ°ŻĥĴĞ°¨Ĥ­›šĦ•”ĴĞ´”‘ž”‘ž¤­–•ž”“š˘Ħ¨´³¸ĊÄÈÓÒÖ½ğÀĴ­°ŞĞ­şğżğżĊ¤¨°ÀĈ͞¤Ğäï÷âôûŜ÷ûŬöúÜôùâúŝŜ÷úß÷ĝùŝá÷ŭá÷ŭá÷ŭá÷ŭâĝŭâĝŭâùúâùúáùŭáùŭßĝûŜ÷úŜ÷úŜ÷úŜ÷úŜ÷úŬöùŬöùŬöùŬöùÜôĝÜôĝÜôĝÜôĝÛóĝÛóĝÛóĝÛóĝÚò÷Úò÷Úò÷Úò÷×òô×òô×òô×òôÖñóÖñóÖñóĠò}{€wvzrqvvty€…°Ż³ĦŸ¤”‹‰‰ˆ‰ˆ‘š˜˘„ŽŒ‰–a^lZWe^^j[^iZ]h]_lPTeJNcGLe@Db?Fc>Fc=Fa;E_;E_;E_;Da;Da=Fa=F^=Fa=Fa=Fa=Fa?GdELh\azOTicfx€…“jly\^k{z‹_^oVUebaqqp€~Ž}}‰ss€˜–ݨŻĞŞŻŻ­´œ£ĦŸİĈĊΨ²Ž›­Ĵĥ‘šž¤¤ĞĦŸ¤ÂÁĈëêïÌËÏĈĈÈħħ³ĊĈÉżĊŻ²ş½Àȓ˜ŸÁÌÔßñĝßöûŜôúßöûßöúùŝâúûŜ÷úá÷ŭá÷ŭá÷ŭá÷ŭâĝŭâĝŭâùúâùúáùŭáùŭßĝûŜ÷úŜ÷úŜ÷úŜ÷úŜ÷úŬöùŬöùŬöùŬöùÜôĝÜôĝÜôĝÜôĝÛóĝÛóĝÛóĝÛóĝÚò÷Úò÷Úò÷Úò÷×òô×òô×òô×òôÖñóÖñóÖñóĠòżÂ‰ˆ\[_edi„‚‡ħ°´ĥ´ı—–›”“š‹‰„‚Œ[Zc…‚ż½É“žcao]]iVZdkoy_boNQcNQfGLeGJh?Fb>Fc=Fa;E_;E_;E_;Da;E_=F^=F^=Fa=Fa@Id>Gb>FcAHdGLeX]rcfxdiw]_leht~Žbaq\[ksr‚xw‡{z‹‚‚yy†‰‰•••ž£˘İŞİ­³²ıž¤”“œŞİ²ĦžĞ¤˘Ż·ĥżŒ‹”¤£ŞŞİ°ĤŞİ¨ĴÇĈËÁÀĊ½½żżżÁşıĈÇÍııÂÁÉÁɍ”ÒáéŜñĝä÷ŝäú˙âĝŭá÷ûßĝùáùŭá÷ŭá÷ŭá÷ŭá÷ŭâĝŭâĝŭâùúâùúáùŭáùŭßĝûŜ÷úŜ÷úŜ÷úŜ÷úŜ÷úŬöùŬöùŬöùŬöùÜôĝÜôĝÜôĝÜôĝÛóĝÛóĝÛóĝÛóĝÚò÷Úò÷Úò÷Úò÷×òô×òô×òô×òôÖñóÖñóÖñóĠòݨĴ´³¸edi~‚ĦŸ¤—–›­ĴħĴĞ°š˜Ÿ–•œžĤ~}†WUbĴŞ·ÍËÙhesbbmps~Z]ecfq_drMPdINeJOiAHd=Fa=Eb;Da;Da;Da;E_;E_;E];E];E_;E_>Gb;E_AIfJQmJOiW\qZ]olq_boz}‰€ml}ihxrq~}vt…tttt””ŸĤĴвĴĞ°Şİ°š˜Ÿ“‘›žĤ£°½şÇĤŻ–•žŒ“š˜Ÿœ›ŸĤŞżÂĈĊÉÀÁĊÂĈżÂÔÓÚ²²ğğğĊÁɟ¤Ż…šÉÙáäöŭÜïöá÷ûùŝûŭäŭ˙âĝŝâĝŝâĝŝâĝŝâĝŭâĝŭâùúâùúáùŭáùŭáùŭáùŭßĝûßĝûßĝûßĝûŜ÷úŜ÷úŜ÷úŜ÷úŬöùŬöùŬöùŬöùÛó÷Ûó÷Ûó÷Ûó÷ÚòöÚòöÚòöÚòö×òô×òô×òô×òôÖñóÖñóÖñóĠòğşżÂÁĈĦŸ¤Œ‹İ¨Ĵ£˘Ĥ–•š”“—¨Ĥ­œ›˘žĤ°Ż¸‚€[Xe^\j¸ĥÄ]]iX\f_ck\_jlq}fj{DH]GLcDJd>Gb=Eb;Da;Da;Da;E_;E_;E];E];E_;E_=Fa;E_?GdGNjPUoQVkSVhlqvx…y{ˆedtedtxw‡xw‡sr‚„‚“‰‰–qq~‰‰•¨¨ħĈĊ̽ğÀħ°·½ğŸž¨Ž—“{y†€ˆ˜¤£Ş“‘˜”ĤŞğşżËÉÎÈĈÉËÈÌÂÀĊÀżĈğşÄżżÈÂÂÌ°´ż}†œßï÷èù˙áôùá÷ûáùúáùŭâĝŝâĝŝâĝŝâĝŝâĝŭâĝŭâùúâùúáùŭáùŭáùŭáùŭßĝûßĝûßĝûßĝûŜ÷úŜ÷úŜ÷úŜ÷úŬöùŬöùŬöùŬöùÛó÷Ûó÷Ûó÷Ûó÷ÚòöÚòöÚòöÚòö×òô×òô×òô×òôÖñóÖñóÖñóĠòÏÔŻ­²¤İĤŞ—–››šžœĦ‘•‘—ˆ‡Ž–•ž¸·ÀĈÄ^\ib_mqo}SS^Z]h[^fadohlx}FL^=AVFMf@Id=Eb=Eb;Da;Da;E_;E];E];E];E_;E_:D^:D^=EbAHdFJdNShMPb]bp„‘ž„‚“‡†–xw‡€yxˆ€šŒtt€ŽŽ—ħ°·şıĤĴı¸ż­Ĵĥ–•ž£°š˜—Ħš˜˘•”›š˜ŸŽ‘ݨĴğşżËÉÎâßÔÎÓÄÁĈÌÈÏÎ×ÎÍÖğğĊĤŞ´£Ş´y‚Ĵ¸Áè÷˙ċùŝßöúßĝùßĝûâĝŝâĝŝâĝŝâĝŝâĝŭâĝŭâùúâùúáùŭáùŭáùŭáùŭßĝûßĝûßĝûßĝûŜ÷úŜ÷úŜ÷úŜ÷úŬöùŬöùŬöùŬöùÛóôÛóôÛóôÛóôÚòóÚòóÚòóÚòó×òô×òô×òô×òôÖñóÖñóÖñóĠòÈÇÌŞİ­–•šŻ­²İ¨Ĵ£˘Ĥ³²·­Ĵħ£˘Ĥ{z‡†œ›¤¤˘ŻljwVTbVVcNP]Z]hilwbepcfqimyrv‡FL^BJaDJd?Fb>Eb=Da;Da;E_;E];E];E_;E_;E_9B]:D^;Da=D_@Ga?D[FI]ps…ŒŽœžĞ——¤‚‘‚‘xw‡~Ž€€‡‡”„„vv††żĊ²ħĥŞİ°–•œ‰ˆ‘ĦŸİ¸·ÀݨħğşÄ­Ĵ³¸·ĦŸĤ—–›­Ĵħ½ÁÄÂÇÍËÎÍËÎı·ğ½şż°Żĥ³²ğÁÁËÁÁËÁÌ˘Ĥ²‚‰”Ë×Ŝċôûèû˙âĝŭßĝûáùŭáùŝâĝŝâĝŭâĝŭâĝŭáùúáùúáùúáùŭáùŭáùŭßĝûßĝûßĝûßĝûŜ÷úŜ÷úŜ÷úŜ÷úŬöùŬöùŬöùŬöùÛóôÛóôÛóôÛóôÚòóÚòóÚòóÚòó×òô×òô×òô×òôÖñóÖñóÖñóĠòÇÇÉÏÏÒÀÀ¨““•¨¨Ş··ıŻŻħ›››œœœ‚…rsy˜˜˘hhtNM]NP_SUdacrllxffrllxrt}ŽfizAEZHJcJNkDGd>A_9@];B^@Ga?Fb?Fc>Eb>Eb=D_;B^;B^=D_;B\AF_DHb@B[LOcmpŽ˜›¨‹‹—zz‡Žrr~xx„tt€‹‚‚Œĥ·½ışÀ°ħ·ĤĴ•–œĞĴ²³´şħ²¸Ż­´²ħĥ·ĥşşıŸž£ĦŸ¤½ğÀÇĈËÁÀĊ²ħĥ´³¸şı³²·ÏÖĈĊÌÄÂɽĈ´´ÀŽ‘œ•œŬêñè÷˙ċĝ˙á÷ŭáûŝáûŝâúŝúûäûŭûŭâŭŭâŭŭâúûâúûâúûâúûáùúáùúáùúáùúßĝùßĝùßĝùßĝùŜ÷ĝŜ÷ĝŜ÷ĝŜ÷ĝÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ñ×ñ×ñÖïŜŜáÈÈ˽½ż³³ĥ••—““•ÀÀÂÇÇÉĞĞĞŻŻŻşğ‰‹Ž[^fTVcPSbX[lVXjbdsoo{ħħ½››Ĥ~~‰hhtkjz^_tHI_HJeMOlFIf?B_9@\;B^>Eb>Eb>Eb>Eb>Ea=D_;B^;B^;B\@Ga>A^@E^QUjNQcUWd…••Ħ……‡‡“iitffrllx€€Œ‰‰•°°ı´ĥğħ²¸£¤Ş…†ŒŻ°ĥĤ¨­²³ıı¸½²ħĥ¸·ğÁÀĊı¸½›šž˘ĦğşżÉÈÍı¸½ĴĞ°·ĥş´³¸Ù×ÜÄÂÇÈÇÎÀ½Ç¤­²²ğ‡Œ–Żıßëôĉ÷˙äú˙áûŝáûûâúûúûäûŭûŭâŭŭâŭŭâûùâûùâûùâûùáúĝáúĝáúĝáúĝßĝùßĝùßĝùßĝùŜ÷ĝŜ÷ĝŜ÷ĝŜ÷ĝÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ñ×ñ×ñÖïßßâÒÒԚšœŸ££““•­­°³³ĥŻŻŻŸŸŸ—˜›Ż°³z~†jlyNP_QTewy‹hjyzz‡””ĦÈÈÔ\\hllyts„y{PQfEG_HJeGJhAEb=D_>Ea>Eb>Eb=Da=Da>Ea>Ea=D_;B^=D]9@Z@Da?D]PTi^bsdfswz…]]immy‰‰•{{‡jjvzz†††‘““ž˘˘Ğ³´şışÀ²³ı–—‘“˜³´şİŞ°¸·ğ²ħĥ­ĴħŻ­²½Á¸·ğħ°´¤İ½ÁÇĈËşı”“—½ğÀ·ĥşÀżÄÎÍÔÇĈÏ°Ż¸½½Ĉšžİx‰ħ½Ĉéĝ˙ä÷ŝâúŝáûûâúûâúûûŭûŭâŭŭâŭŭâûùâûùâûùâûùáúĝáúĝáúĝáúĝßĝùßĝùßĝùßĝùŜ÷ĝŜ÷ĝŜ÷ĝŜ÷ĝÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ñ×ñ×ñÖïÌÌÎ××Ú³³ĥŞŞĴ°°²ĴĴŻššœ””–ĦĦĦ£¤Ĥ¤İ˜œ¤‚…‘WZiTVhpr„\^m~Žffs““ŸZZe‰‰–ĦĦ­¤ĤĥtwˆEH]FHaLPjGJh?Fb;B^=Da=Da=Da=Da=D_>Ea>Ea=D_>E^:A[;?\@E^NQfcfx…‡”£ĤħŒŒ—€€Œww‚„„{{‡‹œŽŽš˜˜˘´ĥğ³´ş³´ş¤Ğ‚ˆ¤ĞĦ˘¨´³¸³²·ħ°´·ĥşÌËϽğÀ½Áı¸½żÂğşżĈĊɘ—œÌËÏÂÁĈìëáßĉÄÂÌÇĈÏĥĥż°³y€‹‰“ë÷˙ôûùŝáûûâúûâúûûŭûŭâŭŭâŭŭâûùâûùâûùâûùáúĝáúĝáúĝáúĝßĝùßĝùßĝùßĝùŜ÷ĝŜ÷ĝŜ÷ĝŜ÷ĝÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ñ×ñ×ñÖïÄÄĈÌÌÎÏÏÒ¸¸şşş½³³ĥÂÂĊĴĴŻ˜˜˜“”–—˜œœŸ¨‡‰–LN]IL]qs‚lo~kjzNM]FFSOO\eeq~~‹y{ˆ…‡–_cw>@XOTmFIfAHd>Ea>Eb>Eb>Eb>Eb9@\:A]>Ea>Ea=D]=D];?\HMfHLa\_q„Ĵ°şŻŻş““ž††‘‰‰•ŽŽšˆˆ”——£ˆˆ”ĤĤĴŻ°ĥ²³ı¸ıżš›ĦŽ”°ħ·´³¸ı¸½ħ°´şıĊÄÈÇĈ˽ğÀı¸½ĊÄÈÈÇÌĊÄȤ£¨¨ĤĞÖĠÚâĉŬÜÔÓÜËÉÓÀÀÉÍۖ›Ĥ—˘ÈÎÜë÷˙ùŝûŭâŭŭâŭŭâŭŭâŭŭûŭûŭŭúâûùáúĝáúĝáúĝáúĝáúĝáúĝßĝùßĝùßĝùßĝùŜ÷ĝŜ÷ĝŜ÷ĝŜ÷ĝÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ñ×ñ×ñÖïÀÀ··ıËËÍÀ¨¨Şĥĥ¸ÈÈËÇÇɲ²²ŞŞŞĴ­°ŸĦ¤˜œ¤›ŞGIXIL]SUdehw„‚“XWhML\^^k]]ipp{Ž‘œŻħÀ“–ŞILdINhHLiAHdAHd?Fc>Eb>Eb>Eb:A]:A]=D_>Ea>E^@Ga>A^AF_AEZ]arrt{‰}}ˆˆˆ”……›——£ħŸŸĞĤĤ°ŞĞħ¸ıżĥ·½­Ż´ħ²¸†‡ŞĞħğşżÌËÏÂÁĈĞŞŻı¸½ÍÌÄÂÇşı·ĥş½ğÀ½Áħ°´żÂßŜÄÂÇŬÜíìöÂÁËÇÇÜĊÇԏ”ŸŞŻ½ëô˙ċùŝâúûâŭŭâŭŭâŭŭâŭŭûŭûŭŭúâûùáúĝáúĝáúĝáúĝáúĝáúĝßĝùßĝùßĝùßĝùŜ÷ĝŜ÷ĝŜ÷ĝŜ÷ĝÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ñ×ñ×ñÖïÁÁÄşş½ĊĊÇÀ“¨ğğÁÁÄĊĊĊ³´·°ħ´Ĵ°¸”–£LN]OQc\^macr…„”ji{TScbboZZejjsŻ²şş½É_ctVZo@E^>A^FMiBIeBIf?Fc>Eb>Eb:A];B^=D_=D_?F_>E^@Da@E^GJ_\_q{~‹z~ˆss††‘……‹‹‹–˜˜¤““ž›¤¤­ÍÎÔğ½Â³´ş¤ĞŸĦĤ­Ż´ž¤ħ°´żÂÏÎÓ­Ĵħħ°´ž˘Ż­²ÂÁĈżÂ³²·¸·ğĞŞŻşıżÂÍÌ÷öŭÙ×áÄÂ̽½ĈĈĈÒ××䚜ݐ“˘ÉÛèù˙âúûâŭŭßŝŭßŝŭâŭŭûŭäûŭŭúâûùáúĝáúĝáúĝáúĝáúĝáúĝßĝùßĝùßĝùßĝùŜ÷ĝŜ÷ĝŜ÷ĝŜ÷ĝÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ñ×ñ×ñÖïÇÇÉÄÄĈ½½ż½½żĤĤİıığħħ³³³³ÁÁżÇÇDz³ĥ°ħ´ŞŞ³ĦĦ­NM_IL]UWiik}PObqp‚edt^^kaaljjs­­·İĴ·}Ž{}‘SUmFHcHLiGJhDJfAJe>Gb>Ea:A];B^>Ea=D_?F_9@Z@DaFJd=?WLOc~‚Žwz…ooz‚‚Ž——£‚‚Žˆˆ”––˘……–—•–œ³´şżÀĈŒ“Ħ˘¨½½ĈĤ¨­¨ĤĞĊÄÈÎÍÒ´³¸ĴĞ°·ĥşÀżÄ²ħĥğşżĊÄÈÇĈËĥ´ıÀżÄÎÍÒÜÛßÔÓ××ÖŬâéżÀĈĴĴĥÀÀÌ´´ÁŒœĦĴá÷ċùûâúûâŭŭâŭŭâŭŭûŭŭúŭúâûùáúĝáúĝáúĝáúĝáúĝáúĝßĝùßĝùßĝùßĝùŜ÷ĝŜ÷ĝŜ÷ĝŜ÷ĝÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ñ×ñ×ñÖ‘ÀÀÀĞĞĞËËËÁÁÁııı½½½ÄÄÁżżżÎέĴ³ŻĴıŸ­PMaNMbHF]qo†\[pzyŒ€\\i‚‚Žllv}~„‚‚Œ˜˜¤­Ĵ½›²NLeNLhJMjJTlGTkANe=F^=D]:A[;?\=@]?D]?D]?B_?B_AF_?DXfmx~„‹swvvŽŽ—~~‡ww„žžĞ‡‡“–¤Ğ‹‰“£˘Ğ“‘˜œ›¤ŻĴıĦŸİ˘Ħ²²´ÏÏÒÀÀÂĈĈÈħħ³££ħħ³½½żÈÈËÇÇÉÂÂĊÄÄĈÇÇÉÍÍÏÌÌÎñôöôùÈÇÌ­Ĵ³żĊĊÄ͑œ–š¤¸ÂËĉöŭäöùë˙˙ŭúŝûŜŭùâŝĝäŝùŭĝâû÷âû÷áúöáúöáúöáúĝßù÷ßù÷ßù÷ßù÷ŜĝöŜĝöŜĝöŜĝöŬ÷ôŬ÷ôŬ÷ôŬ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòÙòÙò×ñïxxxÌÌÌÒÒÒĥĥĥÌÌ̜œœ‡‡‡°°°ĤĤ¤˘˘˘¸¸ş²ħ¸°­ş˘Ÿ­OL_LH^VTkWUlMLahfy˜˜ddpœœss}iho…„‹¤­ħŻ½ÉĈډ†XToTTpDMeN[rDPh>G_:A[:A[>A^>A^?D]?D]?B_?B_BFc=AXZ_my~ˆtx‚ppyˆˆ‘‰‰•““Ÿ‹‹—zz‡””ŸŽ—Ÿž–•žŒ••”Ž—ĥ³ÀğşÄš˜²²´ÁÁĸ¸şżżÁÇÇÉĴĴŻĦĦ£ğğĈĈÈÀÁÁÄÀÀÂĊĊÇÌÌÎÇÇÉċÂÂĊÒĠ·ĥşÀżĈżĊĞŞ³ŽŽ—˘İ²áëóċöùéúŝċŭûáúĝâ˙úŜŭ÷˙ùŭĝâû÷âû÷áúöáúöáúöáúößù÷ßù÷ßù÷ßù÷ŜĝöŜĝöŜĝöŜĝöŬ÷ôŬ÷ôŬ÷ôŬ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòÙòÙò×ñïllj––”ÏÏÍżż½¸¸ĥ~~{……‚żż½ĦĦžžžœ££ħ°´İ¨ħĞİĥHFTOM]XWjXWjSQb\\ittww‚{{…fhmomt–•œŻ­·ğȽşË²ŻÂ[WmVWpBIcQ[vISmBIe=D_>A^?B_>A^?D]?D]?D]?D]?B_BG^MSasx„xz‡zz‡Œ––£{{‡šš£……Ž‚‚Žzx…˜–£œ—–Ÿ—–Ÿ–•žŞİ°ÇĈÍ°Ż³³³ĥĞĞ­ħħ³²²´ĞĞ­żżÁĈĈȳ³ĥÁÁÄÇÇÉÁÁÄıığ½½żÈÈËÈÈ˽½żÇÇɸ·ğÁÀĊı¸żÉÈÏı¸Á‹‹”Œ›ĈÙíú˙èùŭċùùċ˙ŭßûöá˙ù˙ùŭĝâû÷âû÷áúöáúöáúöáúößù÷ßù÷ßù÷ßù÷ŜĝöŜĝöŜĝöŜĝöŬ÷ôŬ÷ôŬ÷ôŬ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòÙòÙò×ñïaa^yywÙÙÖËËÈÈÈĈ……‚wwtĦĦžŸĦœ´´²ŸŸ˘ĤĴœ›¤VTaLIVNN[UUbPP\EEPaalMMVMNThiokjož˘½ğ£˘ĞÄÁÏğıÉws‡QPeFJdMTpNUqHLiEHeAEb>A^=A[?D[?D[?D]?D]?B_DHbBHXqvsv‚qq~wv†——¤……Ž‹Œ‘˜””Ÿqo}Œ‰–¤­¨Ĥ°›¨›š£–ğşż¸¸ş¸¸şğğÄÄĈğğ´´·²²´íí¸¸şĊĊÇÂÂĊĊĊǽ½ż´´···ııığ¨¨ŞÉÉÌÄÂÇşıħ°·¸·ÈǞž¨‚‡‘“š£Üéíâòöĉúúúù˙ùâŝĝäŝùŭĝâû÷âû÷áúöáúöáúöáúößù÷ßù÷ßù÷ßù÷ŜĝöŜĝöŜĝöŜĝöŬ÷ôŬ÷ôŬ÷ôŬ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòÙòÙò×ñïVWSopk½ı½ıŒvwr€}”•‚„²³Ż³³ħ„„„ĦĦ£Œ‹–•œkjqddmTT]TT]OOXZ[aTU[NOSdeiomrˆ‡Œ¤İ£˘İÂÁËĤ¤ħqo}edtSVk?D]OTmOTmHLiBFc;?\>B\?DX?DX>BZ>B\AEb>B\?EWzsv‚oo{ml}š––Ÿ‚„‰Ĥ““ž›rp}Ž—ħ°ı°­şžĤŒ‹‘Ž“ħħ³ÁÁÄÄÄĈşş½ĈĈÈĈĈÈÉÉÌÌÌθ¸şÌÌÎÇÇÉĊĊÇÀÀÂÀÀÂşş½²²´şş½èèêÜÛßżÂ½ğ¸·ÍÌĠÈÈ҇‹•y€‰İ³ıíú˙ä÷÷ċŭûäŝùáŭ÷ċ˙úäŝùŭĝŭĝâû÷âû÷âû÷âû÷ßù÷ßù÷ßù÷ßù÷ŜĝöŜĝöŜĝöŜĝöÜöóÜöóÜöóÜöóÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòÙòÙò×ñïTUPbc^}~yĞĴ¨˘£žbc^‡ˆ„stpklh‹Œ‡‹‹ˆ€€~€€€‡‡‰°°²°Ż³ijm]^bXZ]deiZ[^\]aTUXefjooqwwyıı𜛟ı¸żŒ‹”°Ż¸††“cewLOdPTiPSkOQlIMj?B_>B\?DX@EZ?D[?D]?B_?D]BH[jo}€‚{{ˆddqxx…œœ¨††‘‘›˜˜¤£Ħ­“xw€Ž—°Ż¸ħ°ı˜—ž†…ŒĞŞŻğğÄÄĈÉÉÌÀÌÌÎÈÈËââäÍÍϸ¸şËËÍÉÉÌÇÇÉÂÂĊÁÁÄÂÂĊğğÀÀÂÄÂÇÈÇÌÇĈͨĤ­ÒÚĊĊΟ£­{€‹†”Ôáċċĝĝċùùŭĝ˙ùċ˙úäŝùŭĝŭĝâû÷âû÷âû÷âû÷ßù÷ßù÷ßù÷ßù÷ŜĝöŜĝöŜĝöŜĝöÜöóÜöóÜöóÜöóÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòÙòÙò×ñïUVOef_lmfİŞ£‰‹„WXQ„…~mohde^stpjkfddbaa^eeeŸŸŸŽŽŽijljkm^_b]^aefi„…‡LMOijlrrtkkkžžž‹‹——šjimfel…„}}ˆfivUWiPQfPSkOTmDGd;B\@EZAGZ@E\?D]?B_@E^BH[SWey}‡yy‚mmwww‚……‘Ž……‘ššŒ•ŸžŸž¨‚‹”“œ³²ğš—¤Œ‹”¤£¨ğğĊĊÇĠĠ×ÏÏÒĈĈÈÔÔÖÈÈËßßâğğÍÍÏÇÇÉÈÈËÌÌÎËËÍĈĈÈÂÂĊÎÎÇĈËĴĞ°ĥ´ğĞŞħşıÂÎÎ×ÁÌ{€‹‚‰ŸĴħóôĉúúĉŝúċ˙úċ˙úäŝùŭĝŭĝâû÷âû÷âû÷âû÷ßù÷ßù÷ßù÷ßù÷ŜĝöŜĝöŜĝöŜĝöÜöóÜöóÜöóÜöóÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòÙòÙò×ñïUVQ[\Wfhclmffha^_Xef_cd][\U^_[^_[Z[ViifccaŽŽŒttrceddfe]_^TVUehf{}bcekmlsssiiilll‘‘‘}}}vvxxxz{z‰ˆ€‡ddpVXhTUjTVoINh@E^AGZAGZ@EZ?D[?B_@E^>BWBFWkmz€€‰llvxx„‡‡”jiytt‚‚Ž‰ˆ–•œŸž¨¨Ĥ°zy‚ˆ†“ŽŒš…‚”“—¤¤Ĥ··ıÄÄĈÎÎÎÎÓ××ÚÔÔÖÛÛŬÏÏÒÎÎÏÏÒÌÌÎËËÍÍÍÏĊĊÇÈÈËÂÁĈ½Ä´³ş·ĥ½²ħşÒÚÒÒŬŒš„‰~ˆŽŜïêŭŭê˙ŝŭĝċ˙úäŝùŭĝŭĝâû÷âû÷âû÷âû÷ßù÷áĝ÷áĝ÷áĝ÷ß÷öß÷öß÷öß÷öŬôóŬôóŬôóŬôóŬôóŬôóŬôóÜóòÛòñÛòñÛòñÛòñÚñÚñÚñÙïOOM[[Xiif…†klhefbcd]cd]NOJTTQUUSQQOppmppmXXVddbjjhbb_[[XWWUffdjjjjjjffdhheddbaa^^^\iifxxxqqqttrmmmomr__iPO_LMbGIbAD\AEVLOaNQf?AZAD\?AZ?BWBFZacr††‘}}ˆkkx‰‰–ˆˆ•ddq‹ŽŽ—‡‡˜˜˜˘ŞŞĥ‹‹—˜˜˜ŒžžĦ°°²ÀÀÂËËÍÀÀÂÏÏÒÔÔÖÈÈËÛÛŬĊĊÇÒÒÔĈĈÈÇÇÉĈĈÈÍÍÏĈĈĈËËÍÉÈϽĭĴħ¤İ³²ğĈÄÒÚ·ĥż‡‹“‘šê÷ùéûùéŝúäŭ÷ĉ˙ùċŝĝäŭ÷äŭ÷ûöûöûöú÷áĝôĝôĝôĝôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñŜóŜóŬòïŬòïŬòïŬòïÜñíÜñíÜñíSSSNNNkkissqqrm]^Zbc\ijeQQOLLIZZWMMJbb_ŽŽŒ^^\ZZWiifbb_\\Z[[Xbb_hheaa^ddbeecVVTddbaa^__]eeceecmmksrojieiii…„ˆ__kTScTVhQUfLP\UZeUXjEH\AEZ?AZ?BWBDXIHXlly††“}}‰‚‚Žİİ´¤¤°„„__kppyˆ””Ÿ••˘””ĦĦĦ­ŞŞ³”žžĦ´´···ıËËÍÉÉÌĊĊÇÎÎÇÇÉÛÛŬÍÍÏÓÓĠÄÄĈÉÉÌÓÓĠŬŬßÚÚÚĈĈÈĈĊÌÄÂÉËÉ͜ĦŻ­·ż½ÉËÇÒÖĠŜœ£•šßíìëŝûéŝùĉ˙ùĉ˙ùċŝĝäŭ÷äŭ÷ûöûöûöûöĝôĝôĝôĝôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñŜóŜóŬòïŬòïŬòïŬòïÜñíÜñíÜñíJJJMMM]][hhefhcbc^_aZfhcTTQOOM__]TTQUUS‚‚€~~{SSPbc^]^Z[\W\]Xbc^efb^_[efbiifPPN]][eechhemmkjjhddbppmbb____Ž{}‚^^hZZeX\dU[_\biZ^jFI[@DW@DXBF[HI^TSeiivzz‡ww‚–£¤Ş²³ıĊĈ̉‰•bboZZfŽ““žŽŽ—““œĥĥżĥ´ıœœž¸¸ş¸¸şÂÂĊÒÒÔÂÂĊÓÔÔÖÔÔÖÓÓĠÓÇÇÉĈĈÈÈÈ˨···ğğÄÂÇÉÈÍżÂÇĈ˛š£²ħşÍÉÔ½ĈÀÁǎ”˜Üêéċĝöéŝùäŭ÷ĉ˙ùċŝĝäŭ÷äŭ÷ûöûöûöûöĝôĝôĝôĝôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñŜóŜóŜóŬòïŬòïŬòïŬòïÜñíÜñíAAALLLUUSccabc^Z[V^_Xab]WWUXXVUUSNNLXXVeec““LLI[\WWXTWXTZ[V_a\\]X\]X_a\^^\ZZW]][eeceecmmkjjhZZWffd[]\^a_vxwˆ‰Œklo]^bZ[]X]\]bcX\dDFS@BTDEZLMbTVhLJ[aam~~‰€€‰…†ŒĦ˘ĥ·ıĈÇËĈĈ҅…‘XWhbbo‹‹”„…‹——Ħ¤Ğž˘¨¨¨ŞÀÀÂĥĥ¸ğğËËÍÌÌÎÀÀÂÌÌÎĈĈÈĈĈÈÎÎÉÉ̎Žyy{‹‹‹¨¨ŞÂÂĊÀżÄżÂĠÔÛĞŞħ“‘›ıĥÀÏÎ×Ö×Ŭ•›ŸÍÛÚèúĝê˙úè˙úĉ˙ùċŝĝäŭ÷äŭ÷ûöûöûöûöĝôĝôĝôĝôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñŜóŜóŜóŬòïŬòïŬòïŬòïÜñíÜñíHHHNNNVVTffd]^ZNOJQSL_a\WWU\\ZPPNPPN^^\cca}}zVVTXZU_aZWXQ_aZab[\]VZ[T_a\bb_]][ZZW^^\eeceecddbUUSX[ZX[Z^a_ceddddiiihheTVSTVQ[]Z^_cQQ[IIVEDTML\^]m]]jaaliir‚ˆ}~‰‹Żħ°żÁÉÉÓÎÎۇ†–QQ^qqz˜šŸ˜˜˘–~}ĊĊÇĴĴŻżżÁËËÍÀÀÂÂÂĊÀÍÍÏĈĈȸ¸ş½½żÎÎÍÍÏÓÓĠÎÎÌÌÎŻŻħÀÉÈ͸·½ğÂÏ֓‘˜ıĥÀÍÌĠÜŬÍÓ×Ġâéûùĉû÷ûöĉ˙ùċŝĝäŭ÷äŭ÷ûöûöûöûöäùöäùöäùöäùöĝôĝôĝôĝôâ÷óâ÷óâ÷óâ÷óßôñßôñßôñŜóßôñßôñŜóŜóŬòïŬòïŬòïŬòïOOOGGGQQO]][VWSMNIUVOab]]][__]__][[XVVTaa^aa^]][TUPhibUVObc\bc\]^WWXQ^_[[[X]][VVT]][eecddbddbVXWSWXX]^[\^bdcbbbccalljWXTZ[Vbd_abdTU[JJTFFQJJVWWcaajijp\]c€„z{~}~¨Ĥħ²´ÁÁËŬÍÍÚss€^^j››¤››¤˘˘Ğš˜żżÁ³³ĥĊĊdz³ĥÈÈËÂÂĊËËÍÓÓĠÒÒÔŞŞĴŸŸ˘ıığËËÍÓÓĠÔÔÖċééë··ıÄÂÇËɲħşÂÁÈŻ­´ŻĞĥÁÀÉÎÏĠÙŜÛéèèúĝĉû÷äŭ÷ĉ˙ùċŝĝäŭ÷äŭ÷ûöûöûöûöäùöäùöäùöäùöĝôĝôĝôĝôâ÷óâ÷óâ÷óâ÷óßôñßôñßôñŜóßôñßôñŜóŜóŬòïŬòïŬòïŬòïZZZPPPQQOOOMPQMNOJWXQ^_[[[X^^\__]\\ZVVTcca^^\ZZW_a\de^XZSde^ab[_aZbc\TUPZZW]][\\Z__]cca__]aa^VXWUZ[PUWNSUcdfiik]]]cab[ZWVWSccaddd\[_WV]TSZSQVTSW]^bvwycdfqrttwvwyx›œ˜šœİŞ°ÉÉÓ××áÉ[[hxx…ˆˆ•””‰ˆ½½żŬŬߢ˘¤°°²ÀÌÌÎÂÂĊËËÍÒÒÔĤĤİ––˜½½żĈĈÈĠĠ×ÚÚÜĉĉéĉĉéÛÛÛżżÁÍÌĠżÇÍÌÓÈÇΣŸŞ·ĥżÀÁÇŬèáïíèúĝèŭĝċŝĝĉ˙ùċŝĝäŭ÷äŭ÷ûöûöûöûöäùöäùöäùöäùöĝôĝôĝôĝôâ÷óâ÷óâ÷óâ÷óßôñßôñßôñŜóßôñßôñŜóŜóŬòïŬòïŬòïŬòïXXXaa^XXVTTQOPLOPLWXQZ[V[\Weec__]bb_[[X__]XXVUVQbc\de^STMXZSde^cd]]^WUVOWXTSSP]][XXV]][XXV[[XTVSNSTGLMEIJ_acaac]]]b_a_^\VVTcca^^^VVXTTVWWZZZ\WWWZ\[fih_ba_bafihqsrŽ‚…‹Œ‘ĊĈÌÖ×ŬÒÓÙħvveeq‡‡“‡†Ž“âáċ›šž´´·ĊĊÇÍÍÏÈÈËÈÈËÒÒÔÍÍÏĉĉéÒÒÔÍÍÏÌÌÎßßâċċèññóäääÇÇÉĈĊÌÏÖÈÇÎÄÂǽıÁ­Ĵ³ĊĈÉÎÔÖßëëé÷ôêûĝêŭùéŝùċŝĝäŭ÷äŭ÷ûöûöûöċúöäùöäùöäùöäùöĝôĝôĝôĝôâ÷óâ÷óâ÷óâ÷óßôñßóóßóóŜòòßôñßôñŜóŜóŬòïŬññŬññŬññUUS\]X_a\UVQQSNIJFOPL\]XZ[V]^Z^_[]^Z]^ZWXTZ[VXZUab[de^[\UMNG^_XWXQZ[TZ[TVWSWXT_a\XZUab]QSNXZU\\ZNPMVXWGIHXXXeee^^\aa^ccaUUSeecddbZZZSSS___[[[OOO[[[hhhXXVddbjjhyyw‡‡‡€€‚Œ‹żĊÒÒÔÈÈËĥ´ı£˘Ĥ~}„^]fvs€ˆ†“Ÿž¨“‘˜ž˘ŜŜáÒÒÔÌÌÌÇÇÉĊĊÇßßâÉÉÌÖÖÙÓÒÒÔÙÙÛŜŜŜêêêêêêĠĠĠĊĊÇÈÈË××ÚÀÀÂÀÀ··ı½ÀÉÎÏËÒÒÙßŬìöòìĝôêŭùéŝùèŭöċúóéŝ÷ċúóäùòäùôööä÷÷ä÷ôċĝööòċĝôċĝôâôñâôñâôñâôòáóñáóóáòöáòöáòöáóñáóáóñáóñßòòßñôßñôßñôOPLUVQ]^ZZ[VHIEEFAMNIXZUTUP]^Z^_[WXT^_[XZUTUP]^Z\]V]^WXZSEF?WXQUVOZ[TZ[TXZU]^Z[\WZ[V^_[STOQSN]^ZSSP\\ZPPNVVTZZWQQOWWU\\ZPPNXXViif^^\LLIUUSWWUSSP^^^cca_a\cd_rsowxsttrqqs{z—–›¤¤Ĥ‚‚‚ŸŸŸıı𘗜€‰dbp}zˆ‡…‘Œ•–•œİ¨ĴÂÂĊÀÀÀÇÇÉÓÖÖÙşş½ÏÏÒÛÛŬÈÈËÉÉÌŬŬŬ×××ßßßÖÖÖÄÄÄÍÍÍÌÌÌĊĊĊÁÁÁÇÇÇ°²ħĊÇĈĊÉÈÂÇĈìñïĝôìŝúê˙úéŝ÷éŝ÷ċúöèŭĝċúóâ÷òĉù÷ċĝĝöóâôòĉùööòâöïċĝôâôñâôòáóñßòòáóóáòöáòĝáòöáóñáóñáóñáóóßòòßñôßñ÷ßñ÷IJFQSN^_[^_[JLGBD?GHDTUPSTO\]X]^ZVWS^_[XZUTUP]^ZZ[T[\U]^WGHAVWPVWP[\UXZSXZS^_XZ[TXZS\]VNOHOPI^_[]^ZZ[VQSNSTO^_[QSNSTOXZUQSNWXTjkfXZUMNIQSNZ[V[[X__]eec_a\de^fhadeappmlllihl~}rrp€€~‰ˆ£˘İecpb_lljw´³½´³şœĦħħ³––˜´´·ĠĠ×ÛÛŬÜÜŜÍÍÏÖÖÙÈÈËÓÓÓÓÜÜÜÓÓÓÍÍÍÔÔÔĈĈĈÈÈÈĈĈĈ‹ŒığşÎÓÒÁĈĊ×ÜÛôŝúìŝĝèûôċĝôċĝôéûùċĝöä÷ôĉù÷âôòöóċĝôĉùööòċĝôċĝôâôñä÷ôöóâôôâôôáóóáòöáòöáòöáóñáóñáóóáóóßòòßñôßñôßñôHIETUPbc^^_[MNI>?:EFA[\WXZU\]X]^Z\]X]^ZWXTZ[VXZU^_X^_Xbc\STM[\U[\U]^WVWPZ[T]^WZ[TUVO[\UHIBQSL_aZUVQWXTWXTQSN\]XOPLSTO]^ZPQM\]XijeXZUVWSUVQbc^^_[ccaab]fhacd]bc\de^efb___aacqqsmmmhheqqo{{~ÂÁĈ{z„\[d[Zcdcjjimˆ‡Œ¨¨Ş••—ĞĞ­ËËÍÇÇÉßßâÔÔÖĠĠ×ĠĠ×ÓÓĠÙÙÙÙÙÙßßßáááÔÔÔÍÍÍĠĠĠÌÌÌıııÉÉ̜Ÿ²³ĥ³¸ıĠÖÌÒéíûùëûúìŭûéùúéùŭéĝ˙è÷˙ċôûċ÷úä÷÷ä÷÷öóä÷ôâôòâôñä÷ôä÷ôööööâôôáóóáóóáóóáóóáóóáóóáóóáóóßòòßòòßòòßñôIJFIJFZ[V\]XLMH9:6BD?]^ZVWSUVQXZU\]X[\WZ[V^_[WXTab[\]V]^WXZSNOHTUNXZSXZSZ[Q]^UbcZ\]T\]TTULWXOZ[TSTMXZS]^WLMFZ[T[\U_aZbc\OPI]^Wde^bc\^_XWXQab[]^Z[\W^_[hibde\abX_aZ]^Zbb_VVXjjljjh_a\jkfccahhjĤŞÌËώ”•”›zy~~‚ŞŞĴ¨bbd­­°À¤¤ĤÀÀÀÂÍÍÏÓÓĠÒÒÔÜÜÜċċċßßßâââÜÜÜÔÔÔÈÈÈÁÁÁĈĈÈĊÄȝž˘°ħ´­²´ÂÇÉÛßââèêêôĝéöúĠâé·Â̗£ħ—˘´š£ıĦĞ·ĊÓ°ÀËşÌÓÍŜäâó÷áóóŬíä÷ôŬáóóáóóßòòáóóáóóáóóáóóáóóáóóáóóáóóáóóáóóáóóáóóLMHLMH\]X^_[QSN?@;EFA[\WOPLQSNZ[VVWSZ[VZ[VZ[VUVQ\]V[\U]^W\]VJLEPQJUVOUVOWXO\]TabXZ[QQSITULXZP[\SOPIWXQbc\UVOTUNWXQ[\U[\UNOHde^ab[[\U\]VUVO]^WXZS\]Xcd]Z[Q]^Ucd[]^UZ[T__]ZZZfffbb_Z[V_a\bb____‹‹žžĦĥ´ı²ħĥ‰ˆ~~€œœž¸¸şkkm‚‚…‘œœž¨¨Ş´´·²²´½½żÍÍÏÚÚÚßßßéééĉĉĉÛÛÛÌÌ̸¸şÎÍÒżÂĦ˘İŞ­Ğ°²´ığÎÓĠáĉëÏÖŬÇÎ×­´żŸĥĴħĈ˘ğ„‡¤„‹Ĥ„€Ÿ‹›¨—¨²¨ıÀĈ×ŬÜíñŜóäöùâó÷ßñôŜññáóóáóóáóñáóñáòöáòöáòöáóóáóóáóóáóñáóñVWSQSN[\WZ[V[\WGHDHIEXZUNOJSTOZ[VXZUZ[VVWSXZUOPLWXQXZSWXQXZSGHAMNGPQJQSLSTJ[\S_aWUVMMNEQSIUVMZ[Q\]VQSLXZSXZSVWPZ[TXZSUVOOPIde^^_XWXQ\]VTUN[\UUVOZ[VZ[T]^U_bU^aTbcZUVOaa^\\\bbb\\ZXZUbc^iif^^^iiikkk´´·ĦĦ£‡‡‰œœž¨jjl‡‡‰ÄÄĈ””–††ˆ••—ıığıığ„‘‘‘ÒÒÒÔÔÔÙÙÙċċċÙÙÙĈĈÈÂÁĈÎÍԚ›ĦĴ­³°³ıİŻğżĊŜâèëñóÖÜáÇÌÖ¸½Ëßĝħ³ÎŽ­„‹¨~‹˘z‰›}ŒŽŸŽž­Ħ­˘²żÎÙÙêñôúáòöŬáóóáóñáóñáóñáòöáòĝáòöáóóáóóáóñáóñáóWXTTUP]^Z\]Xab]MNIJLGWXTIJFPQMNOJ[\W[\WPQMZ[VIJFUVOVWPOPIPQMDE@IJFNOJNOJPQJZ[Q^_VSTJOPGOPGNOFXZP[\UNOHUVOVWPIJDSTMWXQUVOPQJ\]V_aZZ[T^_XUVO[\UTUP\]XZ[V_aWcd[WXO\]Tab]^^\]][aa^\]X^_[dealmieecbbbTTQddb———˘˘˘}}}ĞĞĞ›››SSUyy{ğğ……‡ppr€€‚À˜˜›†††pppĥĥĥäääíííĉĉĉâââŬŬŬÏÏÏÄÂÇÄÂÇÁÂĈŽ“ħ²ĥ—˜œ¨İĴÒÖÙèìïÓ×ÚğżÇ½Á͸ğϜž·€‚Ÿ…˘}†ž{ˆ{ˆy†›x…š}‰žˆ•Ş•£´ĦŻ½ËÜáòĝáòöáóóáóñáóáóáóóáòöáòöáóóáóóáóñáóñáóñSTOVWSWXTUVQUVQVWSPQMXZUGHDJLGOPLab]LMH[\WPQMIJFQSNMNISTOTTQIIGNNLLLLMMJSTOSTMUVOUVOQSLWXQSTMVWPVWPPQJOPISTMGHAQSLPQJSTMUVMUVM\]V^_XXZUUVQUVQMMJTTQZ[V]^W_aZab[[\UXZU^^\[[X\\Zdeade^ab[lmiffd__]UVQZ[Tjkf}~yool‚‚€iifXXXlllĦĦĦŒŒŒ•••žžž~~~hhhddb““¸¸ĥÎÎÌċċĠĠÓÖÖÔŻŻĴÇÇÇşş½Óħħ³İİĞŸŸ˘ŸĤİÍÎÒĉèíŻŻ¸İи›ŻŽ†‡Ÿ„†Ħˆ¤”˜­Ÿ¤ğ¤İÀ“—Ż‰Ž¨„t{•…˘¤²ÂĠċáòĝŜòòßôßôíĝñâôñóòóòóôâòóâòöâòöâòöJLGPQMVWSVWS[\WXZUNOJSTOGHDIJFUVQ_a\LMHWXTNOJEFAQSNLMHNNLUUSLLLLLLGGIJJJOPLPQJTUNTUNSTMMNGZ[T[\UVWPPQJOPISTMNOHLMFSTMOPIUVMUVMXZS]^WVWSTUPOOMHHFPPNXXV]^Zab[cd]^_[XXV]][^^\__]^_[^_XUVO^_[XXVTUPQSLVWNcd]mohbc^xytffd\\Z___qqq‡‡‡†††vvvmmm\\\^^\qrmĤ¨£‰‹†×ÙÔĞĴ¨ÄĊÀ‹ÌÌÌ···ÎÎÎÛÛÛ½½½şşşŞŞĴŬÜÒÚÉĴĞğ”–¨‡ˆˆ‰˘‰‹£…‡Ÿ†ˆĦ“Ğ›ĥ˘¤½–˜ħŽİ„ˆŸy‚˜‡•Ĥ£ħżĠĉíŜóÛìáöïßôíâôñóòóòóôâòóâòöâñĝâñĝFGBNOJTUPVWSVWSTUPJLGOPLPQMLMHZ[VTUPJLGVWSVWSNOJOPLMNIMMJSSPPPPGGG@@BGGGIJFQSLMNGUVONOHWXQ]^W^_XVWPPQJOPISTMUVOHIBOPINOHUVMUVMVWN[\UQSLUVQQSNPQMOOMVVTWXTZ[Tef_deaZZW^^\[[Xaa^XZU^_X[\U]^ZPPNJLGUVO\]Tab[bc\TUPefb[[XQQO\\Z__]eeeqqqxxxiiiqqsVVV\\Zffdlljyywĥĥ³{{y——•rrp´´´³³³ĈĈĈÁÁÁÈÈÈĴĴĴ½½½ğğâáèÇĊÒÍÌܖ•¨‘“İ€˜{{—„„˘€‚Ÿwy–~€›‘Ş£Ĥğ²ĥɟ£´–İ€ˆœ{†˜‚ĦĴğĈÚëñĉùùĝôáöñâôòóòóôóôâòóâòöâòöâòö@@>FFDJJHJJHVVTPPNBB@DE@QSNJLG[\WTUPJLGTUPUVQIJFIJFPQMNNLOOMSSSEEE::=EEEHIEHIEQSNQSNNOJQSNTUPZ[VVWSPQMOPLSTOXZUIJFJLGNOHJLBTULXZP]^WNOHSTOMNIJLGPPNPPNOPLPQJ]^Wbc^XXVVVT\\ZccaPQMVWPWXQWXTJJHGHDVWP]^U^_XXZSPQMcd_bb_]][TTQ\\Z___hhh[[[TTV^^aLLNNNNbb_ddbiif‚‚€eecmmk]][ŸŸŸ³³³şşş………{{{žžžÂÂĊÙ×ÜĈĊÎĈĈÒ°ŻżÖ×ìżÀٕ•ħ††¤wx—xy˜}œ€‚›‹Ž£”—ݝ˘°šŸ­•İt‘w“Œ›ÏŜĉÜíóßñôä÷÷óôóôóôóôâòóâòóâòóâòö>>>AAAMMMFFFHHHJJJAAA@@>PQMPQMXZUMNI@A=HIENOJEFAGHDFGBFFDGGENNNAAA99;>>>DDA@@>LLIMMJLLIJJHNNLOOMUUSMMJIIGPPN]][SSPJJHMNISTJWXO^_X^_XPQMEFAHHFIIGLLIPPNVWSSTM^_X]^ZSSPTTQUUS]][UVQUVOZ[TUVQHHFMNIWXQ_aWab[]^WUVQ\]XZZWPPNUVQXXVQQO[[[UUUMMOONSGGIPPPZZZZZZ\\\TTT[[[\\\iii‹‹‹Â–––°°°sss€€€ˆˆˆžžž½½şÉÉÇÀÁĊ••žĊÇÖÒĉ˜š²‡‡{}œxy˜xz•}—‚†š˜œ­šžŞ¨­ğ˘Ş½‹“Ĥr}ŽsŽ¨·ĈÜíôÛìòó÷äôöóôäôöóôâòóâòóâòó666;;;GGG>>>DDDAAA>>>AAAMMJJLGPQMNOJBD?HIELMHDE@DE@EFAEEBDDASSSFFF88:::=;;;@@>??=IIGGGELLIGGEFFDOOMLLIFFDQQOXXVSSPNNLHIELMFSTJ\]V^_XWXTGHDFFDDDAGGEJJHQSNMNG[\U]^ZSSPOOMVVTXXVUVQPQJVWPLMHIIGSTO^_Xfh^fhabc\VWSQSNSSPXXVNOJTUPWWUPPPQQTGGIHGLA@ENNP[[[UUU___WWWSSS]]]ZZZbbbœœœ~~~jjjlllooo‚‚‚‹‹‹ħ²­ÇÈÄŞĴĞxyĤŞ´}Ž‚—xy‘rty{–{~–wz…˜˘šžĴ£¨ĥ˘¨ş˜Ĵrzlwˆr}ŽŒšŞ²ÀÎóŝâñĝó÷äôĝâòóóôóôâòñâòñ>>@::=FFHFFHJJMHHJBBE999MMJIJFGHDDE@BD?FGBLMHGHDBD?FGBFFDBB@TTTJJJ88:99;888777>>>BBBLLLEEEGGGGGGJJJFFFGGGQQQQQQUUUOOODDAMNGPQHVWPVWPWXTIJFLLILLINNLLLIMNIHIB^_Xfhc\\ZTTQWWU[[XZ[VUVOWXQOPLHHFTUPbc\ijahibab[XZUFGBJJHXXVOPLXZUUUSNNNSSUPPSQPUDBGJJMUUWPPS^^aJJMHHJPPSbbd^^acceXX[VVX[[]ddf~~€‚‚…ŻŻŻ–––£¤Ĥmot˜z~ˆoq~qs‚ps‡{”{”rv‹tx„‡œ‚†›Ž‘Ĥ—œħ‘Ĥiq…fo‚s~ˆ“—·ÄÒâÜëóâñĝâòöâòóóôóòóòâó???>>>IIIGGI??A;;>AADAAAEEBHIEEFA?@;?@;BD?LMHMNIDE@IJFIIGDDAQQQNNN:::;;;777666===AAAJJJNNNMMMNNNGGG>>>JJJPPPMMMVVVLLLFFDGHDMNGUVOVWP]^ZOPLQQOQQOJJHEEBHIEFGBUVQbc^VVTJJHOOMZ[VVWSXZUQSNXZULLI]][^_Xde\cd]\]Vab]NOJNNLSSP\]XXZUXXVJJHQQQPPSUTXLJOFEIIHMNMQZZ\PPSMMOOOQ[[][[]]]___bWWZTTViikŽŽooq‚‚…yy{ˆ‰fhkz~„kotfjrilwoq~vx‡rt„fizwx„…›~•„†žˆ‹£…‰Ħvz‰‘¤qypyˆ‘ޘşĊÔŜÛêñßóßñâòóóòóòóò480794BEALNM>?A236687FHE@@>FFDFFD>>;GGEAA?FFDHHFDDALLIJJHJJHVVT]][TTQFFD442997;;9@@>DDALLIJJHBB@BEAEGFILJEFHFGINPO@BAGIFDDAIIGUVQ[\WOPLLMH997???GGEBB@FFDEEB]][[[XQQONNLLMHSTONOJWWUSSPMMMIIIUUSef_de\ab[ef_cd_TUPMMJNNLWWU\\Z[[XTUPMMJNNLUUUSSUDEJEFLFGMVW[MNQIJNIJMUVX[[[QQQUUUVVVOOO]]]eeeeeeefi^_bijlabdcdhopshilefjdekqqzssjjwrq‹‰œ˜—Ĵ‡ˆž„„ŸoqŒcfzw}{”s{‘t{˜~‡˘›ĴÌÙßßìïâïäôöáñòööáóóAE:AE=EGBDFE9:=013687ILHEEBLLIGGE886663@@>GGEEEB??=MMJPPNQQOaa^bb_XXVTTQ??=@@>;;9DDAFFDLLIJJHAA?JMIEGFGHJFGIDEGIJMJMLFHEFFFGGGVVTXZUQSNTTQ???;;;FFD@@>FFDNNLiif__]SSPSSPMNIOPLOOMJJHQQQFFFDDFBBBVWPab[ab[TUP\]XVVTMMJMMJSSSWWWPPNXZUIJFNNLNNNOOQFGMDDMGHNTU[NOSEFIEFHVWZSSSMMMTTTXXXQQQVVV[[[WWW^_bUVX^_b]^a]^abcebce]^abcfdei{}‚ffpvv‚ŒœĞ޽Œ‰Ħ‰‰ij‚advsx†£Ĥş{€—tx–t{—€ˆ›¤ÛèêäòñóôäôöÛííä÷÷BF;=@8>@;>@?78:-/1243BEAMMJOOMFFD220220??=EEBGGE@@>IIGJJHLLIUUSbb_ccaaa^OOMEEB997BB@DDALLIJJHBB@DFB>@??@B?@B:;>@ADNPOILHLLLNNNZZW\]XUVQOOMPPP@@@LLIMMJIIGJJHbb___]TTQLLIUVQMNIOOMNNLNNN===::=???PQMXZS[\WGHD\\ZLLIFFFIIIQQQWWWLLIXZUJLGOOMOOOJJMGHL@AGEFLNOSOPTFGIFGIWZXMMMHHHNNNTTTVVVUUUSSSWWWTUWUVX\]_VWZbce]^a]^abcebcfdeibci^^hhhsllyzy‰‹‰žˆ‰˘qrˆikzšœİĴ°Á–šŻqsrwr{‹v€ˆĊÒÔâïáñôó÷ċö÷ßñTWMNQI?A=@BA@AD347243?A>NNLIIG??=774>>;;;9==:JJHGGEHHFHHFQQOJJHQQOSSPXXV]][HHF774AA???=IIGMMJGGE:=9;>=@AD=>@;=?;=?EGFDFBGGGOOOOOMXZUWXTJJHJJJ???LLIHHFAA?GGEZZWTTQJJHJJHHIEHIEHHFGGE@@@;;;88:AAA\]XXZUXZUUUSLLI???IIIGGGJJJQQQQQO_a\QSNQQOSSSFFH@AE;=BBDGGHLPQTMNPMONX[ZJJJEEEIIINNNIIITTTTTTVVVOPSNOQTUWVWZXZ\_acXZ\[\^^_cefjfhmQSXeeoddpxx…†…•‘“¨z}Ž_bomq{ž—˜­~—txqw…w‰ŸŞ­Üéëäñĝâñĝó÷áñò[^TOSJ8:68:9@AD124364>@=LLIJJHAA?::8HHF;;9??=QQOBB@??=QQOSSPNNLOOMOOMNNLZZWEEB::8EEBEEBFFDLLIPPN9;80219:=46801389;>@?@B?FFFQQQSSPNOJXZUSSP>>>BBBOOMLLIGGEFFDOOMHHFBB@BB@EFA?@;AA?DDADDD???::=EEEPPNPQM[[XAA?>>>:::FFHPPSFFFMMMNNLXZUSTOMMJSSSIIL?@D:;?GHLSTVIJMMONNPOUWTOOONNNFFFMMMHHHQQQOOOQQQLMONOQOPSMNPXZ\]^aVWZPQTVWZVW[^_c]^b[\b_afffpmmyz}‰acp[^feiqmp}‰ˆ˜{zoq‚lq{ipw‡“ĊÏĠáëóŜëòßìñéöúTWMLOGDFA;>=@AD679132@B?LLIQQOGGE774MMJEEBAA?NNLEEBAA?PPNLLIWWULLIFFDIIGVVTAA?442??=BB@IIGJJHTTQ>@=79889;/02124>?A?A@GIFGGGEEEHHFQSN[\W^^\LLLLLLLLIIIGJJHEEBIIGIIGAA?GGE@A=DE@HHF@@>BBBHHHEEGBBBJJHMMJZZWBBB@@@NNPGGIMMODDDAAA>>;NOJXZUMMJPPPIIIBDFBDGNOQZ[]LNMLNMJMIQTPVVVQQQEEELLLHHHNNNSSSVVVPQTOPSPQTFGITUW[\^UVXTUWQSUTUW\]_^_cOPTVW[cdhklrdhpSV^W\^efjss}€€vt…oo{eiqekpv~ĥ½ÄÜì¸ÁÌâìòâïóMPFMPHNPL=?>=>@78:8:9LNJGGENNLHHF>>;PPNNNL>>;JJHBB@@@>MMJFFDUUSTTQNNLHHFWWU@@>331;;9DDAQQOIIGUUSFHE;>=89;34789;@AD:=;>@=HHH>>>HHF]^Zab]\\ZZZZLLLLLIMMJHHF@@>DDAEEB774IIG@A=;=8FFDBB@DDDJJJNNPGGGGGELLI[[[EEENNPQQT@?DBBE;;;>>>??=QSN^_[LLIJJJIIIGHJHILMNPUWVNPOHJGEGDMOLSSSVVVLLLOOOMMMJJJWWWWWWVWZMNPIJMIJMPQTPQTZ[]WX[LMONOQ[\^_acUVXUVX^_bbceX]_TX[dehdehdcjvt~wtjirbfibhjjpt£İ°żĈ”žÌÖÜÙĉIMBSVNPSN=?>=>@1249;:HJGMMJFFD??=@@>AA?UUS>>;TTQFFDGGEUUSMMJLLIOOMOOMLLI\\ZEEB663>>;FFD[[XFFDNNLBEA8:92364682369;:HJGILHDDDFFDIJFJLG[\WPQMJJH??=GGETTQJJH886>>;MMJ;;9HHFFGB?@;NNLIIGFFFSSSUUU???GGGMMM^^^JJMIILEDHDBG;:?88:>>>==:HIE^_[GGEFFFHHJBDFFGIGHJSUTOQPGIF@B?ILHGGEXXXWWWVVVSSSHHHXXXUUUQTSQSUGHJMNPMNPNOQXZ\\]_QSUPQTVXWX[Z[]\UWV^a_abd^cdPUVacbbdcbaehfmmlsbah^cdejkilrv{‚‚‡“}„ŽğÂÉÙĉGH?PQJWXTMMM@@B//1BBBBB@JMH:=8362798>@?TVSFHDTVQDE@MNIVWSOPLNOJJLGMNIHIEQQOOOM9:6FGBVWSab]GHDLMHJJJ>>@224224DDDIII??=DDALLIHHFMNINOJVWP^_XSTMHIEJJHMMJDDA442::8886886EEBNNL@@>EEBTTQAA?LLIXXVIIIOOOEEETTVPPSVUZFEIBAH:9>88:???@@>JJH]][AA????JJMABFFGJHIMFGIXZ\MONILJMOLIIGTTQ^^\MMJTTQPPNNNLTTQSUTQTSNPOLNMILJILJQTS\^][\^UVXUVXVWZ^_b[\^[\^]^a\]__ac\]_^_babd^_c^_c_ac^_bijlhioeiq_dp^co€‡Ž•œ£@A8MNGOPLIIIDDF113:::;;9PSNAD?3644768:9NPO@B>OSJNOJLMHOPLLMHPQMFGBLMHAB>UUS[[XGHD?@;HIBZ[THIBJLGFFH327224BBELLLPPPAA???=??=??=HHFNOJPQM\]VUVONOHJLGPPNNNL;;9442663774GGEPPNBB@AA?QQOIIGEEBPPNSSPWWWIIIGGI??AFEI@?D=;B98=779??ADDDZZWaa^LLL@@BJJM9:>?@DDEHFGJOPSGHJGIHJMLJJHOPLZ[VUVQXZUVWSQSNVVTPSQX[ZWZXLNMHJIFHGJMLUWV\]_TUWQSUTUWZ[]WX[STVXZ\[\^XZ\[\^Z[]^_bQSUVWZ\]_bbbcce_afadlX\fX]hsy€}„‹GH?LMFTUPFFFQQT113111;;9MOJAD@/10/02468JML=?;JMHNOJIJFOPLMNIGHD?@;IJFEFAFFDUUSWXTPQMNOHWXQJLEQSNLLL99;,,/;;;UUUUUSNNL@@>@@>>>>EEBGGELMHVWSNOHGHABD?MMJPPNHHF886220997@@>SSPDDA==:NNLJJHFFDPPNTTQWWUJJJNNNHHJ@@B;:??>BIHM@@B??AMMMZZWZZWQQQ;;>GGI:;?;=@?@DBDGMNPHILGIHHJIMMJNNLUUSZZWVVTUUSPPNTTQQTSWZXVXWOQPHJIGIHGIHOQPXZ\QSUNOQVWZXZ\Z[]PQTWX[XZ\VWZXZ\WX[]^aPQTQSU]^a_acWX[TU[[^fSV^LOW_ej_ejLMDQSLNOJ@@@SSU>>@777;;9LNJFHE236347679LMO>@=MOLPPNIIGOOMEEBBB@AA?FFDDDAMMJVVTVWSFGBFG@VWPQSLVWSFFF447;;;777LLITTQHIEEFADDDAAAGGGIIGFFDTUPQSNMNIMMJUUSMMJPPN==:++)997??=TTQDDA>>;LLILLIIIGNNLPPN\\ZIIGUUULLLWWZMMOFEI87;668==?HHHQQO\\ZJJJMMOHHJ89=9:>@AEHIMIJMEFHADB?A@OOOQQQSSSVVVNNNQQQNNNPPPSUTLNMMONSUTNPOILJHJIMONWX[TUWJLNMNPTUWTUWIJMTUWTUWWX[TUWVWZUVXSTVJLNXZ\WX[OTVIMSPTZQU]OSXZ^aW\^VWNUVOMNIDDDOOQTTVHHH::8>@==?>*+-&(+67:@AD@BAEGFFFF>>>FFFFFFMMMPPPPPPMMJAA?QQOWXTHIEIJDNOHTUNPQMJJHGGGTTTFFDIIGVWSSTO>?:888668>>>AAA>>;HHFPPNSSPSSPMMJNNLQQOIIG774442??=VVTOOMEEBFFDUUSMMJIIGMMJXZUXXVWWULLLTTTLLN--0336113>>@HHHUUS\\ZOOOLLN??A67::;?=>AGHLDEGFGI=?>EGFLLLTTVLLNOOQLLNNNPGGIQQQSUTOQPQTSOQPWZXPSQOQPZ\[VWZUVXGHJLMOPQTXZ\FGIHILPQTQSUTUWWX[STVZ[]MNPPQTMQSPUWJNTBFLLOUVZ_TXZTUWWXOLMFHIE???DDFGGI>>>11/79878:)*-*+/237469468:=;DDD======AAADDD@@@@@@DDDEEBUUSVWS?@;@A:FG@VWP[\WQQO[[[SSPEEBNOJQSNOPI894AAA>>@>>@======JJHPPNJJHIIGMMJHHFNNLOOM@@>AA?BB@UUSVVTNNLHHFSSPIIGMMJJJHUVQSTOLLIQQONNNOOO;;>IIL88:;;>QQQOOM[[X\\\BBEDDF?@D?@D;=@>?BDEGDEG9;:;>=EEGPPSJJMGGIGGIJJMFFHLLNJMLMONQTSPSQSUTSUTUWVTVUOPSNOQEFHIJMIJMPQTBDFFGILMOPQTSTVTUWMNPVWZTUWOTUFMMPVXPTZEHNIMSTUXQTSQTSUVMPQJEFABBB??AEEGBBB886243236(),/0634:67:78:=?>FFH::=::=??AAAD::=::=AAATTQVVTWXTMNIJLEFG@NOHOPLTTQ^^\]][GHDMNIQSLEF?9:6IILGFJ??A::=999HHHMMJGGEGGEJJHGGENNLSSPGGE??=;;9OOMWWUTTQGGEIIGJJHNNLFFDQSNLMHMNILLINNLNNNFFF@@@GGIMMOUUUGGEXXVNNNAAD88:78;9:>?@D@AE:;>>?A?A@@BAEEGNNPLLNFFHEEGGGIEEGEEGFHGMONOQPSUTTVUQTSVXWOQPTUWNOQEFHEFHBDFMNPHILNOQHILJLNPQTQSUHILPQTWX[MQSAGIMSUQW\IMSMQTTUWTTQVVTJLEIJDHIE??=BBBQQQIII444-/1236+,0/03-/29:>;=??A@JJM447@@B@@B>>@::=::=>>@@@@UUS[\WGHD?@;BD?OPLWXTPPN\\Z\\ZLMHGHDLMHDE@==:LLNMMOAAD==?777AAAJJHLLIHHFBB@>>;@@>@@>BB@>>>999HHHPPPMMJFFDJJHFFDFFDLLIMNINOJJJHIIGMMJ[[[XXXUUULLNPPSQQQDDAUUSPPP99;44778;=>AIJNIJM9:=?@BDEGBDFBBEEEGFFHBBBBBBEEGFFHAADEGFNPONPOSUTPSQILJVXWVXWUVXOPSJLNJLNEFHJLNFGIFGIFGIHILLMOPQTHILMNPWX[GLM=BEJPSOUWJOQLPQPSQNPMTTQNOJOPLPQMGHDDE@GGEBBE87;:;A01778;013124468:;>>?AEEG779779AADIIL99;336::=HHHZZWUUSEEB>>;FFDLLI[[XPPNZZZ\\\PPNBB@MMJBB@BBBDFEILJ;>=FHG=?>8:9MONFHGLNM@B?9;8=?;?A>;>=78:348=>AGHJHJIILHLNIJMIHJGFHGHJINPOFHGFHGFHGLMO?@B:;>ABEIJMEGF?A>TVSOQP@AD89;/039:>FGIUWV=?>;=?:;>?@D=>AEFHSUTOQNEGD=?>@AD?@BLNMNPOPSQQTSQTSJMLNPOX[ZTUWPQTIJMNOQJLNGHJGHJIJMDFEGIHGIHMONFHGLNMVXWMNPINOHMNMQSLPQHMNINOMQPQTSPPNMMJLMHDE>;=8AA?>>@32911:/06;=@6796879;:9:=:;>>>@224779??A==?224//1668===IIGVVTNNL>>;EEBPPNTTQZZZXXX\\\IIIAAAGGGFFF@@@?A@BEDDFEBED:=;8:9JMLEGFILJ>@?362362:=;89;46923967=@AEDFEEGDGIEFHEEGFDFEJLNNOQDEGABEFGIQSUDEG9:=BDFABEDFE@B?OQNTVUIJMABE2379:>EFHVXWEGF;=?78;:;?>?BIJMTVSTVQJMHBEADEG?@DGHJHJILNMQTSMONHJILNMTVUWX[OPSIJMMNPPQTJLNEFHABEGIHHJINPOLNMJMLSUTTVUMONFJLEIJINOHMNEIJGLMINOLPQIIGQQOTUPFG@9:6??===?329,,6-/4?@D?@B243=?>;=?>?A?@D12667:78;-/2+,0126;=?>>>HHFUUSQQOAA?AA?JJHUUSUUUZZZWWWPPP@@@JJJEEEEEEADB@BAHJIADB9;:9;:HJIEGFMONDFE9;8796=?>9:=46912889??@D=?>;>:?A=BEAFHGILJGHJJLN?@B?@B?@BQSUNOQLMO>?AEFHILJ?A>NPMSUTHIL@AD:;?ABFGHJVXWSUT>?A78;89=469GHJQTPWZUOQMILHEFH>?BJLNHILFGIGHJEFHBDFFGILMONOQIJMGHJEFHJLNJLNHILEFHEGFFHGJMLEGFBEDMONQTSNPOHMNGLMLPQJOPBGHDHIGLMINOQQOUUSUVQGHA=>9BB@AAD98?//8+,2>?BEFH687ADB:;>:;>>?B126:;?:;?237014348:;>???MMJLLIDDA>>;FFDHHFQQOWWWUUUZZZPPPEEEIIINNNGGG@BA8:9=?>:=;7989;:FHGDFEHJIDFE=?;9;8DFE>?A469/0667=>?B>@?=?;FHDFHEFHGFHG>?AFGI?@BBDF;=?EFH;=?9:=78:?@B=?>=?>PSQLNM=>@67:67:ABFDEGJML\^]GHJ>?B89=:;?MNPPSOTVQOQMILHGHJABFHILGHJEFHEFH?@B?@BBDFFGIHILMNPNOQ?@BIJMJLNIJMEFHFHGNPOJMLFHGILJMONVXWLNMEIJDHIHMNGLM?DEAFGDHIFJLSSPWWUWXTPQJ=>9??=DDFA@GEEN12878;347798@BA@AD4687;>&*037=14:-17*-3*-3-/2:::NNLQQOIIG??=AA?GGELLINNNUUUWWWNNNAAAHHHQQQGGGEGF?A@9;:ADB8:99;:DFE@BAEGFBED@B?=?;GIH9:=126-/478>;=@ADB>@=BE@?A>:=;798>?A;=?67978:67989;78:3479:>:;?9:=>?AGHJPQTFGJ46989=?@DEFHEGF\^]LMO=>A12667:FGIQTPX[V^a\JMIHIL;=@?@D>?B?@D?@D;=@:;?@AEHIMGHJ?@BJLN9:=ABELMODEG=>@HJIOQPJMLFHGFHGEGFNPOHJIAFG@EFDHIAFG>BD;@ABGHGLMOOMUUSUVQOPI894886==?>=D88A/0689=6799;:ADB>?A12637=$(-,06(+1*-3(+1*-3014;;;LLINNLHHF@@>==:??=HHFJJJUUUTTTQQQ===GGGMMMIII@BA=?>9;:HJI?A@>@?DFEBEDADBBEDEGDGIFILJ;=?46912834:78;>@?>@=GIEEGDBEDADB>?A9:=12434767989;89;67:78>;=B89=89;DEGNOSDEJ12878;=>AABEDFE[]\PQTBDG9:>FGJMNPSUQWZU]_[PSOLMO?@D;=@:;??@D>?B78;9:>ABFIJNMNPGHJPQT@ADGHJIJMGHJGHJPSQOQPNPOEGFGIHEGFMONMONFJLDHIFJLDHI;@A;@ABGHEIJSSPUUSPQMGHA340997;;>218++4,-378;679364>@?>?A469-17&*2/2:,08%)1%)1*-6239PPPQQOBB@442==:??=;;9==:IIIOOOUUUSSS;;;AAAIIIDDD@BA=?>>@?JMLEGF?A@?A@?A@9;:?A@GIFLNJILJ;=?469128/060149;::=9FHDBEA=?>8:99:=67901334746878:78:46934:89?46967:HIMIJN?@F67=34889=?@BDFESUTPQTDEH>?BGHLIJMOQNSUPZ\WUWTOPS?@D9:@9:@>?E:;A78>89?=>D@AEABEABENOQEFHEFHFGIJLNLMOGIHGIHMONJMLGIHADBFHGHJIBGH?DE@EF=AB9>?;@ABGHBGHNNLQSNOPIFG@:;7::8779,+2%&,,-3469124687>?A;=?014),4#&/&*2"%-"%-#&,*-3348DFELLIFFD??=886DDALLIGGEBBBNNNPPNTTQ;;9IIG@@@>>>9;:@BAGIHPSQOQPEGFDFE?A@=?>BEDLNJQTPGIH9:=126-/467=237:=;=?;@B?=?;9;:78:89=67:01434812634834812622;33=12878;IJNNOSDEJ78>12889==>@ABESTVVWZJLNFGJNOSHILILHJMHQTOTVSMON;=?9:>67=:;A9:@12878>>?EABH>?B?@BHILABE89;?@BJLNIJMFHGJMLLNMQTSNPOADBFHGIJMDHI?DE?DE;@A7;=;@ABGH@EFPQMMNGOPISTMMNIBBB=;@43:)*0()/78;3474689:=78;/06**6%%1$$-%%/()/126236013>@?BED@BA9;:;>=ADBFHGLNMPPPHHFQSNQSNHIELMHHHFBBB78:@ADFHEMOLLNJJMIIJM@AD>?ADFEOQNNPMADB78:34:,,612812689;9;::=;89;78;78>14:14=03;/2=-1;69A26>03;009009/0678;GHJJLO@AG34:1289:@?@D=>AFGJVWZEFHEFH@@BLLNFFFIIGUUSUUSNNLGGGEFH89=34:67=11:88A66?88A?@DEFHHILBDF=>@=>@GHJEFHEFHBDFIJMNOQGHJGHJEFHHIL:;?=>A>?B:;?9:>9:>=>A:;?PQJIJDZ[TMNIDDA===76:218*+1,-3:;?236-/1348128((1))4%%2%%1&&0&(--/2468687?A@:=;798798?A@?A@BEDADB???GGGJLGOPI892>?:???;;>89=ABEFHENPLQTOGIFFGIFGJBDFABEGIHNPMJML=>@239,,634:67=9:>236?@B?@D;=B46;37?,0:+-:02?13@79F14?,08--922;017237:;>QSVFGM67=34:78>EFL=>DBDGNOSGHJ>?A??ALLNHHHFFFSSPWWUPQMEEBNOQ?@D34:239//866?11=>>G?@DDEGGHJ@AD:;>=>@DEGDEG>?A=>@HILNOQMNPFGIABEGHJDEH@AE;=@78;=>A=>A>?B=>AQSLGHAWXQOPLBB@III649107+,2128>?B013+,/126/06%%/&&2$$1%%1))2%&,*+/9:=>@?>@?9;:364243:=;BED>@?>@?GGIGGGNNLLMHAB>IIG;;;==?9:>>?A>@=DFASUP@B?;=?@AE@AD9:=:=;GIFNPO?@B017//834:89?=>A89;>?A89=34:34:26>+/9*,9/1>13@79F14?,0822>//8,-3348?@BOPT@AG23923967=DEJ;=BABFMNQHIL;=?JJMIILFFFHHHVVTTTQMNIHHFX[ZWX\89=67=34:66?11:88A=>A@AEABF:;?78;;=@?@DABF?@B>?AHILNOQMNPFGIABEGHJ?@D9:>67:469BDGEFIBDGDEHLMFQSLTUNNOJMMJ@@@327/-4()/128=>A)*,*+-/03,-3$$-%%1$$1&&2**3#$*&(+;=?BEDEGF679/02679679:;>ABE@ADDDFDDFNNLPPNOOMMMJJJMAAD>?BDEG>@=>@;NPL:=9124014@AD>?A:=;BEALNM=>@-/400967=23934812478:78;67=34:/2:-1;,/;+-:-0=68E26@03;11=11:,-3014FGISTWIJP67=-/467==>D9:@ABFQSVIJM=>@>>@@@BEEEFFFTTQVVTOPLFFDZ\[abdABF237/0612899B67=BDIEFIEFI=>A469:;?;=@@AE?@B=>@DEGHILGHJGHJEFHHILGHL=>A469126>?BBDG@AE?@DSTMIJDQSLVWSEEB@@@98=/-4()/128;=@+,/*+-)*-#$*$$-%%1##0$$0&&0&(-)*-BDFILJ236/03,-1348;=@67:78;34898===?GGGLLLAAA???336-,1+,0236AD@HJFTVQGIF124/03=>@>?AADBEGDBED89;,-3++412846;78;01378:89=89?78>,08(+6-0=02?+-:+-:+/9+/7--9,,6,-3469LMOLMPNOU:;A/0646;89?9:@@AEIJN@AD9:=AAD==?:::AAATTQ[[XLMHLLIZ\[TUWMNP=>A126/06*+19:@;=B;=B>?E46;67==>D?@F>?B;=?;=?BDFEFHIJMIJMHILFGIDEGEFH679124?@BHILHILDEG?@9@A:PQJUVQTTQFFF438(&-()/78>BDG/02)*,)*-#$*%%/##/""/$$0**3()/014GHJTUW89=/03+,0-/2/03014:;?237,+0;:?BBEHHHEEE@@B76:0/34699:=:=9?A=OQMLNJ:;>78;89;;=?>@?=?;>@?78:-/4,,646;46;469,-089;67:128-/4+/7),7,/;,/;*,9*,9*-8*-6--9,,6&(-9:>JLNJLOQSXLMS0172399:@46;BDGDEH>?A;=?HHJ99;888>>>MMJOOMIJFMNIPSOTVUUVXNOQ:;?014/0646;89?67=78>34:01789?;=B:;A9:>468=>@EFHGHJGHJEFHBDFDEGABE9:=89;=>@DEGEFH>?A:;4>?8PQJWXTMMJBBB327%$+*+1?@FJLO347,-0,-1)*0++4((3$$1**666?$%+126BDFSTV:;?-/4,-3+,2239-/4017)*00/698=HHJHHHEEE??A98=/-4:;?;=?8:79;7NPLJMI9:=67:89;@AD@BA9;88:9679/06--746;128126,-078:4699:@ABH+/7),7,/;(*7)+8)+8),7),4))4009+,278;BDFFGJFGM:;A9:@01723934:>?B@AE;=?:;>>>@::=>>>===IIGOOMSTOQSNLNJQTSNPOIJM469-/2126;=B:;A66?33=22;11:77@;;E;=B@AE9:=;=?@AD?@BDEGGHJIJMHJI=?>687798=?>ADBFHGADB>?8:;4IJDTUPIIGHHH:9>&%,+,2DEJQSV78:124237/0622;%%1""/**699B()/78;?@BQSU9:>+,2/06,-3,-39:@89?67=43::9>AADHHHBBB::=76:+*189=78:;>::=8QTOEGD-/1+,0236>?A>@?241364468017--7017,-3014/0223678;78>34:*-6*-8+-:$&3(*7(*7(+6(+3++7))2,-39:>GHJIJNOPVQSX@AG017*+146;;=@?@D89;89;??A==?777;;;FFDOOMTUPPQMLNJSUQLNMGHJ/03-/2+,0;=B??H66?22;33=33=22;33=67=78;4689:=9:=@ADEFHHILIJMDFE4763648:9@BA@BAFHGHJI \ No newline at end of file diff --git a/internal/libwebp-1.2.1/examples/unicode.h b/internal/libwebp-1.2.1/examples/unicode.h new file mode 100644 index 0000000..61199ce --- /dev/null +++ b/internal/libwebp-1.2.1/examples/unicode.h @@ -0,0 +1,102 @@ +// Copyright 2018 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. +// ----------------------------------------------------------------------------- +// +// Unicode support for Windows. The main idea is to maintain an array of Unicode +// arguments (wargv) and use it only for file paths. The regular argv is used +// for everything else. +// +// Author: Yannis Guyon (yguyon@google.com) + +#ifndef WEBP_EXAMPLES_UNICODE_H_ +#define WEBP_EXAMPLES_UNICODE_H_ + +#if defined(_WIN32) && defined(_UNICODE) + +// wchar_t is used instead of TCHAR because we only perform additional work when +// Unicode is enabled and because the output of CommandLineToArgvW() is wchar_t. + +#include +#include +#include + +// Create a wchar_t array containing Unicode parameters. +#define INIT_WARGV(ARGC, ARGV) \ + int wargc; \ + const W_CHAR** const wargv = \ + (const W_CHAR**)CommandLineToArgvW(GetCommandLineW(), &wargc); \ + do { \ + if (wargv == NULL || wargc != (ARGC)) { \ + fprintf(stderr, "Error: Unable to get Unicode arguments.\n"); \ + FREE_WARGV_AND_RETURN(-1); \ + } \ + } while (0) + +// Use this to get a Unicode argument (e.g. file path). +#define GET_WARGV(UNUSED, C) wargv[C] +// For cases where argv is shifted by one compared to wargv. +#define GET_WARGV_SHIFTED(UNUSED, C) wargv[(C) + 1] +#define GET_WARGV_OR_NULL() wargv + +// Release resources. LocalFree() is needed after CommandLineToArgvW(). +#define FREE_WARGV() LOCAL_FREE((W_CHAR** const)wargv) +#define LOCAL_FREE(WARGV) \ + do { \ + if ((WARGV) != NULL) LocalFree(WARGV); \ + } while (0) + +#define W_CHAR wchar_t // WCHAR without underscore might already be defined. +#define TO_W_CHAR(STR) (L##STR) + +#define WFOPEN(ARG, OPT) _wfopen((const W_CHAR*)ARG, TO_W_CHAR(OPT)) + +#define WPRINTF(STR, ...) wprintf(TO_W_CHAR(STR), __VA_ARGS__) +#define WFPRINTF(STDERR, STR, ...) fwprintf(STDERR, TO_W_CHAR(STR), __VA_ARGS__) + +#define WSTRLEN(FILENAME) wcslen((const W_CHAR*)FILENAME) +#define WSTRCMP(FILENAME, STR) wcscmp((const W_CHAR*)FILENAME, TO_W_CHAR(STR)) +#define WSTRRCHR(FILENAME, STR) wcsrchr((const W_CHAR*)FILENAME, TO_W_CHAR(STR)) +#define WSNPRINTF(A, B, STR, ...) _snwprintf(A, B, TO_W_CHAR(STR), __VA_ARGS__) + +#else + +// Unicode file paths work as is on Unix platforms, and no extra work is done on +// Windows either if Unicode is disabled. + +#define INIT_WARGV(ARGC, ARGV) + +#define GET_WARGV(ARGV, C) (ARGV)[C] +#define GET_WARGV_SHIFTED(ARGV, C) (ARGV)[C] +#define GET_WARGV_OR_NULL() NULL + +#define FREE_WARGV() +#define LOCAL_FREE(WARGV) + +#define W_CHAR char +#define TO_W_CHAR(STR) (STR) + +#define WFOPEN(ARG, OPT) fopen(ARG, OPT) + +#define WPRINTF(STR, ...) printf(STR, __VA_ARGS__) +#define WFPRINTF(STDERR, STR, ...) fprintf(STDERR, STR, __VA_ARGS__) + +#define WSTRLEN(FILENAME) strlen(FILENAME) +#define WSTRCMP(FILENAME, STR) strcmp(FILENAME, STR) +#define WSTRRCHR(FILENAME, STR) strrchr(FILENAME, STR) +#define WSNPRINTF(A, B, STR, ...) snprintf(A, B, STR, __VA_ARGS__) + +#endif // defined(_WIN32) && defined(_UNICODE) + +// Don't forget to free wargv before returning (e.g. from main). +#define FREE_WARGV_AND_RETURN(VALUE) \ + do { \ + FREE_WARGV(); \ + return (VALUE); \ + } while (0) + +#endif // WEBP_EXAMPLES_UNICODE_H_ diff --git a/internal/libwebp-1.2.1/examples/unicode_gif.h b/internal/libwebp-1.2.1/examples/unicode_gif.h new file mode 100644 index 0000000..afcd46b --- /dev/null +++ b/internal/libwebp-1.2.1/examples/unicode_gif.h @@ -0,0 +1,75 @@ +// Copyright 2018 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. +// ----------------------------------------------------------------------------- +// +// giflib doesn't have a Unicode DGifOpenFileName(). Let's make one. +// +// Author: Yannis Guyon (yguyon@google.com) + +#ifndef WEBP_EXAMPLES_UNICODE_GIF_H_ +#define WEBP_EXAMPLES_UNICODE_GIF_H_ + +#include "./unicode.h" +#ifdef HAVE_CONFIG_H +#include "webp/config.h" // For WEBP_HAVE_GIF +#endif + +#if defined(WEBP_HAVE_GIF) + +#ifdef _WIN32 +#include // Not standard, needed for _topen and flags. +#include +#endif + +#include +#include +#include "./gifdec.h" + +#if !defined(STDIN_FILENO) +#define STDIN_FILENO 0 +#endif + +static GifFileType* DGifOpenFileUnicode(const W_CHAR* file_name, int* error) { + if (!WSTRCMP(file_name, "-")) { +#if LOCAL_GIF_PREREQ(5, 0) + return DGifOpenFileHandle(STDIN_FILENO, error); +#else + (void)error; + return DGifOpenFileHandle(STDIN_FILENO); +#endif + } + +#if defined(_WIN32) && defined(_UNICODE) + + int file_handle = _wopen(file_name, _O_RDONLY | _O_BINARY); + if (file_handle == -1) { + if (error != NULL) *error = D_GIF_ERR_OPEN_FAILED; + return NULL; + } + +#if LOCAL_GIF_PREREQ(5, 0) + return DGifOpenFileHandle(file_handle, error); +#else + return DGifOpenFileHandle(file_handle); +#endif + +#else + +#if LOCAL_GIF_PREREQ(5, 0) + return DGifOpenFileName(file_name, error); +#else + return DGifOpenFileName(file_name); +#endif + +#endif // defined(_WIN32) && defined(_UNICODE) + // DGifCloseFile() is called later. +} + +#endif // defined(WEBP_HAVE_GIF) + +#endif // WEBP_EXAMPLES_UNICODE_GIF_H_ diff --git a/internal/libwebp-1.2.1/examples/vwebp.c b/internal/libwebp-1.2.1/examples/vwebp.c new file mode 100644 index 0000000..ef26e1b --- /dev/null +++ b/internal/libwebp-1.2.1/examples/vwebp.c @@ -0,0 +1,641 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Simple OpenGL-based WebP file viewer. +// +// Author: Skal (pascal.massimino@gmail.com) +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#if defined(__unix__) || defined(__CYGWIN__) +#define _POSIX_C_SOURCE 200112L // for setenv +#endif + +#include +#include +#include + +#if defined(WEBP_HAVE_GL) + +#if defined(HAVE_GLUT_GLUT_H) +#include +#else +#include +#ifdef FREEGLUT +#include +#endif +#endif + +#ifdef WEBP_HAVE_QCMS +#include +#endif + +#include "webp/decode.h" +#include "webp/demux.h" + +#include "../examples/example_util.h" +#include "../imageio/imageio_util.h" +#include "./unicode.h" + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + +// Unfortunate global variables. Gathered into a struct for comfort. +static struct { + int has_animation; + int has_color_profile; + int done; + int decoding_error; + int print_info; + int only_deltas; + int use_color_profile; + int draw_anim_background_color; + + int canvas_width, canvas_height; + int loop_count; + uint32_t bg_color; + + const char* file_name; + WebPData data; + WebPDecoderConfig config; + const WebPDecBuffer* pic; + WebPDemuxer* dmux; + WebPIterator curr_frame; + WebPIterator prev_frame; + WebPChunkIterator iccp; + int viewport_width, viewport_height; +} kParams; + +static void ClearPreviousPic(void) { + WebPFreeDecBuffer((WebPDecBuffer*)kParams.pic); + kParams.pic = NULL; +} + +static void ClearParams(void) { + ClearPreviousPic(); + WebPDataClear(&kParams.data); + WebPDemuxReleaseIterator(&kParams.curr_frame); + WebPDemuxReleaseIterator(&kParams.prev_frame); + WebPDemuxReleaseChunkIterator(&kParams.iccp); + WebPDemuxDelete(kParams.dmux); + kParams.dmux = NULL; +} + +// Sets the previous frame to the dimensions of the canvas and has it dispose +// to background to cause the canvas to be cleared. +static void ClearPreviousFrame(void) { + WebPIterator* const prev = &kParams.prev_frame; + prev->width = kParams.canvas_width; + prev->height = kParams.canvas_height; + prev->x_offset = prev->y_offset = 0; + prev->dispose_method = WEBP_MUX_DISPOSE_BACKGROUND; +} + +// ----------------------------------------------------------------------------- +// Color profile handling +static int ApplyColorProfile(const WebPData* const profile, + WebPDecBuffer* const rgba) { +#ifdef WEBP_HAVE_QCMS + int i, ok = 0; + uint8_t* line; + uint8_t major_revision; + qcms_profile* input_profile = NULL; + qcms_profile* output_profile = NULL; + qcms_transform* transform = NULL; + const qcms_data_type input_type = QCMS_DATA_RGBA_8; + const qcms_data_type output_type = QCMS_DATA_RGBA_8; + const qcms_intent intent = QCMS_INTENT_DEFAULT; + + if (profile == NULL || rgba == NULL) return 0; + if (profile->bytes == NULL || profile->size < 10) return 1; + major_revision = profile->bytes[8]; + + qcms_enable_iccv4(); + input_profile = qcms_profile_from_memory(profile->bytes, profile->size); + // qcms_profile_is_bogus() is broken with ICCv4. + if (input_profile == NULL || + (major_revision < 4 && qcms_profile_is_bogus(input_profile))) { + fprintf(stderr, "Color profile is bogus!\n"); + goto Error; + } + + output_profile = qcms_profile_sRGB(); + if (output_profile == NULL) { + fprintf(stderr, "Error creating output color profile!\n"); + goto Error; + } + + qcms_profile_precache_output_transform(output_profile); + transform = qcms_transform_create(input_profile, input_type, + output_profile, output_type, + intent); + if (transform == NULL) { + fprintf(stderr, "Error creating color transform!\n"); + goto Error; + } + + line = rgba->u.RGBA.rgba; + for (i = 0; i < rgba->height; ++i, line += rgba->u.RGBA.stride) { + qcms_transform_data(transform, line, line, rgba->width); + } + ok = 1; + + Error: + if (input_profile != NULL) qcms_profile_release(input_profile); + if (output_profile != NULL) qcms_profile_release(output_profile); + if (transform != NULL) qcms_transform_release(transform); + return ok; +#else + (void)profile; + (void)rgba; + return 1; +#endif // WEBP_HAVE_QCMS +} + +//------------------------------------------------------------------------------ +// File decoding + +static int Decode(void) { // Fills kParams.curr_frame + const WebPIterator* const curr = &kParams.curr_frame; + WebPDecoderConfig* const config = &kParams.config; + WebPDecBuffer* const output_buffer = &config->output; + int ok = 0; + + ClearPreviousPic(); + output_buffer->colorspace = MODE_RGBA; + ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size, + config) == VP8_STATUS_OK); + if (!ok) { + fprintf(stderr, "Decoding of frame #%d failed!\n", curr->frame_num); + } else { + kParams.pic = output_buffer; + if (kParams.use_color_profile) { + ok = ApplyColorProfile(&kParams.iccp.chunk, output_buffer); + if (!ok) { + fprintf(stderr, "Applying color profile to frame #%d failed!\n", + curr->frame_num); + } + } + } + return ok; +} + +static void decode_callback(int what) { + if (what == 0 && !kParams.done) { + int duration = 0; + if (kParams.dmux != NULL) { + WebPIterator* const curr = &kParams.curr_frame; + if (!WebPDemuxNextFrame(curr)) { + WebPDemuxReleaseIterator(curr); + if (WebPDemuxGetFrame(kParams.dmux, 1, curr)) { + --kParams.loop_count; + kParams.done = (kParams.loop_count == 0); + if (kParams.done) return; + ClearPreviousFrame(); + } else { + kParams.decoding_error = 1; + kParams.done = 1; + return; + } + } + duration = curr->duration; + // Behavior copied from Chrome, cf: + // https://cs.chromium.org/chromium/src/third_party/WebKit/Source/ + // platform/graphics/DeferredImageDecoder.cpp? + // rcl=b4c33049f096cd283f32be9a58b9a9e768227c26&l=246 + if (duration <= 10) duration = 100; + } + if (!Decode()) { + kParams.decoding_error = 1; + kParams.done = 1; + } else { + glutPostRedisplay(); + glutTimerFunc(duration, decode_callback, what); + } + } +} + +//------------------------------------------------------------------------------ +// Callbacks + +static void HandleKey(unsigned char key, int pos_x, int pos_y) { + // Note: rescaling the window or toggling some features during an animation + // generates visual artifacts. This is not fixed because refreshing the frame + // may require rendering the whole animation from start till current frame. + (void)pos_x; + (void)pos_y; + if (key == 'q' || key == 'Q' || key == 27 /* Esc */) { +#ifdef FREEGLUT + glutLeaveMainLoop(); +#else + ClearParams(); + exit(0); +#endif + } else if (key == 'c') { + if (kParams.has_color_profile && !kParams.decoding_error) { + kParams.use_color_profile = 1 - kParams.use_color_profile; + + if (kParams.has_animation) { + // Restart the completed animation to pickup the color profile change. + if (kParams.done && kParams.loop_count == 0) { + kParams.loop_count = + (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT) + 1; + kParams.done = 0; + // Start the decode loop immediately. + glutTimerFunc(0, decode_callback, 0); + } + } else { + Decode(); + glutPostRedisplay(); + } + } + } else if (key == 'b') { + kParams.draw_anim_background_color = 1 - kParams.draw_anim_background_color; + if (!kParams.has_animation) ClearPreviousFrame(); + glutPostRedisplay(); + } else if (key == 'i') { + kParams.print_info = 1 - kParams.print_info; + if (!kParams.has_animation) ClearPreviousFrame(); + glutPostRedisplay(); + } else if (key == 'd') { + kParams.only_deltas = 1 - kParams.only_deltas; + glutPostRedisplay(); + } +} + +static void HandleReshape(int width, int height) { + // Note: reshape doesn't preserve aspect ratio, and might + // be handling larger-than-screen pictures incorrectly. + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + kParams.viewport_width = width; + kParams.viewport_height = height; + if (!kParams.has_animation) ClearPreviousFrame(); +} + +static void PrintString(const char* const text) { + void* const font = GLUT_BITMAP_9_BY_15; + int i; + for (i = 0; text[i]; ++i) { + glutBitmapCharacter(font, text[i]); + } +} + +static float GetColorf(uint32_t color, int shift) { + return ((color >> shift) & 0xff) / 255.f; +} + +static void DrawCheckerBoard(void) { + const int square_size = 8; // must be a power of 2 + int x, y; + GLint viewport[4]; // x, y, width, height + + glPushMatrix(); + + glGetIntegerv(GL_VIEWPORT, viewport); + // shift to integer coordinates with (0,0) being top-left. + glOrtho(0, viewport[2], viewport[3], 0, -1, 1); + for (y = 0; y < viewport[3]; y += square_size) { + for (x = 0; x < viewport[2]; x += square_size) { + const GLubyte color = 128 + 64 * (!((x + y) & square_size)); + glColor3ub(color, color, color); + glRecti(x, y, x + square_size, y + square_size); + } + } + glPopMatrix(); +} + +static void DrawBackground(void) { + // Whole window cleared with clear color, checkerboard rendered on top of it. + glClear(GL_COLOR_BUFFER_BIT); + DrawCheckerBoard(); + + // ANIM background color rendered (blend) on top. Default is white for still + // images (without ANIM chunk). glClear() can't be used for that (no blend). + if (kParams.draw_anim_background_color) { + glPushMatrix(); + glLoadIdentity(); + glColor4f(GetColorf(kParams.bg_color, 16), // BGRA from spec + GetColorf(kParams.bg_color, 8), + GetColorf(kParams.bg_color, 0), + GetColorf(kParams.bg_color, 24)); + glRecti(-1, -1, +1, +1); + glPopMatrix(); + } +} + +// Draw background in a scissored rectangle. +static void DrawBackgroundScissored(int window_x, int window_y, int frame_w, + int frame_h) { + // Only update the requested area, not the whole canvas. + window_x = window_x * kParams.viewport_width / kParams.canvas_width; + window_y = window_y * kParams.viewport_height / kParams.canvas_height; + frame_w = frame_w * kParams.viewport_width / kParams.canvas_width; + frame_h = frame_h * kParams.viewport_height / kParams.canvas_height; + + // glScissor() takes window coordinates (0,0 at bottom left). + window_y = kParams.viewport_height - window_y - frame_h; + + glEnable(GL_SCISSOR_TEST); + glScissor(window_x, window_y, frame_w, frame_h); + DrawBackground(); + glDisable(GL_SCISSOR_TEST); +} + +static void HandleDisplay(void) { + const WebPDecBuffer* const pic = kParams.pic; + const WebPIterator* const curr = &kParams.curr_frame; + WebPIterator* const prev = &kParams.prev_frame; + GLfloat xoff, yoff; + if (pic == NULL) return; + glPushMatrix(); + glPixelZoom((GLfloat)(+1. / kParams.canvas_width * kParams.viewport_width), + (GLfloat)(-1. / kParams.canvas_height * kParams.viewport_height)); + xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width); + yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height); + glRasterPos2f(-1.f + xoff, 1.f - yoff); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4); + + if (kParams.only_deltas) { + DrawBackground(); + } else { + // The rectangle of the previous frame might be different than the current + // frame, so we may need to DrawBackgroundScissored for both. + if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { + // Clear the previous frame rectangle. + DrawBackgroundScissored(prev->x_offset, prev->y_offset, prev->width, + prev->height); + } + if (curr->blend_method == WEBP_MUX_NO_BLEND) { + // We simulate no-blending behavior by first clearing the current frame + // rectangle and then alpha-blending against it. + DrawBackgroundScissored(curr->x_offset, curr->y_offset, curr->width, + curr->height); + } + } + + *prev = *curr; + + glDrawPixels(pic->width, pic->height, + GL_RGBA, GL_UNSIGNED_BYTE, + (GLvoid*)pic->u.RGBA.rgba); + if (kParams.print_info) { + char tmp[32]; + + glColor4f(0.90f, 0.0f, 0.90f, 1.0f); + glRasterPos2f(-0.95f, 0.90f); + PrintString(kParams.file_name); + + snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", pic->width, pic->height); + glColor4f(0.90f, 0.0f, 0.90f, 1.0f); + glRasterPos2f(-0.95f, 0.80f); + PrintString(tmp); + if (curr->x_offset != 0 || curr->y_offset != 0) { + snprintf(tmp, sizeof(tmp), " (offset:%d,%d)", + curr->x_offset, curr->y_offset); + glRasterPos2f(-0.95f, 0.70f); + PrintString(tmp); + } + } + glPopMatrix(); +#if defined(__APPLE__) || defined(_WIN32) + glFlush(); +#else + glutSwapBuffers(); +#endif +} + +static void StartDisplay(void) { + int width = kParams.canvas_width; + int height = kParams.canvas_height; + int screen_width, screen_height; + // TODO(webp:365) GLUT_DOUBLE results in flickering / old frames to be + // partially displayed with animated webp + alpha. +#if defined(__APPLE__) || defined(_WIN32) + glutInitDisplayMode(GLUT_RGBA); +#else + glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); +#endif + screen_width = glutGet(GLUT_SCREEN_WIDTH); + screen_height = glutGet(GLUT_SCREEN_HEIGHT); + if (width > screen_width || height > screen_height) { + if (width > screen_width) { + height = (height * screen_width + width - 1) / width; + width = screen_width; + } + if (height > screen_height) { + width = (width * screen_height + height - 1) / height; + height = screen_height; + } + } + glutInitWindowSize(width, height); + glutCreateWindow("WebP viewer"); + glutDisplayFunc(HandleDisplay); + glutReshapeFunc(HandleReshape); + glutIdleFunc(NULL); + glutKeyboardFunc(HandleKey); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glClearColor(0, 0, 0, 0); // window will be cleared to black (no blend) + DrawBackground(); +} + +//------------------------------------------------------------------------------ +// Main + +static void Help(void) { + printf( + "Usage: vwebp in_file [options]\n\n" + "Decodes the WebP image file and visualize it using OpenGL\n" + "Options are:\n" + " -version ..... print version number and exit\n" + " -noicc ....... don't use the icc profile if present\n" + " -nofancy ..... don't use the fancy YUV420 upscaler\n" + " -nofilter .... disable in-loop filtering\n" + " -dither dithering strength (0..100), default=50\n" + " -noalphadither disable alpha plane dithering\n" + " -usebgcolor .. display background color\n" + " -mt .......... use multi-threading\n" + " -info ........ print info\n" + " -h ........... this help message\n" + "\n" + "Keyboard shortcuts:\n" + " 'c' ................ toggle use of color profile\n" + " 'b' ................ toggle background color display\n" + " 'i' ................ overlay file information\n" + " 'd' ................ disable blending & disposal (debug)\n" + " 'q' / 'Q' / ESC .... quit\n"); +} + +int main(int argc, char* argv[]) { + int c; + WebPDecoderConfig* const config = &kParams.config; + WebPIterator* const curr = &kParams.curr_frame; + + INIT_WARGV(argc, argv); + + if (!WebPInitDecoderConfig(config)) { + fprintf(stderr, "Library version mismatch!\n"); + FREE_WARGV_AND_RETURN(-1); + } + config->options.dithering_strength = 50; + config->options.alpha_dithering_strength = 100; + kParams.use_color_profile = 1; + // Background color hidden by default to see transparent areas. + kParams.draw_anim_background_color = 0; + + for (c = 1; c < argc; ++c) { + int parse_error = 0; + if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { + Help(); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-noicc")) { + kParams.use_color_profile = 0; + } else if (!strcmp(argv[c], "-nofancy")) { + config->options.no_fancy_upsampling = 1; + } else if (!strcmp(argv[c], "-nofilter")) { + config->options.bypass_filtering = 1; + } else if (!strcmp(argv[c], "-noalphadither")) { + config->options.alpha_dithering_strength = 0; + } else if (!strcmp(argv[c], "-usebgcolor")) { + kParams.draw_anim_background_color = 1; + } else if (!strcmp(argv[c], "-dither") && c + 1 < argc) { + config->options.dithering_strength = + ExUtilGetInt(argv[++c], 0, &parse_error); + } else if (!strcmp(argv[c], "-info")) { + kParams.print_info = 1; + } else if (!strcmp(argv[c], "-version")) { + const int dec_version = WebPGetDecoderVersion(); + const int dmux_version = WebPGetDemuxVersion(); + printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n", + (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff, + dec_version & 0xff, (dmux_version >> 16) & 0xff, + (dmux_version >> 8) & 0xff, dmux_version & 0xff); + FREE_WARGV_AND_RETURN(0); + } else if (!strcmp(argv[c], "-mt")) { + config->options.use_threads = 1; + } else if (!strcmp(argv[c], "--")) { + if (c < argc - 1) kParams.file_name = (const char*)GET_WARGV(argv, ++c); + break; + } else if (argv[c][0] == '-') { + printf("Unknown option '%s'\n", argv[c]); + Help(); + FREE_WARGV_AND_RETURN(-1); + } else { + kParams.file_name = (const char*)GET_WARGV(argv, c); + } + + if (parse_error) { + Help(); + FREE_WARGV_AND_RETURN(-1); + } + } + + if (kParams.file_name == NULL) { + printf("missing input file!!\n"); + Help(); + FREE_WARGV_AND_RETURN(0); + } + + if (!ImgIoUtilReadFile(kParams.file_name, + &kParams.data.bytes, &kParams.data.size)) { + goto Error; + } + + if (!WebPGetInfo(kParams.data.bytes, kParams.data.size, NULL, NULL)) { + fprintf(stderr, "Input file doesn't appear to be WebP format.\n"); + goto Error; + } + + kParams.dmux = WebPDemux(&kParams.data); + if (kParams.dmux == NULL) { + fprintf(stderr, "Could not create demuxing object!\n"); + goto Error; + } + + kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH); + kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT); + if (kParams.print_info) { + printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height); + } + + ClearPreviousFrame(); + + memset(&kParams.iccp, 0, sizeof(kParams.iccp)); + kParams.has_color_profile = + !!(WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & ICCP_FLAG); + if (kParams.has_color_profile) { +#ifdef WEBP_HAVE_QCMS + if (!WebPDemuxGetChunk(kParams.dmux, "ICCP", 1, &kParams.iccp)) goto Error; + printf("VP8X: Found color profile\n"); +#else + fprintf(stderr, "Warning: color profile present, but qcms is unavailable!\n" + "Build libqcms from Mozilla or Chromium and define WEBP_HAVE_QCMS " + "before building.\n"); +#endif + } + + if (!WebPDemuxGetFrame(kParams.dmux, 1, curr)) goto Error; + + kParams.has_animation = (curr->num_frames > 1); + kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT); + kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR); + printf("VP8X: Found %d images in file (loop count = %d)\n", + curr->num_frames, kParams.loop_count); + + // Decode first frame + if (!Decode()) goto Error; + + // Position iterator to last frame. Next call to HandleDisplay will wrap over. + // We take this into account by bumping up loop_count. + WebPDemuxGetFrame(kParams.dmux, 0, curr); + if (kParams.loop_count) ++kParams.loop_count; + +#if defined(__unix__) || defined(__CYGWIN__) + // Work around GLUT compositor bug. + // https://bugs.launchpad.net/ubuntu/+source/freeglut/+bug/369891 + setenv("XLIB_SKIP_ARGB_VISUALS", "1", 1); +#endif + + // Start display (and timer) + glutInit(&argc, argv); +#ifdef FREEGLUT + glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); +#endif + StartDisplay(); + + if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0); + glutMainLoop(); + + // Should only be reached when using FREEGLUT: + ClearParams(); + FREE_WARGV_AND_RETURN(0); + + Error: + ClearParams(); + FREE_WARGV_AND_RETURN(-1); +} + +#else // !WEBP_HAVE_GL + +int main(int argc, const char* argv[]) { + fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]); + (void)argc; + return 0; +} + +#endif + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/examples/webpinfo.c b/internal/libwebp-1.2.1/examples/webpinfo.c new file mode 100644 index 0000000..7792629 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/webpinfo.c @@ -0,0 +1,1180 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Command-line tool to print out the chunk level structure of WebP files +// along with basic integrity checks. +// +// Author: Hui Su (huisu@google.com) + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include "../imageio/imageio_util.h" +#include "./unicode.h" +#include "webp/decode.h" +#include "webp/format_constants.h" +#include "webp/mux_types.h" + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + +#define LOG_ERROR(MESSAGE) \ + do { \ + if (webp_info->show_diagnosis_) { \ + fprintf(stderr, "Error: %s\n", MESSAGE); \ + } \ + } while (0) + +#define LOG_WARN(MESSAGE) \ + do { \ + if (webp_info->show_diagnosis_) { \ + fprintf(stderr, "Warning: %s\n", MESSAGE); \ + } \ + ++webp_info->num_warnings_; \ + } while (0) + +static const char* const kFormats[3] = { + "Unknown", + "Lossy", + "Lossless" +}; + +static const char* const kLosslessTransforms[4] = { + "Predictor", + "Cross Color", + "Subtract Green", + "Color Indexing" +}; + +static const char* const kAlphaFilterMethods[4] = { + "None", + "Horizontal", + "Vertical", + "Gradient" +}; + +typedef enum { + WEBP_INFO_OK = 0, + WEBP_INFO_TRUNCATED_DATA, + WEBP_INFO_PARSE_ERROR, + WEBP_INFO_INVALID_PARAM, + WEBP_INFO_BITSTREAM_ERROR, + WEBP_INFO_MISSING_DATA, + WEBP_INFO_INVALID_COMMAND +} WebPInfoStatus; + +typedef enum ChunkID { + CHUNK_VP8, + CHUNK_VP8L, + CHUNK_VP8X, + CHUNK_ALPHA, + CHUNK_ANIM, + CHUNK_ANMF, + CHUNK_ICCP, + CHUNK_EXIF, + CHUNK_XMP, + CHUNK_UNKNOWN, + CHUNK_TYPES = CHUNK_UNKNOWN +} ChunkID; + +typedef struct { + size_t start_; + size_t end_; + const uint8_t* buf_; +} MemBuffer; + +typedef struct { + size_t offset_; + size_t size_; + const uint8_t* payload_; + ChunkID id_; +} ChunkData; + +typedef struct WebPInfo { + int canvas_width_; + int canvas_height_; + int loop_count_; + int num_frames_; + int chunk_counts_[CHUNK_TYPES]; + int anmf_subchunk_counts_[3]; // 0 VP8; 1 VP8L; 2 ALPH. + uint32_t bgcolor_; + int feature_flags_; + int has_alpha_; + // Used for parsing ANMF chunks. + int frame_width_, frame_height_; + size_t anim_frame_data_size_; + int is_processing_anim_frame_, seen_alpha_subchunk_, seen_image_subchunk_; + // Print output control. + int quiet_, show_diagnosis_, show_summary_; + int num_warnings_; + int parse_bitstream_; +} WebPInfo; + +static void WebPInfoInit(WebPInfo* const webp_info) { + memset(webp_info, 0, sizeof(*webp_info)); +} + +static const char kWebPChunkTags[CHUNK_TYPES][4] = { + { 'V', 'P', '8', ' ' }, + { 'V', 'P', '8', 'L' }, + { 'V', 'P', '8', 'X' }, + { 'A', 'L', 'P', 'H' }, + { 'A', 'N', 'I', 'M' }, + { 'A', 'N', 'M', 'F' }, + { 'I', 'C', 'C', 'P' }, + { 'E', 'X', 'I', 'F' }, + { 'X', 'M', 'P', ' ' }, +}; + +// ----------------------------------------------------------------------------- +// Data reading. + +static int GetLE16(const uint8_t* const data) { + return (data[0] << 0) | (data[1] << 8); +} + +static int GetLE24(const uint8_t* const data) { + return GetLE16(data) | (data[2] << 16); +} + +static uint32_t GetLE32(const uint8_t* const data) { + return GetLE16(data) | ((uint32_t)GetLE16(data + 2) << 16); +} + +static int ReadLE16(const uint8_t** data) { + const int val = GetLE16(*data); + *data += 2; + return val; +} + +static int ReadLE24(const uint8_t** data) { + const int val = GetLE24(*data); + *data += 3; + return val; +} + +static uint32_t ReadLE32(const uint8_t** data) { + const uint32_t val = GetLE32(*data); + *data += 4; + return val; +} + +static int ReadFileToWebPData(const char* const filename, + WebPData* const webp_data) { + const uint8_t* data; + size_t size; + if (!ImgIoUtilReadFile(filename, &data, &size)) return 0; + webp_data->bytes = data; + webp_data->size = size; + return 1; +} + +// ----------------------------------------------------------------------------- +// MemBuffer object. + +static void InitMemBuffer(MemBuffer* const mem, const WebPData* webp_data) { + mem->buf_ = webp_data->bytes; + mem->start_ = 0; + mem->end_ = webp_data->size; +} + +static size_t MemDataSize(const MemBuffer* const mem) { + return (mem->end_ - mem->start_); +} + +static const uint8_t* GetBuffer(MemBuffer* const mem) { + return mem->buf_ + mem->start_; +} + +static void Skip(MemBuffer* const mem, size_t size) { + mem->start_ += size; +} + +static uint32_t ReadMemBufLE32(MemBuffer* const mem) { + const uint8_t* const data = mem->buf_ + mem->start_; + const uint32_t val = GetLE32(data); + assert(MemDataSize(mem) >= 4); + Skip(mem, 4); + return val; +} + +// ----------------------------------------------------------------------------- +// Lossy bitstream analysis. + +static int GetBits(const uint8_t* const data, size_t data_size, size_t nb, + int* val, uint64_t* const bit_pos) { + *val = 0; + while (nb-- > 0) { + const uint64_t p = (*bit_pos)++; + if ((p >> 3) >= data_size) { + return 0; + } else { + const int bit = !!(data[p >> 3] & (128 >> ((p & 7)))); + *val = (*val << 1) | bit; + } + } + return 1; +} + +static int GetSignedBits(const uint8_t* const data, size_t data_size, size_t nb, + int* val, uint64_t* const bit_pos) { + int sign; + if (!GetBits(data, data_size, nb, val, bit_pos)) return 0; + if (!GetBits(data, data_size, 1, &sign, bit_pos)) return 0; + if (sign) *val = -(*val); + return 1; +} + +#define GET_BITS(v, n) \ + do { \ + if (!GetBits(data, data_size, n, &(v), bit_pos)) { \ + LOG_ERROR("Truncated lossy bitstream."); \ + return WEBP_INFO_TRUNCATED_DATA; \ + } \ + } while (0) + +#define GET_SIGNED_BITS(v, n) \ + do { \ + if (!GetSignedBits(data, data_size, n, &(v), bit_pos)) { \ + LOG_ERROR("Truncated lossy bitstream."); \ + return WEBP_INFO_TRUNCATED_DATA; \ + } \ + } while (0) + +static WebPInfoStatus ParseLossySegmentHeader(const WebPInfo* const webp_info, + const uint8_t* const data, + size_t data_size, + uint64_t* const bit_pos) { + int use_segment; + GET_BITS(use_segment, 1); + printf(" Use segment: %d\n", use_segment); + if (use_segment) { + int update_map, update_data; + GET_BITS(update_map, 1); + GET_BITS(update_data, 1); + printf(" Update map: %d\n" + " Update data: %d\n", + update_map, update_data); + if (update_data) { + int i, a_delta; + int quantizer[4] = {0, 0, 0, 0}; + int filter_strength[4] = {0, 0, 0, 0}; + GET_BITS(a_delta, 1); + printf(" Absolute delta: %d\n", a_delta); + for (i = 0; i < 4; ++i) { + int bit; + GET_BITS(bit, 1); + if (bit) GET_SIGNED_BITS(quantizer[i], 7); + } + for (i = 0; i < 4; ++i) { + int bit; + GET_BITS(bit, 1); + if (bit) GET_SIGNED_BITS(filter_strength[i], 6); + } + printf(" Quantizer: %d %d %d %d\n", quantizer[0], quantizer[1], + quantizer[2], quantizer[3]); + printf(" Filter strength: %d %d %d %d\n", filter_strength[0], + filter_strength[1], filter_strength[2], filter_strength[3]); + } + if (update_map) { + int i; + int prob_segment[3] = {255, 255, 255}; + for (i = 0; i < 3; ++i) { + int bit; + GET_BITS(bit, 1); + if (bit) GET_BITS(prob_segment[i], 8); + } + printf(" Prob segment: %d %d %d\n", + prob_segment[0], prob_segment[1], prob_segment[2]); + } + } + return WEBP_INFO_OK; +} + +static WebPInfoStatus ParseLossyFilterHeader(const WebPInfo* const webp_info, + const uint8_t* const data, + size_t data_size, + uint64_t* const bit_pos) { + int simple_filter, level, sharpness, use_lf_delta; + GET_BITS(simple_filter, 1); + GET_BITS(level, 6); + GET_BITS(sharpness, 3); + GET_BITS(use_lf_delta, 1); + printf(" Simple filter: %d\n", simple_filter); + printf(" Level: %d\n", level); + printf(" Sharpness: %d\n", sharpness); + printf(" Use lf delta: %d\n", use_lf_delta); + if (use_lf_delta) { + int update; + GET_BITS(update, 1); + printf(" Update lf delta: %d\n", update); + if (update) { + int i; + for (i = 0; i < 4 + 4; ++i) { + int temp; + GET_BITS(temp, 1); + if (temp) GET_BITS(temp, 7); + } + } + } + return WEBP_INFO_OK; +} + +static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data, + const WebPInfo* const webp_info) { + const uint8_t* data = chunk_data->payload_; + size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE; + const uint32_t bits = (uint32_t)data[0] | (data[1] << 8) | (data[2] << 16); + const int key_frame = !(bits & 1); + const int profile = (bits >> 1) & 7; + const int display = (bits >> 4) & 1; + const uint32_t partition0_length = (bits >> 5); + WebPInfoStatus status = WEBP_INFO_OK; + uint64_t bit_position = 0; + uint64_t* const bit_pos = &bit_position; + int colorspace, clamp_type; + printf(" Parsing lossy bitstream...\n"); + // Calling WebPGetFeatures() in ProcessImageChunk() should ensure this. + assert(chunk_data->size_ >= CHUNK_HEADER_SIZE + 10); + if (profile > 3) { + LOG_ERROR("Unknown profile."); + return WEBP_INFO_BITSTREAM_ERROR; + } + if (!display) { + LOG_ERROR("Frame is not displayable."); + return WEBP_INFO_BITSTREAM_ERROR; + } + data += 3; + data_size -= 3; + printf(" Key frame: %s\n" + " Profile: %d\n" + " Display: %s\n" + " Part. 0 length: %d\n", + key_frame ? "Yes" : "No", profile, + display ? "Yes" : "No", partition0_length); + if (key_frame) { + if (!(data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a)) { + LOG_ERROR("Invalid lossy bitstream signature."); + return WEBP_INFO_BITSTREAM_ERROR; + } + printf(" Width: %d\n" + " X scale: %d\n" + " Height: %d\n" + " Y scale: %d\n", + ((data[4] << 8) | data[3]) & 0x3fff, data[4] >> 6, + ((data[6] << 8) | data[5]) & 0x3fff, data[6] >> 6); + data += 7; + data_size -= 7; + } else { + LOG_ERROR("Non-keyframe detected in lossy bitstream."); + return WEBP_INFO_BITSTREAM_ERROR; + } + if (partition0_length >= data_size) { + LOG_ERROR("Bad partition length."); + return WEBP_INFO_BITSTREAM_ERROR; + } + GET_BITS(colorspace, 1); + GET_BITS(clamp_type, 1); + printf(" Color space: %d\n", colorspace); + printf(" Clamp type: %d\n", clamp_type); + status = ParseLossySegmentHeader(webp_info, data, data_size, bit_pos); + if (status != WEBP_INFO_OK) return status; + status = ParseLossyFilterHeader(webp_info, data, data_size, bit_pos); + if (status != WEBP_INFO_OK) return status; + { // Partition number and size. + const uint8_t* part_size = data + partition0_length; + int num_parts, i; + size_t part_data_size; + GET_BITS(num_parts, 2); + num_parts = 1 << num_parts; + if ((int)(data_size - partition0_length) < (num_parts - 1) * 3) { + LOG_ERROR("Truncated lossy bitstream."); + return WEBP_INFO_TRUNCATED_DATA; + } + part_data_size = data_size - partition0_length - (num_parts - 1) * 3; + printf(" Total partitions: %d\n", num_parts); + for (i = 1; i < num_parts; ++i) { + const size_t psize = + part_size[0] | (part_size[1] << 8) | (part_size[2] << 16); + if (psize > part_data_size) { + LOG_ERROR("Truncated partition."); + return WEBP_INFO_TRUNCATED_DATA; + } + printf(" Part. %d length: %d\n", i, (int)psize); + part_data_size -= psize; + part_size += 3; + } + } + // Quantizer. + { + int base_q, bit; + int dq_y1_dc = 0, dq_y2_dc = 0, dq_y2_ac = 0, dq_uv_dc = 0, dq_uv_ac = 0; + GET_BITS(base_q, 7); + GET_BITS(bit, 1); + if (bit) GET_SIGNED_BITS(dq_y1_dc, 4); + GET_BITS(bit, 1); + if (bit) GET_SIGNED_BITS(dq_y2_dc, 4); + GET_BITS(bit, 1); + if (bit) GET_SIGNED_BITS(dq_y2_ac, 4); + GET_BITS(bit, 1); + if (bit) GET_SIGNED_BITS(dq_uv_dc, 4); + GET_BITS(bit, 1); + if (bit) GET_SIGNED_BITS(dq_uv_ac, 4); + printf(" Base Q: %d\n", base_q); + printf(" DQ Y1 DC: %d\n", dq_y1_dc); + printf(" DQ Y2 DC: %d\n", dq_y2_dc); + printf(" DQ Y2 AC: %d\n", dq_y2_ac); + printf(" DQ UV DC: %d\n", dq_uv_dc); + printf(" DQ UV AC: %d\n", dq_uv_ac); + } + if ((*bit_pos >> 3) >= partition0_length) { + LOG_ERROR("Truncated lossy bitstream."); + return WEBP_INFO_TRUNCATED_DATA; + } + return WEBP_INFO_OK; +} + +// ----------------------------------------------------------------------------- +// Lossless bitstream analysis. + +static int LLGetBits(const uint8_t* const data, size_t data_size, size_t nb, + int* val, uint64_t* const bit_pos) { + uint32_t i = 0; + *val = 0; + while (i < nb) { + const uint64_t p = (*bit_pos)++; + if ((p >> 3) >= data_size) { + return 0; + } else { + const int bit = !!(data[p >> 3] & (1 << ((p & 7)))); + *val = *val | (bit << i); + ++i; + } + } + return 1; +} + +#define LL_GET_BITS(v, n) \ + do { \ + if (!LLGetBits(data, data_size, n, &(v), bit_pos)) { \ + LOG_ERROR("Truncated lossless bitstream."); \ + return WEBP_INFO_TRUNCATED_DATA; \ + } \ + } while (0) + +static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info, + const uint8_t* const data, + size_t data_size, + uint64_t* const bit_pos) { + int use_transform, block_size, n_colors; + LL_GET_BITS(use_transform, 1); + printf(" Use transform: %s\n", use_transform ? "Yes" : "No"); + if (use_transform) { + int type; + LL_GET_BITS(type, 2); + printf(" 1st transform: %s (%d)\n", kLosslessTransforms[type], type); + switch (type) { + case PREDICTOR_TRANSFORM: + case CROSS_COLOR_TRANSFORM: + LL_GET_BITS(block_size, 3); + block_size = 1 << (block_size + 2); + printf(" Tran. block size: %d\n", block_size); + break; + case COLOR_INDEXING_TRANSFORM: + LL_GET_BITS(n_colors, 8); + n_colors += 1; + printf(" No. of colors: %d\n", n_colors); + break; + default: break; + } + } + return WEBP_INFO_OK; +} + +static WebPInfoStatus ParseLosslessHeader(const ChunkData* const chunk_data, + WebPInfo* const webp_info) { + const uint8_t* data = chunk_data->payload_; + size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE; + uint64_t bit_position = 0; + uint64_t* const bit_pos = &bit_position; + WebPInfoStatus status; + printf(" Parsing lossless bitstream...\n"); + if (data_size < VP8L_FRAME_HEADER_SIZE) { + LOG_ERROR("Truncated lossless bitstream."); + return WEBP_INFO_TRUNCATED_DATA; + } + if (data[0] != VP8L_MAGIC_BYTE) { + LOG_ERROR("Invalid lossless bitstream signature."); + return WEBP_INFO_BITSTREAM_ERROR; + } + data += 1; + data_size -= 1; + { + int width, height, has_alpha, version; + LL_GET_BITS(width, 14); + LL_GET_BITS(height, 14); + LL_GET_BITS(has_alpha, 1); + LL_GET_BITS(version, 3); + width += 1; + height += 1; + printf(" Width: %d\n", width); + printf(" Height: %d\n", height); + printf(" Alpha: %d\n", has_alpha); + printf(" Version: %d\n", version); + } + status = ParseLosslessTransform(webp_info, data, data_size, bit_pos); + if (status != WEBP_INFO_OK) return status; + return WEBP_INFO_OK; +} + +static WebPInfoStatus ParseAlphaHeader(const ChunkData* const chunk_data, + WebPInfo* const webp_info) { + const uint8_t* data = chunk_data->payload_; + size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE; + if (data_size <= ALPHA_HEADER_LEN) { + LOG_ERROR("Truncated ALPH chunk."); + return WEBP_INFO_TRUNCATED_DATA; + } + printf(" Parsing ALPH chunk...\n"); + { + const int compression_method = (data[0] >> 0) & 0x03; + const int filter = (data[0] >> 2) & 0x03; + const int pre_processing = (data[0] >> 4) & 0x03; + const int reserved_bits = (data[0] >> 6) & 0x03; + printf(" Compression: %d\n", compression_method); + printf(" Filter: %s (%d)\n", + kAlphaFilterMethods[filter], filter); + printf(" Pre-processing: %d\n", pre_processing); + if (compression_method > ALPHA_LOSSLESS_COMPRESSION) { + LOG_ERROR("Invalid Alpha compression method."); + return WEBP_INFO_BITSTREAM_ERROR; + } + if (pre_processing > ALPHA_PREPROCESSED_LEVELS) { + LOG_ERROR("Invalid Alpha pre-processing method."); + return WEBP_INFO_BITSTREAM_ERROR; + } + if (reserved_bits != 0) { + LOG_WARN("Reserved bits in ALPH chunk header are not all 0."); + } + data += ALPHA_HEADER_LEN; + data_size -= ALPHA_HEADER_LEN; + if (compression_method == ALPHA_LOSSLESS_COMPRESSION) { + uint64_t bit_pos = 0; + WebPInfoStatus status = + ParseLosslessTransform(webp_info, data, data_size, &bit_pos); + if (status != WEBP_INFO_OK) return status; + } + } + return WEBP_INFO_OK; +} + +// ----------------------------------------------------------------------------- +// Chunk parsing. + +static WebPInfoStatus ParseRIFFHeader(WebPInfo* const webp_info, + MemBuffer* const mem) { + const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE; + size_t riff_size; + + if (MemDataSize(mem) < min_size) { + LOG_ERROR("Truncated data detected when parsing RIFF header."); + return WEBP_INFO_TRUNCATED_DATA; + } + if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) || + memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) { + LOG_ERROR("Corrupted RIFF header."); + return WEBP_INFO_PARSE_ERROR; + } + riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE); + if (riff_size < CHUNK_HEADER_SIZE) { + LOG_ERROR("RIFF size is too small."); + return WEBP_INFO_PARSE_ERROR; + } + if (riff_size > MAX_CHUNK_PAYLOAD) { + LOG_ERROR("RIFF size is over limit."); + return WEBP_INFO_PARSE_ERROR; + } + riff_size += CHUNK_HEADER_SIZE; + if (!webp_info->quiet_) { + printf("RIFF HEADER:\n"); + printf(" File size: %6d\n", (int)riff_size); + } + if (riff_size < mem->end_) { + LOG_WARN("RIFF size is smaller than the file size."); + mem->end_ = riff_size; + } else if (riff_size > mem->end_) { + LOG_ERROR("Truncated data detected when parsing RIFF payload."); + return WEBP_INFO_TRUNCATED_DATA; + } + Skip(mem, RIFF_HEADER_SIZE); + return WEBP_INFO_OK; +} + +static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info, + MemBuffer* const mem, + ChunkData* const chunk_data) { + memset(chunk_data, 0, sizeof(*chunk_data)); + if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { + LOG_ERROR("Truncated data detected when parsing chunk header."); + return WEBP_INFO_TRUNCATED_DATA; + } else { + const size_t chunk_start_offset = mem->start_; + const uint32_t fourcc = ReadMemBufLE32(mem); + const uint32_t payload_size = ReadMemBufLE32(mem); + const uint32_t payload_size_padded = payload_size + (payload_size & 1); + const size_t chunk_size = CHUNK_HEADER_SIZE + payload_size_padded; + int i; + if (payload_size > MAX_CHUNK_PAYLOAD) { + LOG_ERROR("Size of chunk payload is over limit."); + return WEBP_INFO_INVALID_PARAM; + } + if (payload_size_padded > MemDataSize(mem)){ + LOG_ERROR("Truncated data detected when parsing chunk payload."); + return WEBP_INFO_TRUNCATED_DATA; + } + for (i = 0; i < CHUNK_TYPES; ++i) { + if (!memcmp(kWebPChunkTags[i], &fourcc, TAG_SIZE)) break; + } + chunk_data->offset_ = chunk_start_offset; + chunk_data->size_ = chunk_size; + chunk_data->id_ = (ChunkID)i; + chunk_data->payload_ = GetBuffer(mem); + if (chunk_data->id_ == CHUNK_ANMF) { + if (payload_size != payload_size_padded) { + LOG_ERROR("ANMF chunk size should always be even."); + return WEBP_INFO_PARSE_ERROR; + } + // There are sub-chunks to be parsed in an ANMF chunk. + Skip(mem, ANMF_CHUNK_SIZE); + } else { + Skip(mem, payload_size_padded); + } + return WEBP_INFO_OK; + } +} + +// ----------------------------------------------------------------------------- +// Chunk analysis. + +static WebPInfoStatus ProcessVP8XChunk(const ChunkData* const chunk_data, + WebPInfo* const webp_info) { + const uint8_t* data = chunk_data->payload_; + if (webp_info->chunk_counts_[CHUNK_VP8] || + webp_info->chunk_counts_[CHUNK_VP8L] || + webp_info->chunk_counts_[CHUNK_VP8X]) { + LOG_ERROR("Already seen a VP8/VP8L/VP8X chunk when parsing VP8X chunk."); + return WEBP_INFO_PARSE_ERROR; + } + if (chunk_data->size_ != VP8X_CHUNK_SIZE + CHUNK_HEADER_SIZE) { + LOG_ERROR("Corrupted VP8X chunk."); + return WEBP_INFO_PARSE_ERROR; + } + ++webp_info->chunk_counts_[CHUNK_VP8X]; + webp_info->feature_flags_ = *data; + data += 4; + webp_info->canvas_width_ = 1 + ReadLE24(&data); + webp_info->canvas_height_ = 1 + ReadLE24(&data); + if (!webp_info->quiet_) { + printf(" ICCP: %d\n Alpha: %d\n EXIF: %d\n XMP: %d\n Animation: %d\n", + (webp_info->feature_flags_ & ICCP_FLAG) != 0, + (webp_info->feature_flags_ & ALPHA_FLAG) != 0, + (webp_info->feature_flags_ & EXIF_FLAG) != 0, + (webp_info->feature_flags_ & XMP_FLAG) != 0, + (webp_info->feature_flags_ & ANIMATION_FLAG) != 0); + printf(" Canvas size %d x %d\n", + webp_info->canvas_width_, webp_info->canvas_height_); + } + if (webp_info->canvas_width_ > MAX_CANVAS_SIZE) { + LOG_WARN("Canvas width is out of range in VP8X chunk."); + } + if (webp_info->canvas_height_ > MAX_CANVAS_SIZE) { + LOG_WARN("Canvas height is out of range in VP8X chunk."); + } + if ((uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ > + MAX_IMAGE_AREA) { + LOG_WARN("Canvas area is out of range in VP8X chunk."); + } + return WEBP_INFO_OK; +} + +static WebPInfoStatus ProcessANIMChunk(const ChunkData* const chunk_data, + WebPInfo* const webp_info) { + const uint8_t* data = chunk_data->payload_; + if (!webp_info->chunk_counts_[CHUNK_VP8X]) { + LOG_ERROR("ANIM chunk detected before VP8X chunk."); + return WEBP_INFO_PARSE_ERROR; + } + if (chunk_data->size_ != ANIM_CHUNK_SIZE + CHUNK_HEADER_SIZE) { + LOG_ERROR("Corrupted ANIM chunk."); + return WEBP_INFO_PARSE_ERROR; + } + webp_info->bgcolor_ = ReadLE32(&data); + webp_info->loop_count_ = ReadLE16(&data); + ++webp_info->chunk_counts_[CHUNK_ANIM]; + if (!webp_info->quiet_) { + printf(" Background color:(ARGB) %02x %02x %02x %02x\n", + (webp_info->bgcolor_ >> 24) & 0xff, + (webp_info->bgcolor_ >> 16) & 0xff, + (webp_info->bgcolor_ >> 8) & 0xff, + webp_info->bgcolor_ & 0xff); + printf(" Loop count : %d\n", webp_info->loop_count_); + } + if (webp_info->loop_count_ > MAX_LOOP_COUNT) { + LOG_WARN("Loop count is out of range in ANIM chunk."); + } + return WEBP_INFO_OK; +} + +static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data, + WebPInfo* const webp_info) { + const uint8_t* data = chunk_data->payload_; + int offset_x, offset_y, width, height, duration, blend, dispose, temp; + if (webp_info->is_processing_anim_frame_) { + LOG_ERROR("ANMF chunk detected within another ANMF chunk."); + return WEBP_INFO_PARSE_ERROR; + } + if (!webp_info->chunk_counts_[CHUNK_ANIM]) { + LOG_ERROR("ANMF chunk detected before ANIM chunk."); + return WEBP_INFO_PARSE_ERROR; + } + if (chunk_data->size_ <= CHUNK_HEADER_SIZE + ANMF_CHUNK_SIZE) { + LOG_ERROR("Truncated data detected when parsing ANMF chunk."); + return WEBP_INFO_TRUNCATED_DATA; + } + offset_x = 2 * ReadLE24(&data); + offset_y = 2 * ReadLE24(&data); + width = 1 + ReadLE24(&data); + height = 1 + ReadLE24(&data); + duration = ReadLE24(&data); + temp = *data; + dispose = temp & 1; + blend = (temp >> 1) & 1; + ++webp_info->chunk_counts_[CHUNK_ANMF]; + if (!webp_info->quiet_) { + printf(" Offset_X: %d\n Offset_Y: %d\n Width: %d\n Height: %d\n" + " Duration: %d\n Dispose: %d\n Blend: %d\n", + offset_x, offset_y, width, height, duration, dispose, blend); + } + if (duration > MAX_DURATION) { + LOG_ERROR("Invalid duration parameter in ANMF chunk."); + return WEBP_INFO_INVALID_PARAM; + } + if (offset_x > MAX_POSITION_OFFSET || offset_y > MAX_POSITION_OFFSET) { + LOG_ERROR("Invalid offset parameters in ANMF chunk."); + return WEBP_INFO_INVALID_PARAM; + } + if ((uint64_t)offset_x + width > (uint64_t)webp_info->canvas_width_ || + (uint64_t)offset_y + height > (uint64_t)webp_info->canvas_height_) { + LOG_ERROR("Frame exceeds canvas in ANMF chunk."); + return WEBP_INFO_INVALID_PARAM; + } + webp_info->is_processing_anim_frame_ = 1; + webp_info->seen_alpha_subchunk_ = 0; + webp_info->seen_image_subchunk_ = 0; + webp_info->frame_width_ = width; + webp_info->frame_height_ = height; + webp_info->anim_frame_data_size_ = + chunk_data->size_ - CHUNK_HEADER_SIZE - ANMF_CHUNK_SIZE; + return WEBP_INFO_OK; +} + +static WebPInfoStatus ProcessImageChunk(const ChunkData* const chunk_data, + WebPInfo* const webp_info) { + const uint8_t* data = chunk_data->payload_ - CHUNK_HEADER_SIZE; + WebPBitstreamFeatures features; + const VP8StatusCode vp8_status = + WebPGetFeatures(data, chunk_data->size_, &features); + if (vp8_status != VP8_STATUS_OK) { + LOG_ERROR("VP8/VP8L bitstream error."); + return WEBP_INFO_BITSTREAM_ERROR; + } + if (!webp_info->quiet_) { + assert(features.format >= 0 && features.format <= 2); + printf(" Width: %d\n Height: %d\n Alpha: %d\n Animation: %d\n" + " Format: %s (%d)\n", + features.width, features.height, features.has_alpha, + features.has_animation, kFormats[features.format], features.format); + } + if (webp_info->is_processing_anim_frame_) { + ++webp_info->anmf_subchunk_counts_[chunk_data->id_ == CHUNK_VP8 ? 0 : 1]; + if (chunk_data->id_ == CHUNK_VP8L && webp_info->seen_alpha_subchunk_) { + LOG_ERROR("Both VP8L and ALPH sub-chunks are present in an ANMF chunk."); + return WEBP_INFO_PARSE_ERROR; + } + if (webp_info->frame_width_ != features.width || + webp_info->frame_height_ != features.height) { + LOG_ERROR("Frame size in VP8/VP8L sub-chunk differs from ANMF header."); + return WEBP_INFO_PARSE_ERROR; + } + if (webp_info->seen_image_subchunk_) { + LOG_ERROR("Consecutive VP8/VP8L sub-chunks in an ANMF chunk."); + return WEBP_INFO_PARSE_ERROR; + } + webp_info->seen_image_subchunk_ = 1; + } else { + if (webp_info->chunk_counts_[CHUNK_VP8] || + webp_info->chunk_counts_[CHUNK_VP8L]) { + LOG_ERROR("Multiple VP8/VP8L chunks detected."); + return WEBP_INFO_PARSE_ERROR; + } + if (chunk_data->id_ == CHUNK_VP8L && + webp_info->chunk_counts_[CHUNK_ALPHA]) { + LOG_WARN("Both VP8L and ALPH chunks are detected."); + } + if (webp_info->chunk_counts_[CHUNK_ANIM] || + webp_info->chunk_counts_[CHUNK_ANMF]) { + LOG_ERROR("VP8/VP8L chunk and ANIM/ANMF chunk are both detected."); + return WEBP_INFO_PARSE_ERROR; + } + if (webp_info->chunk_counts_[CHUNK_VP8X]) { + if (webp_info->canvas_width_ != features.width || + webp_info->canvas_height_ != features.height) { + LOG_ERROR("Image size in VP8/VP8L chunk differs from VP8X chunk."); + return WEBP_INFO_PARSE_ERROR; + } + } else { + webp_info->canvas_width_ = features.width; + webp_info->canvas_height_ = features.height; + if (webp_info->canvas_width_ < 1 || webp_info->canvas_height_ < 1 || + webp_info->canvas_width_ > MAX_CANVAS_SIZE || + webp_info->canvas_height_ > MAX_CANVAS_SIZE || + (uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ > + MAX_IMAGE_AREA) { + LOG_WARN("Invalid parameters in VP8/VP8L chunk."); + } + } + ++webp_info->chunk_counts_[chunk_data->id_]; + } + ++webp_info->num_frames_; + webp_info->has_alpha_ |= features.has_alpha; + if (webp_info->parse_bitstream_) { + const int is_lossy = (chunk_data->id_ == CHUNK_VP8); + const WebPInfoStatus status = + is_lossy ? ParseLossyHeader(chunk_data, webp_info) + : ParseLosslessHeader(chunk_data, webp_info); + if (status != WEBP_INFO_OK) return status; + } + return WEBP_INFO_OK; +} + +static WebPInfoStatus ProcessALPHChunk(const ChunkData* const chunk_data, + WebPInfo* const webp_info) { + if (webp_info->is_processing_anim_frame_) { + ++webp_info->anmf_subchunk_counts_[2]; + if (webp_info->seen_alpha_subchunk_) { + LOG_ERROR("Consecutive ALPH sub-chunks in an ANMF chunk."); + return WEBP_INFO_PARSE_ERROR; + } + webp_info->seen_alpha_subchunk_ = 1; + + if (webp_info->seen_image_subchunk_) { + LOG_ERROR("ALPHA sub-chunk detected after VP8 sub-chunk " + "in an ANMF chunk."); + return WEBP_INFO_PARSE_ERROR; + } + } else { + if (webp_info->chunk_counts_[CHUNK_ANIM] || + webp_info->chunk_counts_[CHUNK_ANMF]) { + LOG_ERROR("ALPHA chunk and ANIM/ANMF chunk are both detected."); + return WEBP_INFO_PARSE_ERROR; + } + if (!webp_info->chunk_counts_[CHUNK_VP8X]) { + LOG_ERROR("ALPHA chunk detected before VP8X chunk."); + return WEBP_INFO_PARSE_ERROR; + } + if (webp_info->chunk_counts_[CHUNK_VP8]) { + LOG_ERROR("ALPHA chunk detected after VP8 chunk."); + return WEBP_INFO_PARSE_ERROR; + } + if (webp_info->chunk_counts_[CHUNK_ALPHA]) { + LOG_ERROR("Multiple ALPHA chunks detected."); + return WEBP_INFO_PARSE_ERROR; + } + ++webp_info->chunk_counts_[CHUNK_ALPHA]; + } + webp_info->has_alpha_ = 1; + if (webp_info->parse_bitstream_) { + const WebPInfoStatus status = ParseAlphaHeader(chunk_data, webp_info); + if (status != WEBP_INFO_OK) return status; + } + return WEBP_INFO_OK; +} + +static WebPInfoStatus ProcessICCPChunk(const ChunkData* const chunk_data, + WebPInfo* const webp_info) { + (void)chunk_data; + if (!webp_info->chunk_counts_[CHUNK_VP8X]) { + LOG_ERROR("ICCP chunk detected before VP8X chunk."); + return WEBP_INFO_PARSE_ERROR; + } + if (webp_info->chunk_counts_[CHUNK_VP8] || + webp_info->chunk_counts_[CHUNK_VP8L] || + webp_info->chunk_counts_[CHUNK_ANIM]) { + LOG_ERROR("ICCP chunk detected after image data."); + return WEBP_INFO_PARSE_ERROR; + } + ++webp_info->chunk_counts_[CHUNK_ICCP]; + return WEBP_INFO_OK; +} + +static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data, + WebPInfo* const webp_info) { + WebPInfoStatus status = WEBP_INFO_OK; + ChunkID id = chunk_data->id_; + if (chunk_data->id_ == CHUNK_UNKNOWN) { + char error_message[50]; + snprintf(error_message, 50, "Unknown chunk at offset %6d, length %6d", + (int)chunk_data->offset_, (int)chunk_data->size_); + LOG_WARN(error_message); + } else { + if (!webp_info->quiet_) { + const char* tag = kWebPChunkTags[chunk_data->id_]; + printf("Chunk %c%c%c%c at offset %6d, length %6d\n", + tag[0], tag[1], tag[2], tag[3], (int)chunk_data->offset_, + (int)chunk_data->size_); + } + } + switch (id) { + case CHUNK_VP8: + case CHUNK_VP8L: + status = ProcessImageChunk(chunk_data, webp_info); + break; + case CHUNK_VP8X: + status = ProcessVP8XChunk(chunk_data, webp_info); + break; + case CHUNK_ALPHA: + status = ProcessALPHChunk(chunk_data, webp_info); + break; + case CHUNK_ANIM: + status = ProcessANIMChunk(chunk_data, webp_info); + break; + case CHUNK_ANMF: + status = ProcessANMFChunk(chunk_data, webp_info); + break; + case CHUNK_ICCP: + status = ProcessICCPChunk(chunk_data, webp_info); + break; + case CHUNK_EXIF: + case CHUNK_XMP: + ++webp_info->chunk_counts_[id]; + break; + case CHUNK_UNKNOWN: + default: + break; + } + if (webp_info->is_processing_anim_frame_ && id != CHUNK_ANMF) { + if (webp_info->anim_frame_data_size_ == chunk_data->size_) { + if (!webp_info->seen_image_subchunk_) { + LOG_ERROR("No VP8/VP8L chunk detected in an ANMF chunk."); + return WEBP_INFO_PARSE_ERROR; + } + webp_info->is_processing_anim_frame_ = 0; + } else if (webp_info->anim_frame_data_size_ > chunk_data->size_) { + webp_info->anim_frame_data_size_ -= chunk_data->size_; + } else { + LOG_ERROR("Truncated data detected when parsing ANMF chunk."); + return WEBP_INFO_TRUNCATED_DATA; + } + } + return status; +} + +static WebPInfoStatus Validate(WebPInfo* const webp_info) { + if (webp_info->num_frames_ < 1) { + LOG_ERROR("No image/frame detected."); + return WEBP_INFO_MISSING_DATA; + } + if (webp_info->chunk_counts_[CHUNK_VP8X]) { + const int iccp = !!(webp_info->feature_flags_ & ICCP_FLAG); + const int exif = !!(webp_info->feature_flags_ & EXIF_FLAG); + const int xmp = !!(webp_info->feature_flags_ & XMP_FLAG); + const int animation = !!(webp_info->feature_flags_ & ANIMATION_FLAG); + const int alpha = !!(webp_info->feature_flags_ & ALPHA_FLAG); + if (!alpha && webp_info->has_alpha_) { + LOG_ERROR("Unexpected alpha data detected."); + return WEBP_INFO_PARSE_ERROR; + } + if (alpha && !webp_info->has_alpha_) { + LOG_WARN("Alpha flag is set with no alpha data present."); + } + if (iccp && !webp_info->chunk_counts_[CHUNK_ICCP]) { + LOG_ERROR("Missing ICCP chunk."); + return WEBP_INFO_MISSING_DATA; + } + if (exif && !webp_info->chunk_counts_[CHUNK_EXIF]) { + LOG_ERROR("Missing EXIF chunk."); + return WEBP_INFO_MISSING_DATA; + } + if (xmp && !webp_info->chunk_counts_[CHUNK_XMP]) { + LOG_ERROR("Missing XMP chunk."); + return WEBP_INFO_MISSING_DATA; + } + if (!iccp && webp_info->chunk_counts_[CHUNK_ICCP]) { + LOG_ERROR("Unexpected ICCP chunk detected."); + return WEBP_INFO_PARSE_ERROR; + } + if (!exif && webp_info->chunk_counts_[CHUNK_EXIF]) { + LOG_ERROR("Unexpected EXIF chunk detected."); + return WEBP_INFO_PARSE_ERROR; + } + if (!xmp && webp_info->chunk_counts_[CHUNK_XMP]) { + LOG_ERROR("Unexpected XMP chunk detected."); + return WEBP_INFO_PARSE_ERROR; + } + // Incomplete animation frame. + if (webp_info->is_processing_anim_frame_) return WEBP_INFO_MISSING_DATA; + if (!animation && webp_info->num_frames_ > 1) { + LOG_ERROR("More than 1 frame detected in non-animation file."); + return WEBP_INFO_PARSE_ERROR; + } + if (animation && (!webp_info->chunk_counts_[CHUNK_ANIM] || + !webp_info->chunk_counts_[CHUNK_ANMF])) { + LOG_ERROR("No ANIM/ANMF chunk detected in animation file."); + return WEBP_INFO_PARSE_ERROR; + } + } + return WEBP_INFO_OK; +} + +static void ShowSummary(const WebPInfo* const webp_info) { + int i; + printf("Summary:\n"); + printf("Number of frames: %d\n", webp_info->num_frames_); + printf("Chunk type : VP8 VP8L VP8X ALPH ANIM ANMF(VP8 /VP8L/ALPH) ICCP " + "EXIF XMP\n"); + printf("Chunk counts: "); + for (i = 0; i < CHUNK_TYPES; ++i) { + printf("%4d ", webp_info->chunk_counts_[i]); + if (i == CHUNK_ANMF) { + printf("%4d %4d %4d ", + webp_info->anmf_subchunk_counts_[0], + webp_info->anmf_subchunk_counts_[1], + webp_info->anmf_subchunk_counts_[2]); + } + } + printf("\n"); +} + +static WebPInfoStatus AnalyzeWebP(WebPInfo* const webp_info, + const WebPData* webp_data) { + ChunkData chunk_data; + MemBuffer mem_buffer; + WebPInfoStatus webp_info_status = WEBP_INFO_OK; + + InitMemBuffer(&mem_buffer, webp_data); + webp_info_status = ParseRIFFHeader(webp_info, &mem_buffer); + if (webp_info_status != WEBP_INFO_OK) goto Error; + + // Loop through all the chunks. Terminate immediately in case of error. + while (webp_info_status == WEBP_INFO_OK && MemDataSize(&mem_buffer) > 0) { + webp_info_status = ParseChunk(webp_info, &mem_buffer, &chunk_data); + if (webp_info_status != WEBP_INFO_OK) goto Error; + webp_info_status = ProcessChunk(&chunk_data, webp_info); + } + if (webp_info_status != WEBP_INFO_OK) goto Error; + if (webp_info->show_summary_) ShowSummary(webp_info); + + // Final check. + webp_info_status = Validate(webp_info); + + Error: + if (!webp_info->quiet_) { + if (webp_info_status == WEBP_INFO_OK) { + printf("No error detected.\n"); + } else { + printf("Errors detected.\n"); + } + if (webp_info->num_warnings_ > 0) { + printf("There were %d warning(s).\n", webp_info->num_warnings_); + } + } + return webp_info_status; +} + +static void Help(void) { + printf("Usage: webpinfo [options] in_files\n" + "Note: there could be multiple input files;\n" + " options must come before input files.\n" + "Options:\n" + " -version ........... Print version number and exit.\n" + " -quiet ............. Do not show chunk parsing information.\n" + " -diag .............. Show parsing error diagnosis.\n" + " -summary ........... Show chunk stats summary.\n" + " -bitstream_info .... Parse bitstream header.\n"); +} + +int main(int argc, const char* argv[]) { + int c, quiet = 0, show_diag = 0, show_summary = 0; + int parse_bitstream = 0; + WebPInfoStatus webp_info_status = WEBP_INFO_OK; + WebPInfo webp_info; + + INIT_WARGV(argc, argv); + + if (argc == 1) { + Help(); + FREE_WARGV_AND_RETURN(WEBP_INFO_OK); + } + + // Parse command-line input. + for (c = 1; c < argc; ++c) { + if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help") || + !strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) { + Help(); + FREE_WARGV_AND_RETURN(WEBP_INFO_OK); + } else if (!strcmp(argv[c], "-quiet")) { + quiet = 1; + } else if (!strcmp(argv[c], "-diag")) { + show_diag = 1; + } else if (!strcmp(argv[c], "-summary")) { + show_summary = 1; + } else if (!strcmp(argv[c], "-bitstream_info")) { + parse_bitstream = 1; + } else if (!strcmp(argv[c], "-version")) { + const int version = WebPGetDecoderVersion(); + printf("WebP Decoder version: %d.%d.%d\n", + (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); + FREE_WARGV_AND_RETURN(0); + } else { // Assume the remaining are all input files. + break; + } + } + + if (c == argc) { + Help(); + FREE_WARGV_AND_RETURN(WEBP_INFO_INVALID_COMMAND); + } + + // Process input files one by one. + for (; c < argc; ++c) { + WebPData webp_data; + const W_CHAR* in_file = NULL; + WebPInfoInit(&webp_info); + webp_info.quiet_ = quiet; + webp_info.show_diagnosis_ = show_diag; + webp_info.show_summary_ = show_summary; + webp_info.parse_bitstream_ = parse_bitstream; + in_file = GET_WARGV(argv, c); + if (in_file == NULL || + !ReadFileToWebPData((const char*)in_file, &webp_data)) { + webp_info_status = WEBP_INFO_INVALID_COMMAND; + WFPRINTF(stderr, "Failed to open input file %s.\n", in_file); + continue; + } + if (!webp_info.quiet_) WPRINTF("File: %s\n", in_file); + webp_info_status = AnalyzeWebP(&webp_info, &webp_data); + WebPDataClear(&webp_data); + } + FREE_WARGV_AND_RETURN(webp_info_status); +} diff --git a/internal/libwebp-1.2.1/examples/webpmux.c b/internal/libwebp-1.2.1/examples/webpmux.c new file mode 100644 index 0000000..b37d8c5 --- /dev/null +++ b/internal/libwebp-1.2.1/examples/webpmux.c @@ -0,0 +1,1233 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Simple command-line to create a WebP container file and to extract or strip +// relevant data from the container file. +// +// Authors: Vikas (vikaas.arora@gmail.com), +// Urvang (urvang@google.com) + +/* Usage examples: + + Create container WebP file: + webpmux -frame anim_1.webp +100+10+10 \ + -frame anim_2.webp +100+25+25+1 \ + -frame anim_3.webp +100+50+50+1 \ + -frame anim_4.webp +100 \ + -loop 10 -bgcolor 128,255,255,255 \ + -o out_animation_container.webp + + webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp + webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp + webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp + webpmux -set loop 1 in.webp -o out_looped.webp + + Extract relevant data from WebP container file: + webpmux -get frame n in.webp -o out_frame.webp + webpmux -get icc in.webp -o image_profile.icc + webpmux -get exif in.webp -o image_metadata.exif + webpmux -get xmp in.webp -o image_metadata.xmp + + Strip data from WebP Container file: + webpmux -strip icc in.webp -o out.webp + webpmux -strip exif in.webp -o out.webp + webpmux -strip xmp in.webp -o out.webp + + Change duration of frame intervals: + webpmux -duration 150 in.webp -o out.webp + webpmux -duration 33,2 in.webp -o out.webp + webpmux -duration 200,10,0 -duration 150,6,50 in.webp -o out.webp + + Misc: + webpmux -info in.webp + webpmux [ -h | -help ] + webpmux -version + webpmux argument_file_name +*/ + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include +#include +#include +#include +#include "webp/decode.h" +#include "webp/mux.h" +#include "../examples/example_util.h" +#include "../imageio/imageio_util.h" +#include "./unicode.h" + +//------------------------------------------------------------------------------ +// Config object to parse command-line arguments. + +typedef enum { + NIL_ACTION = 0, + ACTION_GET, + ACTION_SET, + ACTION_STRIP, + ACTION_INFO, + ACTION_HELP, + ACTION_DURATION +} ActionType; + +typedef enum { + NIL_SUBTYPE = 0, + SUBTYPE_ANMF, + SUBTYPE_LOOP, + SUBTYPE_BGCOLOR +} FeatureSubType; + +typedef struct { + FeatureSubType subtype_; + const char* filename_; + const char* params_; +} FeatureArg; + +typedef enum { + NIL_FEATURE = 0, + FEATURE_EXIF, + FEATURE_XMP, + FEATURE_ICCP, + FEATURE_ANMF, + FEATURE_DURATION, + FEATURE_LOOP, + FEATURE_BGCOLOR, + LAST_FEATURE +} FeatureType; + +static const char* const kFourccList[LAST_FEATURE] = { + NULL, "EXIF", "XMP ", "ICCP", "ANMF" +}; + +static const char* const kDescriptions[LAST_FEATURE] = { + NULL, "EXIF metadata", "XMP metadata", "ICC profile", + "Animation frame" +}; + +typedef struct { + CommandLineArguments cmd_args_; + + ActionType action_type_; + const char* input_; + const char* output_; + FeatureType type_; + FeatureArg* args_; + int arg_count_; +} Config; + +//------------------------------------------------------------------------------ +// Helper functions. + +static int CountOccurrences(const CommandLineArguments* const args, + const char* const arg) { + int i; + int num_occurences = 0; + + for (i = 0; i < args->argc_; ++i) { + if (!strcmp(args->argv_[i], arg)) { + ++num_occurences; + } + } + return num_occurences; +} + +static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = { + "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA", + "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA" +}; + +static const char* ErrorString(WebPMuxError err) { + assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA); + return kErrorMessages[-err]; +} + +#define RETURN_IF_ERROR(ERR_MSG) \ + if (err != WEBP_MUX_OK) { \ + fprintf(stderr, ERR_MSG); \ + return err; \ + } + +#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \ + if (err != WEBP_MUX_OK) { \ + fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \ + return err; \ + } + +#define ERROR_GOTO1(ERR_MSG, LABEL) \ + do { \ + fprintf(stderr, ERR_MSG); \ + ok = 0; \ + goto LABEL; \ + } while (0) + +#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \ + do { \ + fprintf(stderr, ERR_MSG, FORMAT_STR); \ + ok = 0; \ + goto LABEL; \ + } while (0) + +#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \ + do { \ + fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \ + ok = 0; \ + goto LABEL; \ + } while (0) + +static WebPMuxError DisplayInfo(const WebPMux* mux) { + int width, height; + uint32_t flag; + + WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height); + assert(err == WEBP_MUX_OK); // As WebPMuxCreate() was successful earlier. + printf("Canvas size: %d x %d\n", width, height); + + err = WebPMuxGetFeatures(mux, &flag); + RETURN_IF_ERROR("Failed to retrieve features\n"); + + if (flag == 0) { + printf("No features present.\n"); + return err; + } + + // Print the features present. + printf("Features present:"); + if (flag & ANIMATION_FLAG) printf(" animation"); + if (flag & ICCP_FLAG) printf(" ICC profile"); + if (flag & EXIF_FLAG) printf(" EXIF metadata"); + if (flag & XMP_FLAG) printf(" XMP metadata"); + if (flag & ALPHA_FLAG) printf(" transparency"); + printf("\n"); + + if (flag & ANIMATION_FLAG) { + const WebPChunkId id = WEBP_CHUNK_ANMF; + const char* const type_str = "frame"; + int nFrames; + + WebPMuxAnimParams params; + err = WebPMuxGetAnimationParams(mux, ¶ms); + assert(err == WEBP_MUX_OK); + printf("Background color : 0x%.8X Loop Count : %d\n", + params.bgcolor, params.loop_count); + + err = WebPMuxNumChunks(mux, id, &nFrames); + assert(err == WEBP_MUX_OK); + + printf("Number of %ss: %d\n", type_str, nFrames); + if (nFrames > 0) { + int i; + printf("No.: width height alpha x_offset y_offset "); + printf("duration dispose blend "); + printf("image_size compression\n"); + for (i = 1; i <= nFrames; i++) { + WebPMuxFrameInfo frame; + err = WebPMuxGetFrame(mux, i, &frame); + if (err == WEBP_MUX_OK) { + WebPBitstreamFeatures features; + const VP8StatusCode status = WebPGetFeatures( + frame.bitstream.bytes, frame.bitstream.size, &features); + assert(status == VP8_STATUS_OK); // Checked by WebPMuxCreate(). + (void)status; + printf("%3d: %5d %5d %5s %8d %8d ", i, features.width, + features.height, features.has_alpha ? "yes" : "no", + frame.x_offset, frame.y_offset); + { + const char* const dispose = + (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none" + : "background"; + const char* const blend = + (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no"; + printf("%8d %10s %5s ", frame.duration, dispose, blend); + } + printf("%10d %11s\n", (int)frame.bitstream.size, + (features.format == 1) ? "lossy" : + (features.format == 2) ? "lossless" : + "undefined"); + } + WebPDataClear(&frame.bitstream); + RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i); + } + } + } + + if (flag & ICCP_FLAG) { + WebPData icc_profile; + err = WebPMuxGetChunk(mux, "ICCP", &icc_profile); + assert(err == WEBP_MUX_OK); + printf("Size of the ICC profile data: %d\n", (int)icc_profile.size); + } + + if (flag & EXIF_FLAG) { + WebPData exif; + err = WebPMuxGetChunk(mux, "EXIF", &exif); + assert(err == WEBP_MUX_OK); + printf("Size of the EXIF metadata: %d\n", (int)exif.size); + } + + if (flag & XMP_FLAG) { + WebPData xmp; + err = WebPMuxGetChunk(mux, "XMP ", &xmp); + assert(err == WEBP_MUX_OK); + printf("Size of the XMP metadata: %d\n", (int)xmp.size); + } + + if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) { + WebPMuxFrameInfo image; + err = WebPMuxGetFrame(mux, 1, &image); + if (err == WEBP_MUX_OK) { + printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size); + } + WebPDataClear(&image.bitstream); + RETURN_IF_ERROR("Failed to retrieve the image\n"); + } + + return WEBP_MUX_OK; +} + +static void PrintHelp(void) { + printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n"); + printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n"); + printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n"); + printf(" INPUT -o OUTPUT\n"); + printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n"); + printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]" + "\n"); + printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n"); + printf(" webpmux -info INPUT\n"); + printf(" webpmux [-h|-help]\n"); + printf(" webpmux -version\n"); + printf(" webpmux argument_file_name\n"); + + printf("\n"); + printf("GET_OPTIONS:\n"); + printf(" Extract relevant data:\n"); + printf(" icc get ICC profile\n"); + printf(" exif get EXIF metadata\n"); + printf(" xmp get XMP metadata\n"); + printf(" frame n get nth frame\n"); + + printf("\n"); + printf("SET_OPTIONS:\n"); + printf(" Set color profile/metadata/parameters:\n"); + printf(" loop LOOP_COUNT set the loop count\n"); + printf(" bgcolor BACKGROUND_COLOR set the animation background color\n"); + printf(" icc file.icc set ICC profile\n"); + printf(" exif file.exif set EXIF metadata\n"); + printf(" xmp file.xmp set XMP metadata\n"); + printf(" where: 'file.icc' contains the ICC profile to be set,\n"); + printf(" 'file.exif' contains the EXIF metadata to be set\n"); + printf(" 'file.xmp' contains the XMP metadata to be set\n"); + + printf("\n"); + printf("DURATION_OPTIONS:\n"); + printf(" Set duration of selected frames:\n"); + printf(" duration set duration for each frames\n"); + printf(" duration,frame set duration of a particular frame\n"); + printf(" duration,start,end set duration of frames in the\n"); + printf(" interval [start,end])\n"); + printf(" where: 'duration' is the duration in milliseconds\n"); + printf(" 'start' is the start frame index\n"); + printf(" 'end' is the inclusive end frame index\n"); + printf(" The special 'end' value '0' means: last frame.\n"); + + printf("\n"); + printf("STRIP_OPTIONS:\n"); + printf(" Strip color profile/metadata:\n"); + printf(" icc strip ICC profile\n"); + printf(" exif strip EXIF metadata\n"); + printf(" xmp strip XMP metadata\n"); + + printf("\n"); + printf("FRAME_OPTIONS(i):\n"); + printf(" Create animation:\n"); + printf(" file_i +di+[xi+yi[+mi[bi]]]\n"); + printf(" where: 'file_i' is the i'th animation frame (WebP format),\n"); + printf(" 'di' is the pause duration before next frame,\n"); + printf(" 'xi','yi' specify the image offset for this frame,\n"); + printf(" 'mi' is the dispose method for this frame (0 or 1),\n"); + printf(" 'bi' is the blending method for this frame (+b or -b)" + "\n"); + + printf("\n"); + printf("LOOP_COUNT:\n"); + printf(" Number of times to repeat the animation.\n"); + printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n"); + + printf("\n"); + printf("BACKGROUND_COLOR:\n"); + printf(" Background color of the canvas.\n"); + printf(" A,R,G,B\n"); + printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 " + "specifying\n"); + printf(" the Alpha, Red, Green and Blue component values " + "respectively\n"); + printf(" [Default: 255,255,255,255]\n"); + + printf("\nINPUT & OUTPUT are in WebP format.\n"); + + printf("\nNote: The nature of EXIF, XMP and ICC data is not checked"); + printf(" and is assumed to be\nvalid.\n"); + printf("\nNote: if a single file name is passed as the argument, the " + "arguments will be\n"); + printf("tokenized from this file. The file name must not start with " + "the character '-'.\n"); +} + +static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) { + if ((info->x_offset | info->y_offset) & 1) { + fprintf(stderr, "Warning: odd offsets will be snapped to even values" + " (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset, + info->x_offset & ~1, info->y_offset & ~1); + } +} + +static int CreateMux(const char* const filename, WebPMux** mux) { + WebPData bitstream; + assert(mux != NULL); + if (!ExUtilReadFileToWebPData(filename, &bitstream)) return 0; + *mux = WebPMuxCreate(&bitstream, 1); + WebPDataClear(&bitstream); + if (*mux != NULL) return 1; + WFPRINTF(stderr, "Failed to create mux object from file %s.\n", + (const W_CHAR*)filename); + return 0; +} + +static int WriteData(const char* filename, const WebPData* const webpdata) { + int ok = 0; + FILE* fout = WSTRCMP(filename, "-") ? WFOPEN(filename, "wb") + : ImgIoUtilSetBinaryMode(stdout); + if (fout == NULL) { + WFPRINTF(stderr, "Error opening output WebP file %s!\n", + (const W_CHAR*)filename); + return 0; + } + if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) { + WFPRINTF(stderr, "Error writing file %s!\n", (const W_CHAR*)filename); + } else { + WFPRINTF(stderr, "Saved file %s (%d bytes)\n", + (const W_CHAR*)filename, (int)webpdata->size); + ok = 1; + } + if (fout != stdout) fclose(fout); + return ok; +} + +static int WriteWebP(WebPMux* const mux, const char* filename) { + int ok; + WebPData webp_data; + const WebPMuxError err = WebPMuxAssemble(mux, &webp_data); + if (err != WEBP_MUX_OK) { + fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err)); + return 0; + } + ok = WriteData(filename, &webp_data); + WebPDataClear(&webp_data); + return ok; +} + +static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) { + WebPMux* new_mux = WebPMuxNew(); + WebPMuxAnimParams p; + WebPMuxError err; + int i; + int ok = 1; + + if (new_mux == NULL) return NULL; + + err = WebPMuxGetAnimationParams(mux, &p); + if (err == WEBP_MUX_OK) { + err = WebPMuxSetAnimationParams(new_mux, &p); + if (err != WEBP_MUX_OK) { + ERROR_GOTO2("Error (%s) handling animation params.\n", + ErrorString(err), End); + } + } else { + /* it might not be an animation. Just keep moving. */ + } + + for (i = 1; i <= 3; ++i) { + WebPData metadata; + err = WebPMuxGetChunk(mux, kFourccList[i], &metadata); + if (err == WEBP_MUX_OK && metadata.size > 0) { + err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1); + if (err != WEBP_MUX_OK) { + ERROR_GOTO1("Error transferring metadata in DuplicateMux().", End); + } + } + } + + End: + if (!ok) { + WebPMuxDelete(new_mux); + new_mux = NULL; + } + return new_mux; +} + +static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) { + int dispose_method, unused; + char plus_minus, blend_method; + const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration, + &info->x_offset, &info->y_offset, &dispose_method, + &plus_minus, &blend_method, &unused); + switch (num_args) { + case 1: + info->x_offset = info->y_offset = 0; // fall through + case 3: + dispose_method = 0; // fall through + case 4: + plus_minus = '+'; + blend_method = 'b'; // fall through + case 6: + break; + case 2: + case 5: + default: + return 0; + } + + WarnAboutOddOffset(info); + + // Note: The validity of the following conversion is checked by + // WebPMuxPushFrame(). + info->dispose_method = (WebPMuxAnimDispose)dispose_method; + + if (blend_method != 'b') return 0; + if (plus_minus != '-' && plus_minus != '+') return 0; + info->blend_method = + (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND; + return 1; +} + +static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) { + uint32_t a, r, g, b; + if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0; + if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0; + *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0); + return 1; +} + +//------------------------------------------------------------------------------ +// Clean-up. + +static void DeleteConfig(Config* const config) { + if (config != NULL) { + free(config->args_); + ExUtilDeleteCommandLineArguments(&config->cmd_args_); + memset(config, 0, sizeof(*config)); + } +} + +//------------------------------------------------------------------------------ +// Parsing. + +// Basic syntactic checks on the command-line arguments. +// Returns 1 on valid, 0 otherwise. +// Also fills up num_feature_args to be number of feature arguments given. +// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5). +static int ValidateCommandLine(const CommandLineArguments* const cmd_args, + int* num_feature_args) { + int num_frame_args; + int num_loop_args; + int num_bgcolor_args; + int num_durations_args; + int ok = 1; + + assert(num_feature_args != NULL); + *num_feature_args = 0; + + // Simple checks. + if (CountOccurrences(cmd_args, "-get") > 1) { + ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate); + } + if (CountOccurrences(cmd_args, "-set") > 1) { + ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate); + } + if (CountOccurrences(cmd_args, "-strip") > 1) { + ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate); + } + if (CountOccurrences(cmd_args, "-info") > 1) { + ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate); + } + if (CountOccurrences(cmd_args, "-o") > 1) { + ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate); + } + + // Compound checks. + num_frame_args = CountOccurrences(cmd_args, "-frame"); + num_loop_args = CountOccurrences(cmd_args, "-loop"); + num_bgcolor_args = CountOccurrences(cmd_args, "-bgcolor"); + num_durations_args = CountOccurrences(cmd_args, "-duration"); + + if (num_loop_args > 1) { + ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate); + } + if (num_bgcolor_args > 1) { + ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate); + } + + if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) { + ERROR_GOTO1("ERROR: Loop count and background color are relevant only in " + "case of animation.\n", ErrValidate); + } + if (num_durations_args > 0 && num_frame_args != 0) { + ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n", + ErrValidate); + } + + assert(ok == 1); + if (num_durations_args > 0) { + *num_feature_args = num_durations_args; + } else if (num_frame_args == 0) { + // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action). + *num_feature_args = 1; + } else { + // Multiple arguments ('set' action for animation) + *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args; + } + + ErrValidate: + return ok; +} + +#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION) + +#define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE) + +#define CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL) \ + if (argc < i + (NUM)) { \ + fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \ + goto LABEL; \ + } + +#define CHECK_NUM_ARGS_AT_MOST(NUM, LABEL) \ + if (argc > i + (NUM)) { \ + fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \ + goto LABEL; \ + } + +#define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL) \ + CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \ + CHECK_NUM_ARGS_AT_MOST(NUM, LABEL); + +// Parses command-line arguments to fill up config object. Also performs some +// semantic checks. unicode_argv contains wchar_t arguments or is null. +static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) { + int i = 0; + int feature_arg_index = 0; + int ok = 1; + int argc = config->cmd_args_.argc_; + const char* const* argv = config->cmd_args_.argv_; + // Unicode file paths will be used if available. + const char* const* wargv = + (unicode_argv != NULL) ? (const char**)(unicode_argv + 1) : argv; + + while (i < argc) { + FeatureArg* const arg = &config->args_[feature_arg_index]; + if (argv[i][0] == '-') { // One of the action types or output. + if (!strcmp(argv[i], "-set")) { + if (ACTION_IS_NIL) { + config->action_type_ = ACTION_SET; + } else { + ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); + } + ++i; + } else if (!strcmp(argv[i], "-duration")) { + CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); + if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) { + config->action_type_ = ACTION_DURATION; + } else { + ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); + } + if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) { + config->type_ = FEATURE_DURATION; + } else { + ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse); + } + arg->params_ = argv[i + 1]; + ++feature_arg_index; + i += 2; + } else if (!strcmp(argv[i], "-get")) { + if (ACTION_IS_NIL) { + config->action_type_ = ACTION_GET; + } else { + ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); + } + ++i; + } else if (!strcmp(argv[i], "-strip")) { + if (ACTION_IS_NIL) { + config->action_type_ = ACTION_STRIP; + config->arg_count_ = 0; + } else { + ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); + } + ++i; + } else if (!strcmp(argv[i], "-frame")) { + CHECK_NUM_ARGS_AT_LEAST(3, ErrParse); + if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) { + config->action_type_ = ACTION_SET; + } else { + ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); + } + if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) { + config->type_ = FEATURE_ANMF; + } else { + ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse); + } + arg->subtype_ = SUBTYPE_ANMF; + arg->filename_ = argv[i + 1]; + arg->params_ = argv[i + 2]; + ++feature_arg_index; + i += 3; + } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) { + CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); + if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) { + config->action_type_ = ACTION_SET; + } else { + ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); + } + if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) { + config->type_ = FEATURE_ANMF; + } else { + ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse); + } + arg->subtype_ = + !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR; + arg->params_ = argv[i + 1]; + ++feature_arg_index; + i += 2; + } else if (!strcmp(argv[i], "-o")) { + CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); + config->output_ = wargv[i + 1]; + i += 2; + } else if (!strcmp(argv[i], "-info")) { + CHECK_NUM_ARGS_EXACTLY(2, ErrParse); + if (config->action_type_ != NIL_ACTION) { + ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); + } else { + config->action_type_ = ACTION_INFO; + config->arg_count_ = 0; + config->input_ = wargv[i + 1]; + } + i += 2; + } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) { + PrintHelp(); + DeleteConfig(config); + LOCAL_FREE((W_CHAR** const)unicode_argv); + exit(0); + } else if (!strcmp(argv[i], "-version")) { + const int version = WebPGetMuxVersion(); + printf("%d.%d.%d\n", + (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); + DeleteConfig(config); + LOCAL_FREE((W_CHAR** const)unicode_argv); + exit(0); + } else if (!strcmp(argv[i], "--")) { + if (i < argc - 1) { + ++i; + if (config->input_ == NULL) { + config->input_ = wargv[i]; + } else { + ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n", + argv[i], ErrParse); + } + } + break; + } else { + ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse); + } + } else { // One of the feature types or input. + if (ACTION_IS_NIL) { + ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n", + ErrParse); + } + if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") || + !strcmp(argv[i], "xmp")) { + if (FEATURETYPE_IS_NIL) { + config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP : + (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP; + } else { + ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse); + } + if (config->action_type_ == ACTION_SET) { + CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); + arg->filename_ = wargv[i + 1]; + ++feature_arg_index; + i += 2; + } else { + ++i; + } + } else if (!strcmp(argv[i], "frame") && + (config->action_type_ == ACTION_GET)) { + CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); + config->type_ = FEATURE_ANMF; + arg->params_ = argv[i + 1]; + ++feature_arg_index; + i += 2; + } else if (!strcmp(argv[i], "loop") && + (config->action_type_ == ACTION_SET)) { + CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); + config->type_ = FEATURE_LOOP; + arg->params_ = argv[i + 1]; + ++feature_arg_index; + i += 2; + } else if (!strcmp(argv[i], "bgcolor") && + (config->action_type_ == ACTION_SET)) { + CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); + config->type_ = FEATURE_BGCOLOR; + arg->params_ = argv[i + 1]; + ++feature_arg_index; + i += 2; + } else { // Assume input file. + if (config->input_ == NULL) { + config->input_ = wargv[i]; + } else { + ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n", + argv[i], ErrParse); + } + ++i; + } + } + } + ErrParse: + return ok; +} + +// Additional checks after config is filled. +static int ValidateConfig(Config* const config) { + int ok = 1; + + // Action. + if (ACTION_IS_NIL) { + ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2); + } + + // Feature type. + if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) { + ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2); + } + + // Input file. + if (config->input_ == NULL) { + if (config->action_type_ != ACTION_SET) { + ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2); + } else if (config->type_ != FEATURE_ANMF) { + ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2); + } + } + + // Output file. + if (config->output_ == NULL && config->action_type_ != ACTION_INFO) { + ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2); + } + + ErrValidate2: + return ok; +} + +// Create config object from command-line arguments. +static int InitializeConfig(int argc, const char* argv[], Config* const config, + const W_CHAR** const unicode_argv) { + int num_feature_args = 0; + int ok; + + memset(config, 0, sizeof(*config)); + + ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_); + if (!ok) return 0; + + // Validate command-line arguments. + if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) { + ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1); + } + + config->arg_count_ = num_feature_args; + config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_)); + if (config->args_ == NULL) { + ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1); + } + + // Parse command-line. + if (!ParseCommandLine(config, unicode_argv) || !ValidateConfig(config)) { + ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1); + } + + Err1: + return ok; +} + +#undef ACTION_IS_NIL +#undef FEATURETYPE_IS_NIL +#undef CHECK_NUM_ARGS_AT_LEAST +#undef CHECK_NUM_ARGS_AT_MOST +#undef CHECK_NUM_ARGS_EXACTLY + +//------------------------------------------------------------------------------ +// Processing. + +static int GetFrame(const WebPMux* mux, const Config* config) { + WebPMuxError err = WEBP_MUX_OK; + WebPMux* mux_single = NULL; + int num = 0; + int ok = 1; + int parse_error = 0; + const WebPChunkId id = WEBP_CHUNK_ANMF; + WebPMuxFrameInfo info; + WebPDataInit(&info.bitstream); + + num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error); + if (num < 0) { + ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet); + } + if (parse_error) goto ErrGet; + + err = WebPMuxGetFrame(mux, num, &info); + if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND; + if (err != WEBP_MUX_OK) { + ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n", + ErrorString(err), num, ErrGet); + } + + mux_single = WebPMuxNew(); + if (mux_single == NULL) { + err = WEBP_MUX_MEMORY_ERROR; + ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n", + ErrorString(err), ErrGet); + } + err = WebPMuxSetImage(mux_single, &info.bitstream, 1); + if (err != WEBP_MUX_OK) { + ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n", + ErrorString(err), ErrGet); + } + + ok = WriteWebP(mux_single, config->output_); + + ErrGet: + WebPDataClear(&info.bitstream); + WebPMuxDelete(mux_single); + return ok && !parse_error; +} + +// Read and process config. +static int Process(const Config* config) { + WebPMux* mux = NULL; + WebPData chunk; + WebPMuxError err = WEBP_MUX_OK; + int ok = 1; + + switch (config->action_type_) { + case ACTION_GET: { + ok = CreateMux(config->input_, &mux); + if (!ok) goto Err2; + switch (config->type_) { + case FEATURE_ANMF: + ok = GetFrame(mux, config); + break; + + case FEATURE_ICCP: + case FEATURE_EXIF: + case FEATURE_XMP: + err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk); + if (err != WEBP_MUX_OK) { + ERROR_GOTO3("ERROR (%s): Could not get the %s.\n", + ErrorString(err), kDescriptions[config->type_], Err2); + } + ok = WriteData(config->output_, &chunk); + break; + + default: + ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2); + break; + } + break; + } + case ACTION_SET: { + switch (config->type_) { + case FEATURE_ANMF: { + int i; + WebPMuxAnimParams params = { 0xFFFFFFFF, 0 }; + mux = WebPMuxNew(); + if (mux == NULL) { + ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n", + ErrorString(WEBP_MUX_MEMORY_ERROR), Err2); + } + for (i = 0; i < config->arg_count_; ++i) { + switch (config->args_[i].subtype_) { + case SUBTYPE_BGCOLOR: { + uint32_t bgcolor; + ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor); + if (!ok) { + ERROR_GOTO1("ERROR: Could not parse the background color \n", + Err2); + } + params.bgcolor = bgcolor; + break; + } + case SUBTYPE_LOOP: { + int parse_error = 0; + const int loop_count = + ExUtilGetInt(config->args_[i].params_, 10, &parse_error); + if (loop_count < 0 || loop_count > 65535) { + // Note: This is only a 'necessary' condition for loop_count + // to be valid. The 'sufficient' conditioned in checked in + // WebPMuxSetAnimationParams() method called later. + ERROR_GOTO1("ERROR: Loop count must be in the range 0 to " + "65535.\n", Err2); + } + ok = !parse_error; + if (!ok) goto Err2; + params.loop_count = loop_count; + break; + } + case SUBTYPE_ANMF: { + WebPMuxFrameInfo frame; + frame.id = WEBP_CHUNK_ANMF; + ok = ExUtilReadFileToWebPData(config->args_[i].filename_, + &frame.bitstream); + if (!ok) goto Err2; + ok = ParseFrameArgs(config->args_[i].params_, &frame); + if (!ok) { + WebPDataClear(&frame.bitstream); + ERROR_GOTO1("ERROR: Could not parse frame properties.\n", + Err2); + } + err = WebPMuxPushFrame(mux, &frame, 1); + WebPDataClear(&frame.bitstream); + if (err != WEBP_MUX_OK) { + ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d." + "\n", ErrorString(err), i, Err2); + } + break; + } + default: { + ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2); + break; + } + } + } + err = WebPMuxSetAnimationParams(mux, ¶ms); + if (err != WEBP_MUX_OK) { + ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n", + ErrorString(err), Err2); + } + break; + } + + case FEATURE_ICCP: + case FEATURE_EXIF: + case FEATURE_XMP: { + ok = CreateMux(config->input_, &mux); + if (!ok) goto Err2; + ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk); + if (!ok) goto Err2; + err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1); + WebPDataClear(&chunk); + if (err != WEBP_MUX_OK) { + ERROR_GOTO3("ERROR (%s): Could not set the %s.\n", + ErrorString(err), kDescriptions[config->type_], Err2); + } + break; + } + case FEATURE_LOOP: { + WebPMuxAnimParams params = { 0xFFFFFFFF, 0 }; + int parse_error = 0; + const int loop_count = + ExUtilGetInt(config->args_[0].params_, 10, &parse_error); + if (loop_count < 0 || loop_count > 65535 || parse_error) { + ERROR_GOTO1("ERROR: Loop count must be in the range 0 to 65535.\n", + Err2); + } + ok = CreateMux(config->input_, &mux); + if (!ok) goto Err2; + ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK); + if (!ok) { + ERROR_GOTO1("ERROR: input file does not seem to be an animation.\n", + Err2); + } + params.loop_count = loop_count; + err = WebPMuxSetAnimationParams(mux, ¶ms); + ok = (err == WEBP_MUX_OK); + if (!ok) { + ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n", + ErrorString(err), Err2); + } + break; + } + case FEATURE_BGCOLOR: { + WebPMuxAnimParams params = { 0xFFFFFFFF, 0 }; + uint32_t bgcolor; + ok = ParseBgcolorArgs(config->args_[0].params_, &bgcolor); + if (!ok) { + ERROR_GOTO1("ERROR: Could not parse the background color.\n", + Err2); + } + ok = CreateMux(config->input_, &mux); + if (!ok) goto Err2; + ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK); + if (!ok) { + ERROR_GOTO1("ERROR: input file does not seem to be an animation.\n", + Err2); + } + params.bgcolor = bgcolor; + err = WebPMuxSetAnimationParams(mux, ¶ms); + ok = (err == WEBP_MUX_OK); + if (!ok) { + ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n", + ErrorString(err), Err2); + } + break; + } + default: { + ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2); + break; + } + } + ok = WriteWebP(mux, config->output_); + break; + } + case ACTION_DURATION: { + int num_frames; + ok = CreateMux(config->input_, &mux); + if (!ok) goto Err2; + err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames); + ok = (err == WEBP_MUX_OK); + if (!ok) { + ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2); + } + if (num_frames == 0) { + fprintf(stderr, "Doesn't look like the source is animated. " + "Skipping duration setting.\n"); + ok = WriteWebP(mux, config->output_); + if (!ok) goto Err2; + } else { + int i; + int* durations = NULL; + WebPMux* new_mux = DuplicateMuxHeader(mux); + if (new_mux == NULL) goto Err2; + durations = (int*)WebPMalloc((size_t)num_frames * sizeof(*durations)); + if (durations == NULL) goto Err2; + for (i = 0; i < num_frames; ++i) durations[i] = -1; + + // Parse intervals to process. + for (i = 0; i < config->arg_count_; ++i) { + int k; + int args[3]; + int duration, start, end; + const int nb_args = ExUtilGetInts(config->args_[i].params_, + 10, 3, args); + ok = (nb_args >= 1); + if (!ok) goto Err3; + duration = args[0]; + if (duration < 0) { + ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3); + } + + if (nb_args == 1) { // only duration is present -> use full interval + start = 1; + end = num_frames; + } else { + start = args[1]; + if (start <= 0) { + start = 1; + } else if (start > num_frames) { + start = num_frames; + } + end = (nb_args >= 3) ? args[2] : start; + if (end == 0 || end > num_frames) end = num_frames; + } + + for (k = start; k <= end; ++k) { + assert(k >= 1 && k <= num_frames); + durations[k - 1] = duration; + } + } + + // Apply non-negative durations to their destination frames. + for (i = 1; i <= num_frames; ++i) { + WebPMuxFrameInfo frame; + err = WebPMuxGetFrame(mux, i, &frame); + if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) { + ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3); + } + if (durations[i - 1] >= 0) frame.duration = durations[i - 1]; + err = WebPMuxPushFrame(new_mux, &frame, 1); + if (err != WEBP_MUX_OK) { + ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3); + } + WebPDataClear(&frame.bitstream); + } + WebPMuxDelete(mux); + ok = WriteWebP(new_mux, config->output_); + mux = new_mux; // transfer for the WebPMuxDelete() call + new_mux = NULL; + + Err3: + WebPFree(durations); + WebPMuxDelete(new_mux); + if (!ok) goto Err2; + } + break; + } + case ACTION_STRIP: { + ok = CreateMux(config->input_, &mux); + if (!ok) goto Err2; + if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF || + config->type_ == FEATURE_XMP) { + err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]); + if (err != WEBP_MUX_OK) { + ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n", + ErrorString(err), kDescriptions[config->type_], Err2); + } + } else { + ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2); + break; + } + ok = WriteWebP(mux, config->output_); + break; + } + case ACTION_INFO: { + ok = CreateMux(config->input_, &mux); + if (!ok) goto Err2; + ok = (DisplayInfo(mux) == WEBP_MUX_OK); + break; + } + default: { + assert(0); // Invalid action. + break; + } + } + + Err2: + WebPMuxDelete(mux); + return ok; +} + +//------------------------------------------------------------------------------ +// Main. + +int main(int argc, const char* argv[]) { + Config config; + int ok; + + INIT_WARGV(argc, argv); + + ok = InitializeConfig(argc - 1, argv + 1, &config, GET_WARGV_OR_NULL()); + if (ok) { + ok = Process(&config); + } else { + PrintHelp(); + } + DeleteConfig(&config); + FREE_WARGV_AND_RETURN(!ok); +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/extras/Makefile.am b/internal/libwebp-1.2.1/extras/Makefile.am new file mode 100644 index 0000000..7e29888 --- /dev/null +++ b/internal/libwebp-1.2.1/extras/Makefile.am @@ -0,0 +1,44 @@ +AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir) +AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src +noinst_LTLIBRARIES = libwebpextras.la + +noinst_HEADERS = +noinst_HEADERS += ../src/webp/types.h + +libwebpextras_la_SOURCES = +libwebpextras_la_SOURCES += extras.c extras.h quality_estimate.c + +libwebpextras_la_CPPFLAGS = $(AM_CPPFLAGS) +libwebpextras_la_LDFLAGS = -lm +libwebpextras_la_LIBADD = ../src/libwebp.la + +noinst_PROGRAMS = +noinst_PROGRAMS += webp_quality +if BUILD_DEMUX + noinst_PROGRAMS += get_disto +endif +if BUILD_VWEBP_SDL + noinst_PROGRAMS += vwebp_sdl +endif + +get_disto_SOURCES = get_disto.c +get_disto_CPPFLAGS = $(AM_CPPFLAGS) +get_disto_LDADD = +get_disto_LDADD += ../imageio/libimageio_util.la +get_disto_LDADD += ../imageio/libimagedec.la +get_disto_LDADD += ../src/libwebp.la +get_disto_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS) + +webp_quality_SOURCES = webp_quality.c +webp_quality_CPPFLAGS = $(AM_CPPFLAGS) +webp_quality_LDADD = +webp_quality_LDADD += ../imageio/libimageio_util.la +webp_quality_LDADD += libwebpextras.la +webp_quality_LDADD += ../src/libwebp.la + +vwebp_sdl_SOURCES = vwebp_sdl.c webp_to_sdl.c webp_to_sdl.h +vwebp_sdl_CPPFLAGS = $(AM_CPPFLAGS) $(SDL_INCLUDES) +vwebp_sdl_LDADD = +vwebp_sdl_LDADD += ../imageio/libimageio_util.la +vwebp_sdl_LDADD += ../src/libwebp.la +vwebp_sdl_LDADD += $(SDL_LIBS) diff --git a/internal/libwebp-1.2.1/extras/extras.c b/internal/libwebp-1.2.1/extras/extras.c new file mode 100644 index 0000000..a66979a --- /dev/null +++ b/internal/libwebp-1.2.1/extras/extras.c @@ -0,0 +1,162 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Additional WebP utilities. +// + +#include "extras/extras.h" +#include "webp/format_constants.h" +#include "src/dsp/dsp.h" + +#include +#include + +#define XTRA_MAJ_VERSION 1 +#define XTRA_MIN_VERSION 2 +#define XTRA_REV_VERSION 1 + +//------------------------------------------------------------------------------ + +int WebPGetExtrasVersion(void) { + return (XTRA_MAJ_VERSION << 16) | (XTRA_MIN_VERSION << 8) | XTRA_REV_VERSION; +} + +//------------------------------------------------------------------------------ + +int WebPImportGray(const uint8_t* gray_data, WebPPicture* pic) { + int y, width, uv_width; + if (pic == NULL || gray_data == NULL) return 0; + pic->colorspace = WEBP_YUV420; + if (!WebPPictureAlloc(pic)) return 0; + width = pic->width; + uv_width = (width + 1) >> 1; + for (y = 0; y < pic->height; ++y) { + memcpy(pic->y + y * pic->y_stride, gray_data, width); + gray_data += width; // <- we could use some 'data_stride' here if needed + if ((y & 1) == 0) { + memset(pic->u + (y >> 1) * pic->uv_stride, 128, uv_width); + memset(pic->v + (y >> 1) * pic->uv_stride, 128, uv_width); + } + } + return 1; +} + +int WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic) { + int x, y; + uint32_t* dst; + if (pic == NULL || rgb565 == NULL) return 0; + pic->colorspace = WEBP_YUV420; + pic->use_argb = 1; + if (!WebPPictureAlloc(pic)) return 0; + dst = pic->argb; + for (y = 0; y < pic->height; ++y) { + const int width = pic->width; + for (x = 0; x < width; ++x) { +#if defined(WEBP_SWAP_16BIT_CSP) && (WEBP_SWAP_16BIT_CSP == 1) + const uint32_t rg = rgb565[2 * x + 1]; + const uint32_t gb = rgb565[2 * x + 0]; +#else + const uint32_t rg = rgb565[2 * x + 0]; + const uint32_t gb = rgb565[2 * x + 1]; +#endif + uint32_t r = rg & 0xf8; + uint32_t g = ((rg << 5) | (gb >> 3)) & 0xfc; + uint32_t b = (gb << 5); + // dithering + r = r | (r >> 5); + g = g | (g >> 6); + b = b | (b >> 5); + dst[x] = (0xffu << 24) | (r << 16) | (g << 8) | b; + } + rgb565 += 2 * width; + dst += pic->argb_stride; + } + return 1; +} + +int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic) { + int x, y; + uint32_t* dst; + if (pic == NULL || rgb4444 == NULL) return 0; + pic->colorspace = WEBP_YUV420; + pic->use_argb = 1; + if (!WebPPictureAlloc(pic)) return 0; + dst = pic->argb; + for (y = 0; y < pic->height; ++y) { + const int width = pic->width; + for (x = 0; x < width; ++x) { +#if defined(WEBP_SWAP_16BIT_CSP) && (WEBP_SWAP_16BIT_CSP == 1) + const uint32_t rg = rgb4444[2 * x + 1]; + const uint32_t ba = rgb4444[2 * x + 0]; +#else + const uint32_t rg = rgb4444[2 * x + 0]; + const uint32_t ba = rgb4444[2 * x + 1]; +#endif + uint32_t r = rg & 0xf0; + uint32_t g = (rg << 4); + uint32_t b = (ba & 0xf0); + uint32_t a = (ba << 4); + // dithering + r = r | (r >> 4); + g = g | (g >> 4); + b = b | (b >> 4); + a = a | (a >> 4); + dst[x] = (a << 24) | (r << 16) | (g << 8) | b; + } + rgb4444 += 2 * width; + dst += pic->argb_stride; + } + return 1; +} + +int WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride, + const uint32_t palette[], int palette_size, + WebPPicture* pic) { + int x, y; + uint32_t* dst; + // 256 as the input buffer is uint8_t. + assert(MAX_PALETTE_SIZE <= 256); + if (pic == NULL || indexed == NULL || indexed_stride < pic->width || + palette == NULL || palette_size > MAX_PALETTE_SIZE || palette_size <= 0) { + return 0; + } + pic->use_argb = 1; + if (!WebPPictureAlloc(pic)) return 0; + dst = pic->argb; + for (y = 0; y < pic->height; ++y) { + for (x = 0; x < pic->width; ++x) { + // Make sure we are within the palette. + if (indexed[x] >= palette_size) { + WebPPictureFree(pic); + return 0; + } + dst[x] = palette[indexed[x]]; + } + indexed += indexed_stride; + dst += pic->argb_stride; + } + return 1; +} + +//------------------------------------------------------------------------------ + +int WebPUnmultiplyARGB(WebPPicture* pic) { + int y; + uint32_t* dst; + if (pic == NULL || pic->use_argb != 1 || pic->argb == NULL) return 0; + WebPInitAlphaProcessing(); + dst = pic->argb; + for (y = 0; y < pic->height; ++y) { + WebPMultARGBRow(dst, pic->width, /*inverse=*/1); + dst += pic->argb_stride; + } + return 1; +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/extras/extras.h b/internal/libwebp-1.2.1/extras/extras.h new file mode 100644 index 0000000..c084682 --- /dev/null +++ b/internal/libwebp-1.2.1/extras/extras.h @@ -0,0 +1,77 @@ +// 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. +// ----------------------------------------------------------------------------- +// + +#ifndef WEBP_EXTRAS_EXTRAS_H_ +#define WEBP_EXTRAS_EXTRAS_H_ + +#include "webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "webp/encode.h" + +#define WEBP_EXTRAS_ABI_VERSION 0x0002 // MAJOR(8b) + MINOR(8b) + +//------------------------------------------------------------------------------ + +// Returns the version number of the extras library, packed in hexadecimal using +// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507. +WEBP_EXTERN int WebPGetExtrasVersion(void); + +//------------------------------------------------------------------------------ +// Ad-hoc colorspace importers. + +// Import luma sample (gray scale image) into 'picture'. The 'picture' +// width and height must be set prior to calling this function. +WEBP_EXTERN int WebPImportGray(const uint8_t* gray, WebPPicture* picture); + +// Import rgb sample in RGB565 packed format into 'picture'. The 'picture' +// width and height must be set prior to calling this function. +WEBP_EXTERN int WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic); + +// Import rgb sample in RGB4444 packed format into 'picture'. The 'picture' +// width and height must be set prior to calling this function. +WEBP_EXTERN int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic); + +// Import a color mapped image. The number of colors is less or equal to +// MAX_PALETTE_SIZE. 'pic' must have been initialized. Its content, if any, +// will be discarded. Returns 'false' in case of error, or if indexed[] contains +// invalid indices. +WEBP_EXTERN int +WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride, + const uint32_t palette[], int palette_size, + WebPPicture* pic); + +// Convert the ARGB content of 'pic' from associated to unassociated. +// 'pic' can be for instance the result of calling of some WebPPictureImportXXX +// functions, with pic->use_argb set to 'true'. It is assumed (and not checked) +// that the pre-multiplied r/g/b values as less or equal than the alpha value. +// Return false in case of error (invalid parameter, ...). +WEBP_EXTERN int WebPUnmultiplyARGB(WebPPicture* pic); + +//------------------------------------------------------------------------------ + +// Parse a bitstream, search for VP8 (lossy) header and report a +// rough estimation of the quality factor used for compressing the bitstream. +// If the bitstream is in lossless format, the special value '101' is returned. +// Otherwise (lossy bitstream), the returned value is in the range [0..100]. +// Any error (invalid bitstream, animated WebP, incomplete header, etc.) +// will return a value of -1. +WEBP_EXTERN int VP8EstimateQuality(const uint8_t* const data, size_t size); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_EXTRAS_EXTRAS_H_ diff --git a/internal/libwebp-1.2.1/extras/get_disto.c b/internal/libwebp-1.2.1/extras/get_disto.c new file mode 100644 index 0000000..151bead --- /dev/null +++ b/internal/libwebp-1.2.1/extras/get_disto.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. +// ----------------------------------------------------------------------------- +// +// Simple tool to load two webp/png/jpg/tiff files and compute PSNR/SSIM. +// This is mostly a wrapper around WebPPictureDistortion(). +// +/* + gcc -o get_disto get_disto.c -O3 -I../ -L../examples -L../imageio \ + -lexample_util -limageio_util -limagedec -lwebp -L/opt/local/lib \ + -lpng -lz -ljpeg -ltiff -lm -lpthread +*/ +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include +#include +#include + +#include "webp/encode.h" +#include "imageio/image_dec.h" +#include "imageio/imageio_util.h" +#include "../examples/unicode.h" + +static size_t ReadPicture(const char* const filename, WebPPicture* const pic, + int keep_alpha) { + const uint8_t* data = NULL; + size_t data_size = 0; + WebPImageReader reader = NULL; + int ok = ImgIoUtilReadFile(filename, &data, &data_size); + if (!ok) goto End; + + pic->use_argb = 1; // force ARGB + +#ifdef HAVE_WINCODEC_H + // Try to decode the file using WIC falling back to the other readers for + // e.g., WebP. + ok = ReadPictureWithWIC(filename, pic, keep_alpha, NULL); + if (ok) goto End; +#endif + reader = WebPGuessImageReader(data, data_size); + ok = reader(data, data_size, pic, keep_alpha, NULL); + + End: + if (!ok) { + WFPRINTF(stderr, "Error! Could not process file %s\n", + (const W_CHAR*)filename); + } + free((void*)data); + return ok ? data_size : 0; +} + +static void RescalePlane(uint8_t* plane, int width, int height, + int x_stride, int y_stride, int max) { + const uint32_t factor = (max > 0) ? (255u << 16) / max : 0; + int x, y; + for (y = 0; y < height; ++y) { + uint8_t* const ptr = plane + y * y_stride; + for (x = 0; x < width * x_stride; x += x_stride) { + const uint32_t diff = (ptr[x] * factor + (1 << 15)) >> 16; + ptr[x] = diff; + } + } +} + +// Return the max absolute difference. +static int DiffScaleChannel(uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + int x_stride, int w, int h, int do_scaling) { + int x, y; + int max = 0; + for (y = 0; y < h; ++y) { + uint8_t* const ptr1 = src1 + y * stride1; + const uint8_t* const ptr2 = src2 + y * stride2; + for (x = 0; x < w * x_stride; x += x_stride) { + const int diff = abs(ptr1[x] - ptr2[x]); + if (diff > max) max = diff; + ptr1[x] = diff; + } + } + + if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max); + return max; +} + +//------------------------------------------------------------------------------ +// SSIM calculation. We re-implement these functions here, out of dsp/, to avoid +// breaking the library's hidden visibility. This code duplication avoids the +// bigger annoyance of having to open up internal details of libdsp... + +#define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1 + +// struct for accumulating statistical moments +typedef struct { + 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. +} DistoStats; + +// hat-shaped filter. Sum of coefficients is equal to 16. +static const uint32_t kWeight[2 * SSIM_KERNEL + 1] = { 1, 2, 3, 4, 3, 2, 1 }; + +static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) { + const uint32_t N = stats->w; + 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 +} + +static double SSIMGetClipped(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + int xo, int yo, int W, int H) { + DistoStats stats = { 0, 0, 0, 0, 0, 0 }; + const int ymin = (yo - SSIM_KERNEL < 0) ? 0 : yo - SSIM_KERNEL; + const int ymax = (yo + SSIM_KERNEL > H - 1) ? H - 1 : yo + SSIM_KERNEL; + const int xmin = (xo - SSIM_KERNEL < 0) ? 0 : xo - SSIM_KERNEL; + const int xmax = (xo + SSIM_KERNEL > W - 1) ? W - 1 : xo + SSIM_KERNEL; + int x, y; + src1 += ymin * stride1; + src2 += ymin * stride2; + for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) { + for (x = xmin; x <= xmax; ++x) { + const uint32_t w = kWeight[SSIM_KERNEL + x - xo] + * kWeight[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 SSIMCalculation(&stats); +} + +// Compute SSIM-score map. Return -1 in case of error, max diff otherwise. +static int SSIMScaleChannel(uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + int x_stride, int w, int h, int do_scaling) { + int x, y; + int max = 0; + uint8_t* const plane1 = (uint8_t*)malloc(2 * w * h * sizeof(*plane1)); + uint8_t* const plane2 = plane1 + w * h; + if (plane1 == NULL) return -1; + + // extract plane + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + plane1[x + y * w] = src1[x * x_stride + y * stride1]; + plane2[x + y * w] = src2[x * x_stride + y * stride2]; + } + } + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + const double ssim = SSIMGetClipped(plane1, w, plane2, w, x, y, w, h); + int diff = (int)(255 * (1. - ssim)); + if (diff < 0) { + diff = 0; + } else if (diff > max) { + max = diff; + } + src1[x * x_stride + y * stride1] = (diff > 255) ? 255u : (uint8_t)diff; + } + } + free(plane1); + + if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max); + return max; +} + +// Convert an argb picture to luminance. +static void ConvertToGray(WebPPicture* const pic) { + int x, y; + assert(pic != NULL); + assert(pic->use_argb); + for (y = 0; y < pic->height; ++y) { + uint32_t* const row = &pic->argb[y * pic->argb_stride]; + for (x = 0; x < pic->width; ++x) { + const uint32_t argb = row[x]; + const uint32_t r = (argb >> 16) & 0xff; + const uint32_t g = (argb >> 8) & 0xff; + const uint32_t b = (argb >> 0) & 0xff; + // We use BT.709 for converting to luminance. + const uint32_t Y = (uint32_t)(0.2126 * r + 0.7152 * g + 0.0722 * b + .5); + row[x] = (argb & 0xff000000u) | (Y * 0x010101u); + } + } +} + +static void Help(void) { + fprintf(stderr, + "Usage: get_disto [-ssim][-psnr][-alpha] compressed.webp orig.webp\n" + " -ssim ..... print SSIM distortion\n" + " -psnr ..... print PSNR distortion (default)\n" + " -alpha .... preserve alpha plane\n" + " -h ........ this message\n" + " -o . save the diff map as a WebP lossless file\n" + " -scale .... scale the difference map to fit [0..255] range\n" + " -gray ..... use grayscale for difference map (-scale)\n" + " Also handles PNG, JPG and TIFF files, in addition to WebP.\n"); +} + +int main(int argc, const char* argv[]) { + WebPPicture pic1, pic2; + size_t size1 = 0, size2 = 0; + int ret = 1; + float disto[5]; + int type = 0; + int c; + int help = 0; + int keep_alpha = 0; + int scale = 0; + int use_gray = 0; + const char* name1 = NULL; + const char* name2 = NULL; + const char* output = NULL; + + INIT_WARGV(argc, argv); + + if (!WebPPictureInit(&pic1) || !WebPPictureInit(&pic2)) { + fprintf(stderr, "Can't init pictures\n"); + FREE_WARGV_AND_RETURN(1); + } + + for (c = 1; c < argc; ++c) { + if (!strcmp(argv[c], "-ssim")) { + type = 1; + } else if (!strcmp(argv[c], "-psnr")) { + type = 0; + } else if (!strcmp(argv[c], "-alpha")) { + keep_alpha = 1; + } else if (!strcmp(argv[c], "-scale")) { + scale = 1; + } else if (!strcmp(argv[c], "-gray")) { + use_gray = 1; + } else if (!strcmp(argv[c], "-h")) { + help = 1; + ret = 0; + } else if (!strcmp(argv[c], "-o")) { + if (++c == argc) { + fprintf(stderr, "missing file name after %s option.\n", argv[c - 1]); + goto End; + } + output = (const char*)GET_WARGV(argv, c); + } else if (name1 == NULL) { + name1 = (const char*)GET_WARGV(argv, c); + } else { + name2 = (const char*)GET_WARGV(argv, c); + } + } + if (help || name1 == NULL || name2 == NULL) { + if (!help) { + fprintf(stderr, "Error: missing arguments.\n"); + } + Help(); + goto End; + } + size1 = ReadPicture(name1, &pic1, 1); + size2 = ReadPicture(name2, &pic2, 1); + if (size1 == 0 || size2 == 0) goto End; + + if (!keep_alpha) { + WebPBlendAlpha(&pic1, 0x00000000); + WebPBlendAlpha(&pic2, 0x00000000); + } + + if (!WebPPictureDistortion(&pic1, &pic2, type, disto)) { + fprintf(stderr, "Error while computing the distortion.\n"); + goto End; + } + printf("%u %.2f %.2f %.2f %.2f %.2f [ %.2f bpp ]\n", + (unsigned int)size1, + disto[4], disto[0], disto[1], disto[2], disto[3], + 8.f * size1 / pic1.width / pic1.height); + + if (output != NULL) { + uint8_t* data = NULL; + size_t data_size = 0; + if (pic1.use_argb != pic2.use_argb) { + fprintf(stderr, "Pictures are not in the same argb format. " + "Can't save the difference map.\n"); + goto End; + } + if (pic1.use_argb) { + int n; + fprintf(stderr, "max differences per channel: "); + for (n = 0; n < 3; ++n) { // skip the alpha channel + const int range = (type == 1) ? + SSIMScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4, + (const uint8_t*)pic2.argb + n, pic2.argb_stride * 4, + 4, pic1.width, pic1.height, scale) : + DiffScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4, + (const uint8_t*)pic2.argb + n, pic2.argb_stride * 4, + 4, pic1.width, pic1.height, scale); + if (range < 0) fprintf(stderr, "\nError computing diff map\n"); + fprintf(stderr, "[%d]", range); + } + fprintf(stderr, "\n"); + if (use_gray) ConvertToGray(&pic1); + } else { + fprintf(stderr, "Can only compute the difference map in ARGB format.\n"); + goto End; + } +#if !defined(WEBP_REDUCE_CSP) + data_size = WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb, + pic1.width, pic1.height, + pic1.argb_stride * 4, + &data); + if (data_size == 0) { + fprintf(stderr, "Error during lossless encoding.\n"); + goto End; + } + ret = ImgIoUtilWriteFile(output, data, data_size) ? 0 : 1; + WebPFree(data); + if (ret) goto End; +#else + (void)data; + (void)data_size; + fprintf(stderr, "Cannot save the difference map. Please recompile " + "without the WEBP_REDUCE_CSP flag.\n"); +#endif // WEBP_REDUCE_CSP + } + ret = 0; + + End: + WebPPictureFree(&pic1); + WebPPictureFree(&pic2); + FREE_WARGV_AND_RETURN(ret); +} diff --git a/internal/libwebp-1.2.1/extras/quality_estimate.c b/internal/libwebp-1.2.1/extras/quality_estimate.c new file mode 100644 index 0000000..17e98d9 --- /dev/null +++ b/internal/libwebp-1.2.1/extras/quality_estimate.c @@ -0,0 +1,129 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// VP8EstimateQuality(): rough encoding quality estimate +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "extras/extras.h" +#include "webp/decode.h" + +#include + +//------------------------------------------------------------------------------ + +#define INVALID_BIT_POS (1ull << 63) + +// In most cases, we don't need to use a full arithmetic decoder, since +// all the header's bits are written using a uniform probability of 128. +// We can just parse the header as if it was bits (works in 99.999% cases). +static WEBP_INLINE uint32_t GetBit(const uint8_t* const data, size_t nb, + uint64_t max_size, uint64_t* const bit_pos) { + uint32_t val = 0; + if (*bit_pos + nb <= 8 * max_size) { + while (nb-- > 0) { + const uint64_t p = (*bit_pos)++; + const int bit = !!(data[p >> 3] & (128 >> ((p & 7)))); + val = (val << 1) | bit; + } + } else { + *bit_pos = INVALID_BIT_POS; + } + return val; +} + +#define GET_BIT(n) GetBit(data, (n), size, &bit_pos) +#define CONDITIONAL_SKIP(n) (GET_BIT(1) ? GET_BIT((n)) : 0) + +int VP8EstimateQuality(const uint8_t* const data, size_t size) { + size_t pos = 0; + uint64_t bit_pos; + uint64_t sig = 0x00; + int ok = 0; + int Q = -1; + WebPBitstreamFeatures features; + + if (data == NULL) return -1; + + if (WebPGetFeatures(data, size, &features) != VP8_STATUS_OK) { + return -1; // invalid file + } + if (features.format == 2) return 101; // lossless + if (features.format == 0 || features.has_animation) return -1; // mixed + + while (pos < size) { + sig = (sig >> 8) | ((uint64_t)data[pos++] << 40); + if ((sig >> 24) == 0x2a019dull) { + ok = 1; + break; + } + } + if (!ok) return -1; + if (pos + 4 > size) return -1; + + // Skip main Header + // width = (data[pos + 0] | (data[pos + 1] << 8)) & 0x3fff; + // height = (data[pos + 2] | (data[pos + 3] << 8)) & 0x3fff; + pos += 4; + bit_pos = pos * 8; + + GET_BIT(2); // colorspace + clamp type + + // Segment header + if (GET_BIT(1)) { // use_segment_ + int s; + const int update_map = GET_BIT(1); + if (GET_BIT(1)) { // update data + const int absolute_delta = GET_BIT(1); + int q[4] = { 0, 0, 0, 0 }; + for (s = 0; s < 4; ++s) { + if (GET_BIT(1)) { + q[s] = GET_BIT(7); + if (GET_BIT(1)) q[s] = -q[s]; // sign + } + } + if (absolute_delta) Q = q[0]; // just use the first segment's quantizer + for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7); // filter strength + } + if (update_map) { + for (s = 0; s < 3; ++s) CONDITIONAL_SKIP(8); + } + } + // Filter header + GET_BIT(1 + 6 + 3); // simple + level + sharpness + if (GET_BIT(1)) { // use_lf_delta + if (GET_BIT(1)) { // update lf_delta? + int n; + for (n = 0; n < 4 + 4; ++n) CONDITIONAL_SKIP(6); + } + } + // num partitions + GET_BIT(2); + + // ParseQuant + { + const int base_q = GET_BIT(7); + /* dqy1_dc = */ CONDITIONAL_SKIP(5); + /* dqy2_dc = */ CONDITIONAL_SKIP(5); + /* dqy2_ac = */ CONDITIONAL_SKIP(5); + /* dquv_dc = */ CONDITIONAL_SKIP(5); + /* dquv_ac = */ CONDITIONAL_SKIP(5); + + if (Q < 0) Q = base_q; + } + if (bit_pos == INVALID_BIT_POS) return -1; + + // base mapping + Q = (127 - Q) * 100 / 127; + // correction for power-law behavior in low range + if (Q < 80) { + Q = (int)(pow(Q / 80., 1. / 0.38) * 80); + } + return Q; +} diff --git a/internal/libwebp-1.2.1/extras/vwebp_sdl.c b/internal/libwebp-1.2.1/extras/vwebp_sdl.c new file mode 100644 index 0000000..3cf4466 --- /dev/null +++ b/internal/libwebp-1.2.1/extras/vwebp_sdl.c @@ -0,0 +1,101 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Simple SDL-based WebP file viewer. +// Does not support animation, just static images. +// +// Press 'q' to exit. +// +// Author: James Zern (jzern@google.com) + +#include + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#if defined(WEBP_HAVE_SDL) + +#include "webp_to_sdl.h" +#include "webp/decode.h" +#include "imageio/imageio_util.h" +#include "../examples/unicode.h" + +#if defined(WEBP_HAVE_JUST_SDL_H) +#include +#else +#include +#endif + +static void ProcessEvents(void) { + int done = 0; + SDL_Event event; + while (!done && SDL_WaitEvent(&event)) { + switch (event.type) { + case SDL_KEYUP: + switch (event.key.keysym.sym) { + case SDLK_q: done = 1; break; + default: break; + } + break; + default: break; + } + } +} + +int main(int argc, char* argv[]) { + int c; + int ok = 0; + + INIT_WARGV(argc, argv); + + for (c = 1; c < argc; ++c) { + const char* file = NULL; + const uint8_t* webp = NULL; + size_t webp_size = 0; + if (!strcmp(argv[c], "-h")) { + printf("Usage: %s [-h] image.webp [more_files.webp...]\n", argv[0]); + FREE_WARGV_AND_RETURN(0); + } else { + file = (const char*)GET_WARGV(argv, c); + } + if (file == NULL) continue; + if (!ImgIoUtilReadFile(file, &webp, &webp_size)) { + WFPRINTF(stderr, "Error opening file: %s\n", (const W_CHAR*)file); + goto Error; + } + if (webp_size != (size_t)(int)webp_size) { + free((void*)webp); + fprintf(stderr, "File too large.\n"); + goto Error; + } + ok = WebpToSDL((const char*)webp, (int)webp_size); + free((void*)webp); + if (!ok) { + WFPRINTF(stderr, "Error decoding file %s\n", (const W_CHAR*)file); + goto Error; + } + ProcessEvents(); + } + ok = 1; + + Error: + SDL_Quit(); + FREE_WARGV_AND_RETURN(ok ? 0 : 1); +} + +#else // !WEBP_HAVE_SDL + +int main(int argc, const char* argv[]) { + fprintf(stderr, "SDL support not enabled in %s.\n", argv[0]); + (void)argc; + return 0; +} + +#endif diff --git a/internal/libwebp-1.2.1/extras/webp_quality.c b/internal/libwebp-1.2.1/extras/webp_quality.c new file mode 100644 index 0000000..0a3b25f --- /dev/null +++ b/internal/libwebp-1.2.1/extras/webp_quality.c @@ -0,0 +1,54 @@ +// Simple tool to roughly evaluate the quality encoding of a webp bitstream +// +// Result is a *rough* estimation of the quality. You should just consider +// the bucket it's in (q > 80? > 50? > 20?) and not take it for face value. +/* + gcc -o webp_quality webp_quality.c -O3 -I../ -L. -L../imageio \ + -limageio_util -lwebpextras -lwebp -lm -lpthread +*/ + +#include +#include +#include + +#include "extras/extras.h" +#include "imageio/imageio_util.h" +#include "../examples/unicode.h" + +int main(int argc, const char* argv[]) { + int c; + int quiet = 0; + int ok = 1; + + INIT_WARGV(argc, argv); + + for (c = 1; ok && c < argc; ++c) { + if (!strcmp(argv[c], "-quiet")) { + quiet = 1; + } else if (!strcmp(argv[c], "-help") || !strcmp(argv[c], "-h")) { + printf("webp_quality [-h][-quiet] webp_files...\n"); + FREE_WARGV_AND_RETURN(0); + } else { + const char* const filename = (const char*)GET_WARGV(argv, c); + const uint8_t* data = NULL; + size_t data_size = 0; + int q; + ok = ImgIoUtilReadFile(filename, &data, &data_size); + if (!ok) break; + q = VP8EstimateQuality(data, data_size); + if (!quiet) WPRINTF("[%s] ", (const W_CHAR*)filename); + if (q < 0) { + fprintf(stderr, "Not a WebP file, or not a lossy WebP file.\n"); + ok = 0; + } else { + if (!quiet) { + printf("Estimated quality factor: %d\n", q); + } else { + printf("%d\n", q); // just print the number + } + } + free((void*)data); + } + } + FREE_WARGV_AND_RETURN(ok ? 0 : 1); +} diff --git a/internal/libwebp-1.2.1/extras/webp_to_sdl.c b/internal/libwebp-1.2.1/extras/webp_to_sdl.c new file mode 100644 index 0000000..b13fc92 --- /dev/null +++ b/internal/libwebp-1.2.1/extras/webp_to_sdl.c @@ -0,0 +1,110 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Simple WebP-to-SDL wrapper. Useful for emscripten. +// +// Author: James Zern (jzern@google.com) + +#ifdef HAVE_CONFIG_H +#include "src/webp/config.h" +#endif + +#if defined(WEBP_HAVE_SDL) + +#include "webp_to_sdl.h" + +#include +#include "src/webp/decode.h" + +#if defined(WEBP_HAVE_JUST_SDL_H) +#include +#else +#include +#endif + +static int init_ok = 0; +int WebpToSDL(const char* data, unsigned int data_size) { + int ok = 0; + VP8StatusCode status; + WebPDecoderConfig config; + WebPBitstreamFeatures* const input = &config.input; + WebPDecBuffer* const output = &config.output; + SDL_Surface* screen = NULL; + SDL_Surface* surface = NULL; + + if (!WebPInitDecoderConfig(&config)) { + fprintf(stderr, "Library version mismatch!\n"); + return 0; + } + + if (!init_ok) { + SDL_Init(SDL_INIT_VIDEO); + init_ok = 1; + } + + status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &config.input); + if (status != VP8_STATUS_OK) goto Error; + + screen = SDL_SetVideoMode(input->width, input->height, 32, SDL_SWSURFACE); + if (screen == NULL) { + fprintf(stderr, "Unable to set video mode (32bpp %dx%d)!\n", + input->width, input->height); + goto Error; + } + + surface = SDL_CreateRGBSurface(SDL_SWSURFACE, + input->width, input->height, 32, + 0x000000ffu, // R mask + 0x0000ff00u, // G mask + 0x00ff0000u, // B mask + 0xff000000u); // A mask + + if (surface == NULL) { + fprintf(stderr, "Unable to create %dx%d RGBA surface!\n", + input->width, input->height); + goto Error; + } + if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface); + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + output->colorspace = MODE_BGRA; +#else + output->colorspace = MODE_RGBA; +#endif + output->width = surface->w; + output->height = surface->h; + output->u.RGBA.rgba = surface->pixels; + output->u.RGBA.stride = surface->pitch; + output->u.RGBA.size = surface->pitch * surface->h; + output->is_external_memory = 1; + + status = WebPDecode((const uint8_t*)data, (size_t)data_size, &config); + if (status != VP8_STATUS_OK) { + fprintf(stderr, "Error decoding image (%d)\n", status); + goto Error; + } + + if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface); + if (SDL_BlitSurface(surface, NULL, screen, NULL) || + SDL_Flip(screen)) { + goto Error; + } + + ok = 1; + + Error: + SDL_FreeSurface(surface); + SDL_FreeSurface(screen); + WebPFreeDecBuffer(output); + return ok; +} + +//------------------------------------------------------------------------------ + +#endif // WEBP_HAVE_SDL diff --git a/internal/libwebp-1.2.1/extras/webp_to_sdl.h b/internal/libwebp-1.2.1/extras/webp_to_sdl.h new file mode 100644 index 0000000..1b5ea98 --- /dev/null +++ b/internal/libwebp-1.2.1/extras/webp_to_sdl.h @@ -0,0 +1,22 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Simple WebP-to-SDL wrapper. Useful for emscripten. +// +// Author: James Zern (jzern@google.com) + +#ifndef WEBP_EXTRAS_WEBP_TO_SDL_H_ +#define WEBP_EXTRAS_WEBP_TO_SDL_H_ + +// Exports the method WebpToSDL(const char* data, int data_size) which decodes +// a WebP bitstream into an RGBA SDL surface. +// Return false on failure. +extern int WebpToSDL(const char* data, unsigned int data_size); + +#endif // WEBP_EXTRAS_WEBP_TO_SDL_H_ diff --git a/internal/libwebp-1.2.1/gradle.properties b/internal/libwebp-1.2.1/gradle.properties new file mode 100644 index 0000000..28d39f5 --- /dev/null +++ b/internal/libwebp-1.2.1/gradle.properties @@ -0,0 +1,14 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Versions for gradle +BUILD_TOOLS_VERSION=23.0.3 +COMPILE_SDK_VERSION=23 +ANDROID_GRADLE_PLUGIN_VERSION=1.5.0 +GRADLE_DOWNLOAD_TASK_VERSION=2.1.0 diff --git a/internal/libwebp-1.2.1/gradle/wrapper/gradle-wrapper.jar b/internal/libwebp-1.2.1/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 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 literal 0 HcmV?d00001 diff --git a/internal/libwebp-1.2.1/gradle/wrapper/gradle-wrapper.properties b/internal/libwebp-1.2.1/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1b16c34 --- /dev/null +++ b/internal/libwebp-1.2.1/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/internal/libwebp-1.2.1/gradlew b/internal/libwebp-1.2.1/gradlew new file mode 100755 index 0000000..2fe81a7 --- /dev/null +++ b/internal/libwebp-1.2.1/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/internal/libwebp-1.2.1/gradlew.bat b/internal/libwebp-1.2.1/gradlew.bat new file mode 100644 index 0000000..9618d8d --- /dev/null +++ b/internal/libwebp-1.2.1/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/internal/libwebp-1.2.1/imageio/Android.mk b/internal/libwebp-1.2.1/imageio/Android.mk new file mode 100644 index 0000000..e779f71 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/Android.mk @@ -0,0 +1,54 @@ +LOCAL_PATH := $(call my-dir) + +################################################################################ +# libimageio_util + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + imageio_util.c \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src + +LOCAL_MODULE := imageio_util + +include $(BUILD_STATIC_LIBRARY) + +################################################################################ +# libimagedec + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + image_dec.c \ + jpegdec.c \ + metadata.c \ + pngdec.c \ + pnmdec.c \ + tiffdec.c \ + webpdec.c \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src +LOCAL_STATIC_LIBRARIES := imageio_util + +LOCAL_MODULE := imagedec + +include $(BUILD_STATIC_LIBRARY) + +################################################################################ +# libimageenc + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + image_enc.c \ + +LOCAL_CFLAGS := $(WEBP_CFLAGS) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src +LOCAL_STATIC_LIBRARIES := imageio_util + +LOCAL_MODULE := imageenc + +include $(BUILD_STATIC_LIBRARY) diff --git a/internal/libwebp-1.2.1/imageio/Makefile.am b/internal/libwebp-1.2.1/imageio/Makefile.am new file mode 100644 index 0000000..500ec7e --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/Makefile.am @@ -0,0 +1,32 @@ +AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src +noinst_LTLIBRARIES = +noinst_LTLIBRARIES += libimageio_util.la +if BUILD_DEMUX + noinst_LTLIBRARIES += libimagedec.la +endif +noinst_LTLIBRARIES += libimageenc.la + +noinst_HEADERS = +noinst_HEADERS += ../src/webp/decode.h +noinst_HEADERS += ../src/webp/types.h + +libimageio_util_la_SOURCES = +libimageio_util_la_SOURCES += imageio_util.c imageio_util.h + +libimagedec_la_SOURCES = +libimagedec_la_SOURCES += image_dec.c image_dec.h +libimagedec_la_SOURCES += jpegdec.c jpegdec.h +libimagedec_la_SOURCES += metadata.c metadata.h +libimagedec_la_SOURCES += pngdec.c pngdec.h +libimagedec_la_SOURCES += pnmdec.c pnmdec.h +libimagedec_la_SOURCES += tiffdec.c tiffdec.h +libimagedec_la_SOURCES += webpdec.c webpdec.h +libimagedec_la_SOURCES += wicdec.c wicdec.h +libimagedec_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES) +libimagedec_la_CPPFLAGS += $(AM_CPPFLAGS) +libimagedec_la_LIBADD = ../src/demux/libwebpdemux.la + +libimageenc_la_SOURCES = +libimageenc_la_SOURCES += image_enc.c image_enc.h +libimageenc_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES) +libimageenc_la_CPPFLAGS += $(AM_CPPFLAGS) diff --git a/internal/libwebp-1.2.1/imageio/image_dec.c b/internal/libwebp-1.2.1/imageio/image_dec.c new file mode 100644 index 0000000..08a1b18 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/image_dec.c @@ -0,0 +1,66 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Generic image-type guessing. + +#include "./image_dec.h" + +static WEBP_INLINE uint32_t GetBE32(const uint8_t buf[]) { + return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} + +WebPInputFileFormat WebPGuessImageType(const uint8_t* const data, + size_t data_size) { + WebPInputFileFormat format = WEBP_UNSUPPORTED_FORMAT; + if (data != NULL && data_size >= 12) { + const uint32_t magic1 = GetBE32(data + 0); + const uint32_t magic2 = GetBE32(data + 8); + if (magic1 == 0x89504E47U) { + format = WEBP_PNG_FORMAT; + } else if (magic1 >= 0xFFD8FF00U && magic1 <= 0xFFD8FFFFU) { + format = WEBP_JPEG_FORMAT; + } else if (magic1 == 0x49492A00 || magic1 == 0x4D4D002A) { + format = WEBP_TIFF_FORMAT; + } else if (magic1 == 0x52494646 && magic2 == 0x57454250) { + format = WEBP_WEBP_FORMAT; + } else if (((magic1 >> 24) & 0xff) == 'P') { + const int type = (magic1 >> 16) & 0xff; + // we only support 'P5 -> P7' for now. + if (type >= '5' && type <= '7') format = WEBP_PNM_FORMAT; + } + } + return format; +} + +static int FailReader(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, + int keep_alpha, struct Metadata* const metadata) { + (void)data; + (void)data_size; + (void)pic; + (void)keep_alpha; + (void)metadata; + return 0; +} + +WebPImageReader WebPGetImageReader(WebPInputFileFormat format) { + switch (format) { + case WEBP_PNG_FORMAT: return ReadPNG; + case WEBP_JPEG_FORMAT: return ReadJPEG; + case WEBP_TIFF_FORMAT: return ReadTIFF; + case WEBP_WEBP_FORMAT: return ReadWebP; + case WEBP_PNM_FORMAT: return ReadPNM; + default: return FailReader; + } +} + +WebPImageReader WebPGuessImageReader(const uint8_t* const data, + size_t data_size) { + return WebPGetImageReader(WebPGuessImageType(data, data_size)); +} diff --git a/internal/libwebp-1.2.1/imageio/image_dec.h b/internal/libwebp-1.2.1/imageio/image_dec.h new file mode 100644 index 0000000..df411e1 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/image_dec.h @@ -0,0 +1,67 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// All-in-one library to decode PNG/JPEG/WebP/TIFF/WIC input images. +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_IMAGEIO_IMAGE_DEC_H_ +#define WEBP_IMAGEIO_IMAGE_DEC_H_ + +#include "webp/types.h" + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include "./metadata.h" +#include "./jpegdec.h" +#include "./pngdec.h" +#include "./pnmdec.h" +#include "./tiffdec.h" +#include "./webpdec.h" +#include "./wicdec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + WEBP_PNG_FORMAT = 0, + WEBP_JPEG_FORMAT, + WEBP_TIFF_FORMAT, + WEBP_WEBP_FORMAT, + WEBP_PNM_FORMAT, + WEBP_UNSUPPORTED_FORMAT +} WebPInputFileFormat; + +// Try to infer the image format. 'data_size' should be larger than 12. +// Returns WEBP_UNSUPPORTED_FORMAT if format can't be guess safely. +WebPInputFileFormat WebPGuessImageType(const uint8_t* const data, + size_t data_size); + +// Signature for common image-reading functions (ReadPNG, ReadJPEG, ...) +typedef int (*WebPImageReader)(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, + int keep_alpha, struct Metadata* const metadata); + +// Return the reader associated to a given file format. +WebPImageReader WebPGetImageReader(WebPInputFileFormat format); + +// This function is similar to WebPGuessImageType(), but returns a +// suitable reader function. The returned reader is never NULL, but +// unknown formats will return an always-failing valid reader. +WebPImageReader WebPGuessImageReader(const uint8_t* const data, + size_t data_size); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_IMAGE_DEC_H_ diff --git a/internal/libwebp-1.2.1/imageio/image_enc.c b/internal/libwebp-1.2.1/imageio/image_enc.c new file mode 100644 index 0000000..5a78cbd --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/image_enc.c @@ -0,0 +1,604 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Save image + +#include "./image_enc.h" + +#include +#include + +#ifdef WEBP_HAVE_PNG +#include +#include // note: this must be included *after* png.h +#endif + +#ifdef HAVE_WINCODEC_H +#ifdef __MINGW32__ +#define INITGUID // Without this GUIDs are declared extern and fail to link +#endif +#define CINTERFACE +#define COBJMACROS +#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++ + // code with COBJMACROS. +#include // CreateStreamOnHGlobal() +#include +#include +#include +#include +#endif + +#include "./imageio_util.h" +#include "../examples/unicode.h" + +//------------------------------------------------------------------------------ +// PNG + +#ifdef HAVE_WINCODEC_H + +#define IFS(fn) \ + do { \ + if (SUCCEEDED(hr)) { \ + hr = (fn); \ + if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \ + } \ + } while (0) + +#ifdef __cplusplus +#define MAKE_REFGUID(x) (x) +#else +#define MAKE_REFGUID(x) &(x) +#endif + +static HRESULT CreateOutputStream(const char* out_file_name, + int write_to_mem, IStream** stream) { + HRESULT hr = S_OK; + if (write_to_mem) { + // Output to a memory buffer. This is freed when 'stream' is released. + IFS(CreateStreamOnHGlobal(NULL, TRUE, stream)); + } else { + IFS(SHCreateStreamOnFile((const LPTSTR)out_file_name, + STGM_WRITE | STGM_CREATE, stream)); + } + if (FAILED(hr)) { + _ftprintf(stderr, _T("Error opening output file %s (%08lx)\n"), + (const LPTSTR)out_file_name, hr); + } + return hr; +} + +static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout, + REFGUID container_guid, + uint8_t* rgb, int stride, + uint32_t width, uint32_t height, int has_alpha) { + HRESULT hr = S_OK; + IWICImagingFactory* factory = NULL; + IWICBitmapFrameEncode* frame = NULL; + IWICBitmapEncoder* encoder = NULL; + IStream* stream = NULL; + WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA + : GUID_WICPixelFormat24bppBGR; + + if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG; + + IFS(CoInitialize(NULL)); + IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL, + CLSCTX_INPROC_SERVER, + MAKE_REFGUID(IID_IWICImagingFactory), + (LPVOID*)&factory)); + if (hr == REGDB_E_CLASSNOTREG) { + fprintf(stderr, + "Couldn't access Windows Imaging Component (are you running " + "Windows XP SP3 or newer?). PNG support not available. " + "Use -ppm or -pgm for available PPM and PGM formats.\n"); + } + IFS(CreateOutputStream(out_file_name, use_stdout, &stream)); + IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL, + &encoder)); + IFS(IWICBitmapEncoder_Initialize(encoder, stream, + WICBitmapEncoderNoCache)); + IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL)); + IFS(IWICBitmapFrameEncode_Initialize(frame, NULL)); + IFS(IWICBitmapFrameEncode_SetSize(frame, width, height)); + IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format)); + IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride, + height * stride, rgb)); + IFS(IWICBitmapFrameEncode_Commit(frame)); + IFS(IWICBitmapEncoder_Commit(encoder)); + + if (SUCCEEDED(hr) && use_stdout) { + HGLOBAL image; + IFS(GetHGlobalFromStream(stream, &image)); + if (SUCCEEDED(hr)) { + HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD mode; + const BOOL update_mode = GetConsoleMode(std_output, &mode); + const void* const image_mem = GlobalLock(image); + DWORD bytes_written = 0; + + // Clear output processing if necessary, then output the image. + if (update_mode) SetConsoleMode(std_output, 0); + if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image), + &bytes_written, NULL) || + bytes_written != GlobalSize(image)) { + hr = E_FAIL; + } + if (update_mode) SetConsoleMode(std_output, mode); + GlobalUnlock(image); + } + } + + if (frame != NULL) IUnknown_Release(frame); + if (encoder != NULL) IUnknown_Release(encoder); + if (factory != NULL) IUnknown_Release(factory); + if (stream != NULL) IUnknown_Release(stream); + return hr; +} + +int WebPWritePNG(const char* out_file_name, int use_stdout, + const WebPDecBuffer* const buffer) { + const uint32_t width = buffer->width; + const uint32_t height = buffer->height; + uint8_t* const rgb = buffer->u.RGBA.rgba; + const int stride = buffer->u.RGBA.stride; + const int has_alpha = WebPIsAlphaMode(buffer->colorspace); + + return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout, + MAKE_REFGUID(GUID_ContainerFormatPng), + rgb, stride, width, height, has_alpha)); +} + +#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H +static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp unused) { + (void)unused; // remove variable-unused warning + longjmp(png_jmpbuf(png), 1); +} + +int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { + volatile png_structp png; + volatile png_infop info; + + if (out_file == NULL || buffer == NULL) return 0; + + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, PNGErrorFunction, NULL); + if (png == NULL) { + return 0; + } + info = png_create_info_struct(png); + if (info == NULL) { + png_destroy_write_struct((png_structpp)&png, NULL); + return 0; + } + if (setjmp(png_jmpbuf(png))) { + png_destroy_write_struct((png_structpp)&png, (png_infopp)&info); + return 0; + } + png_init_io(png, out_file); + { + const uint32_t width = buffer->width; + const uint32_t height = buffer->height; + png_bytep row = buffer->u.RGBA.rgba; + const int stride = buffer->u.RGBA.stride; + const int has_alpha = WebPIsAlphaMode(buffer->colorspace); + uint32_t y; + + png_set_IHDR(png, info, width, height, 8, + has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, info); + for (y = 0; y < height; ++y) { + png_write_rows(png, &row, 1); + row += stride; + } + } + png_write_end(png, info); + png_destroy_write_struct((png_structpp)&png, (png_infopp)&info); + return 1; +} +#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG +int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) { + if (fout == NULL || buffer == NULL) return 0; + + fprintf(stderr, "PNG support not compiled. Please install the libpng " + "development package before building.\n"); + fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n"); + return 0; +} +#endif + +//------------------------------------------------------------------------------ +// PPM / PAM + +static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer, + int alpha) { + if (fout == NULL || buffer == NULL) { + return 0; + } else { + const uint32_t width = buffer->width; + const uint32_t height = buffer->height; + const uint8_t* row = buffer->u.RGBA.rgba; + const int stride = buffer->u.RGBA.stride; + const size_t bytes_per_px = alpha ? 4 : 3; + uint32_t y; + + if (row == NULL) return 0; + + if (alpha) { + fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n" + "TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height); + } else { + fprintf(fout, "P6\n%u %u\n255\n", width, height); + } + for (y = 0; y < height; ++y) { + if (fwrite(row, width, bytes_per_px, fout) != bytes_per_px) { + return 0; + } + row += stride; + } + } + return 1; +} + +int WebPWritePPM(FILE* fout, const WebPDecBuffer* const buffer) { + return WritePPMPAM(fout, buffer, 0); +} + +int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) { + return WritePPMPAM(fout, buffer, 1); +} + +//------------------------------------------------------------------------------ +// Raw PGM + +// Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose. +int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) { + const uint32_t width = buffer->width; + const uint32_t height = buffer->height; + const uint8_t* rgba = buffer->u.RGBA.rgba; + const int stride = buffer->u.RGBA.stride; + const uint32_t bytes_per_px = 2; + uint32_t y; + + if (fout == NULL || buffer == NULL || rgba == NULL) return 0; + + fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height); + for (y = 0; y < height; ++y) { + if (fwrite(rgba, width, bytes_per_px, fout) != bytes_per_px) { + return 0; + } + rgba += stride; + } + return 1; +} + +//------------------------------------------------------------------------------ +// BMP + +static void PutLE16(uint8_t* const dst, uint32_t value) { + dst[0] = (value >> 0) & 0xff; + dst[1] = (value >> 8) & 0xff; +} + +static void PutLE32(uint8_t* const dst, uint32_t value) { + PutLE16(dst + 0, (value >> 0) & 0xffff); + PutLE16(dst + 2, (value >> 16) & 0xffff); +} + +#define BMP_HEADER_SIZE 54 +int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { + const int has_alpha = WebPIsAlphaMode(buffer->colorspace); + const uint32_t width = buffer->width; + const uint32_t height = buffer->height; + const uint8_t* rgba = buffer->u.RGBA.rgba; + const int stride = buffer->u.RGBA.stride; + const uint32_t bytes_per_px = has_alpha ? 4 : 3; + uint32_t y; + const uint32_t line_size = bytes_per_px * width; + const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4 + const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE; + uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 }; + + if (fout == NULL || buffer == NULL || rgba == NULL) return 0; + + // bitmap file header + PutLE16(bmp_header + 0, 0x4d42); // signature 'BM' + PutLE32(bmp_header + 2, total_size); // size including header + PutLE32(bmp_header + 6, 0); // reserved + PutLE32(bmp_header + 10, BMP_HEADER_SIZE); // offset to pixel array + // bitmap info header + PutLE32(bmp_header + 14, 40); // DIB header size + PutLE32(bmp_header + 18, width); // dimensions + PutLE32(bmp_header + 22, -(int)height); // vertical flip! + PutLE16(bmp_header + 26, 1); // number of planes + PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel + PutLE32(bmp_header + 30, 0); // no compression (BI_RGB) + PutLE32(bmp_header + 34, 0); // image size (placeholder) + PutLE32(bmp_header + 38, 2400); // x pixels/meter + PutLE32(bmp_header + 42, 2400); // y pixels/meter + PutLE32(bmp_header + 46, 0); // number of palette colors + PutLE32(bmp_header + 50, 0); // important color count + + // TODO(skal): color profile + + // write header + if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) { + return 0; + } + + // write pixel array + for (y = 0; y < height; ++y) { + if (fwrite(rgba, line_size, 1, fout) != 1) { + return 0; + } + // write padding zeroes + if (bmp_stride != line_size) { + const uint8_t zeroes[3] = { 0 }; + if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) { + return 0; + } + } + rgba += stride; + } + return 1; +} +#undef BMP_HEADER_SIZE + +//------------------------------------------------------------------------------ +// TIFF + +#define NUM_IFD_ENTRIES 15 +#define EXTRA_DATA_SIZE 16 +// 10b for signature/header + n * 12b entries + 4b for IFD terminator: +#define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4) +#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE) + +int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) { + const int has_alpha = WebPIsAlphaMode(buffer->colorspace); + const uint32_t width = buffer->width; + const uint32_t height = buffer->height; + const uint8_t* rgba = buffer->u.RGBA.rgba; + const int stride = buffer->u.RGBA.stride; + const uint8_t bytes_per_px = has_alpha ? 4 : 3; + const uint8_t assoc_alpha = + WebPIsPremultipliedMode(buffer->colorspace) ? 1 : 2; + // For non-alpha case, we omit tag 0x152 (ExtraSamples). + const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES + : NUM_IFD_ENTRIES - 1; + uint8_t tiff_header[TIFF_HEADER_SIZE] = { + 0x49, 0x49, 0x2a, 0x00, // little endian signature + 8, 0, 0, 0, // offset to the unique IFD that follows + // IFD (offset = 8). Entries must be written in increasing tag order. + num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each). + 0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD) + 0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD) + 0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888 + EXTRA_DATA_OFFSET + 0, 0, 0, 0, + 0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none + 0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB + 0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset: + TIFF_HEADER_SIZE, 0, 0, 0, // data follows header + 0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft + 0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels + bytes_per_px, 0, 0, 0, + 0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD) + 0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD) + 0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution + EXTRA_DATA_OFFSET + 8, 0, 0, 0, + 0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution + EXTRA_DATA_OFFSET + 8, 0, 0, 0, + 0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration + 0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch) + 0x52, 0x01, 3, 0, 1, 0, 0, 0, + assoc_alpha, 0, 0, 0, // 178: ExtraSamples: rgbA/RGBA + 0, 0, 0, 0, // 190: IFD terminator + // EXTRA_DATA_OFFSET: + 8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample + 72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution + }; + uint32_t y; + + if (fout == NULL || buffer == NULL || rgba == NULL) return 0; + + // Fill placeholders in IFD: + PutLE32(tiff_header + 10 + 8, width); + PutLE32(tiff_header + 22 + 8, height); + PutLE32(tiff_header + 106 + 8, height); + PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height); + if (!has_alpha) PutLE32(tiff_header + 178, 0); // IFD terminator + + // write header + if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) { + return 0; + } + // write pixel values + for (y = 0; y < height; ++y) { + if (fwrite(rgba, bytes_per_px, width, fout) != width) { + return 0; + } + rgba += stride; + } + + return 1; +} + +#undef TIFF_HEADER_SIZE +#undef EXTRA_DATA_OFFSET +#undef EXTRA_DATA_SIZE +#undef NUM_IFD_ENTRIES + +//------------------------------------------------------------------------------ +// Raw Alpha + +int WebPWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) { + if (fout == NULL || buffer == NULL) { + return 0; + } else { + const uint32_t width = buffer->width; + const uint32_t height = buffer->height; + const uint8_t* a = buffer->u.YUVA.a; + const int a_stride = buffer->u.YUVA.a_stride; + uint32_t y; + + if (a == NULL) return 0; + + fprintf(fout, "P5\n%u %u\n255\n", width, height); + for (y = 0; y < height; ++y) { + if (fwrite(a, width, 1, fout) != 1) return 0; + a += a_stride; + } + return 1; + } +} + +//------------------------------------------------------------------------------ +// PGM with IMC4 layout + +int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) { + if (fout == NULL || buffer == NULL) { + return 0; + } else { + const int width = buffer->width; + const int height = buffer->height; + const WebPYUVABuffer* const yuv = &buffer->u.YUVA; + const uint8_t* src_y = yuv->y; + const uint8_t* src_u = yuv->u; + const uint8_t* src_v = yuv->v; + const uint8_t* src_a = yuv->a; + const int uv_width = (width + 1) / 2; + const int uv_height = (height + 1) / 2; + const int a_height = (src_a != NULL) ? height : 0; + int ok = 1; + int y; + + if (src_y == NULL || src_u == NULL || src_v == NULL) return 0; + + fprintf(fout, "P5\n%d %d\n255\n", + (width + 1) & ~1, height + uv_height + a_height); + for (y = 0; ok && y < height; ++y) { + ok &= (fwrite(src_y, width, 1, fout) == 1); + if (width & 1) fputc(0, fout); // padding byte + src_y += yuv->y_stride; + } + for (y = 0; ok && y < uv_height; ++y) { + ok &= (fwrite(src_u, uv_width, 1, fout) == 1); + ok &= (fwrite(src_v, uv_width, 1, fout) == 1); + src_u += yuv->u_stride; + src_v += yuv->v_stride; + } + for (y = 0; ok && y < a_height; ++y) { + ok &= (fwrite(src_a, width, 1, fout) == 1); + if (width & 1) fputc(0, fout); // padding byte + src_a += yuv->a_stride; + } + return ok; + } +} + +//------------------------------------------------------------------------------ +// Raw YUV(A) planes + +int WebPWriteYUV(FILE* fout, const WebPDecBuffer* const buffer) { + if (fout == NULL || buffer == NULL) { + return 0; + } else { + const int width = buffer->width; + const int height = buffer->height; + const WebPYUVABuffer* const yuv = &buffer->u.YUVA; + const uint8_t* src_y = yuv->y; + const uint8_t* src_u = yuv->u; + const uint8_t* src_v = yuv->v; + const uint8_t* src_a = yuv->a; + const int uv_width = (width + 1) / 2; + const int uv_height = (height + 1) / 2; + const int a_height = (src_a != NULL) ? height : 0; + int ok = 1; + int y; + + if (src_y == NULL || src_u == NULL || src_v == NULL) return 0; + + for (y = 0; ok && y < height; ++y) { + ok &= (fwrite(src_y, width, 1, fout) == 1); + src_y += yuv->y_stride; + } + for (y = 0; ok && y < uv_height; ++y) { + ok &= (fwrite(src_u, uv_width, 1, fout) == 1); + src_u += yuv->u_stride; + } + for (y = 0; ok && y < uv_height; ++y) { + ok &= (fwrite(src_v, uv_width, 1, fout) == 1); + src_v += yuv->v_stride; + } + for (y = 0; ok && y < a_height; ++y) { + ok &= (fwrite(src_a, width, 1, fout) == 1); + src_a += yuv->a_stride; + } + return ok; + } +} + +//------------------------------------------------------------------------------ +// Generic top-level call + +int WebPSaveImage(const WebPDecBuffer* const buffer, + WebPOutputFileFormat format, + const char* const out_file_name) { + FILE* fout = NULL; + int needs_open_file = 1; + const int use_stdout = + (out_file_name != NULL) && !WSTRCMP(out_file_name, "-"); + int ok = 1; + + if (buffer == NULL || out_file_name == NULL) return 0; + +#ifdef HAVE_WINCODEC_H + needs_open_file = (format != PNG); +#endif + + if (needs_open_file) { + fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout) + : WFOPEN(out_file_name, "wb"); + if (fout == NULL) { + WFPRINTF(stderr, "Error opening output file %s\n", + (const W_CHAR*)out_file_name); + return 0; + } + } + + if (format == PNG || + format == RGBA || format == BGRA || format == ARGB || + format == rgbA || format == bgrA || format == Argb) { +#ifdef HAVE_WINCODEC_H + ok &= WebPWritePNG(out_file_name, use_stdout, buffer); +#else + ok &= WebPWritePNG(fout, buffer); +#endif + } else if (format == PAM) { + ok &= WebPWritePAM(fout, buffer); + } else if (format == PPM || format == RGB || format == BGR) { + ok &= WebPWritePPM(fout, buffer); + } else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) { + ok &= WebPWrite16bAsPGM(fout, buffer); + } else if (format == BMP) { + ok &= WebPWriteBMP(fout, buffer); + } else if (format == TIFF) { + ok &= WebPWriteTIFF(fout, buffer); + } else if (format == RAW_YUV) { + ok &= WebPWriteYUV(fout, buffer); + } else if (format == PGM || format == YUV || format == YUVA) { + ok &= WebPWritePGM(fout, buffer); + } else if (format == ALPHA_PLANE_ONLY) { + ok &= WebPWriteAlphaPlane(fout, buffer); + } + if (fout != NULL && fout != stdout) { + fclose(fout); + } + return ok; +} diff --git a/internal/libwebp-1.2.1/imageio/image_enc.h b/internal/libwebp-1.2.1/imageio/image_enc.h new file mode 100644 index 0000000..f8abdac --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/image_enc.h @@ -0,0 +1,96 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// All-in-one library to save PNG/JPEG/WebP/TIFF/WIC images. +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_IMAGEIO_IMAGE_ENC_H_ +#define WEBP_IMAGEIO_IMAGE_ENC_H_ + +#include + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include "webp/types.h" +#include "webp/decode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Output types +typedef enum { + PNG = 0, + PAM, + PPM, + PGM, + BMP, + TIFF, + RAW_YUV, + ALPHA_PLANE_ONLY, // this is for experimenting only + // forced colorspace output (for testing, mostly) + RGB, RGBA, BGR, BGRA, ARGB, + RGBA_4444, RGB_565, + rgbA, bgrA, Argb, rgbA_4444, + YUV, YUVA +} WebPOutputFileFormat; + +// General all-purpose call. +// Most formats expect a 'buffer' containing RGBA-like samples, except +// RAW_YUV, YUV and YUVA formats. +// If 'out_file_name' is "-", data is saved to stdout. +// Returns false if an error occurred, true otherwise. +int WebPSaveImage(const WebPDecBuffer* const buffer, + WebPOutputFileFormat format, const char* const out_file_name); + +// Save to PNG. +#ifdef HAVE_WINCODEC_H +int WebPWritePNG(const char* out_file_name, int use_stdout, + const struct WebPDecBuffer* const buffer); +#else +int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer); +#endif + +// Save to PPM format (RGB, no alpha) +int WebPWritePPM(FILE* fout, const struct WebPDecBuffer* const buffer); + +// Save to PAM format (= PPM + alpha) +int WebPWritePAM(FILE* fout, const struct WebPDecBuffer* const buffer); + +// Save 16b mode (RGBA4444, RGB565, ...) for debugging purposes. +int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer); + +// Save as BMP +int WebPWriteBMP(FILE* fout, const struct WebPDecBuffer* const buffer); + +// Save as TIFF +int WebPWriteTIFF(FILE* fout, const struct WebPDecBuffer* const buffer); + +// Save the ALPHA plane (only) as a PGM +int WebPWriteAlphaPlane(FILE* fout, const struct WebPDecBuffer* const buffer); + +// Save as YUV samples as PGM format (using IMC4 layout). +// See: http://www.fourcc.org/yuv.php#IMC4. +// (very convenient format for viewing the samples, esp. for odd dimensions). +int WebPWritePGM(FILE* fout, const struct WebPDecBuffer* const buffer); + +// Save YUV(A) planes sequentially (raw dump) +int WebPWriteYUV(FILE* fout, const struct WebPDecBuffer* const buffer); + +// Save 16b mode (RGBA4444, RGB565, ...) as PGM format, for debugging purposes. +int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_IMAGE_ENC_H_ diff --git a/internal/libwebp-1.2.1/imageio/imageio_util.c b/internal/libwebp-1.2.1/imageio/imageio_util.c new file mode 100644 index 0000000..df37137 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/imageio_util.c @@ -0,0 +1,162 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Utility functions used by the image decoders. +// + +#include "./imageio_util.h" + +#if defined(_WIN32) +#include // for _O_BINARY +#include // for _setmode() +#endif +#include +#include +#include "../examples/unicode.h" + +// ----------------------------------------------------------------------------- +// File I/O + +FILE* ImgIoUtilSetBinaryMode(FILE* file) { +#if defined(_WIN32) + if (_setmode(_fileno(file), _O_BINARY) == -1) { + fprintf(stderr, "Failed to reopen file in O_BINARY mode.\n"); + return NULL; + } +#endif + return file; +} + +int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) { + static const size_t kBlockSize = 16384; // default initial size + size_t max_size = 0; + size_t size = 0; + uint8_t* input = NULL; + + if (data == NULL || data_size == NULL) return 0; + *data = NULL; + *data_size = 0; + + if (!ImgIoUtilSetBinaryMode(stdin)) return 0; + + while (!feof(stdin)) { + // We double the buffer size each time and read as much as possible. + const size_t extra_size = (max_size == 0) ? kBlockSize : max_size; + // we allocate one extra byte for the \0 terminator + void* const new_data = realloc(input, max_size + extra_size + 1); + if (new_data == NULL) goto Error; + input = (uint8_t*)new_data; + max_size += extra_size; + size += fread(input + size, 1, extra_size, stdin); + if (size < max_size) break; + } + if (ferror(stdin)) goto Error; + if (input != NULL) input[size] = '\0'; // convenient 0-terminator + *data = input; + *data_size = size; + return 1; + + Error: + free(input); + fprintf(stderr, "Could not read from stdin\n"); + return 0; +} + +int ImgIoUtilReadFile(const char* const file_name, + const uint8_t** data, size_t* data_size) { + int ok; + uint8_t* file_data; + size_t file_size; + FILE* in; + const int from_stdin = (file_name == NULL) || !WSTRCMP(file_name, "-"); + + if (from_stdin) return ImgIoUtilReadFromStdin(data, data_size); + + if (data == NULL || data_size == NULL) return 0; + *data = NULL; + *data_size = 0; + + in = WFOPEN(file_name, "rb"); + if (in == NULL) { + WFPRINTF(stderr, "cannot open input file '%s'\n", (const W_CHAR*)file_name); + return 0; + } + fseek(in, 0, SEEK_END); + file_size = ftell(in); + fseek(in, 0, SEEK_SET); + // we allocate one extra byte for the \0 terminator + file_data = (uint8_t*)WebPMalloc(file_size + 1); + if (file_data == NULL) { + fclose(in); + WFPRINTF(stderr, "memory allocation failure when reading file %s\n", + (const W_CHAR*)file_name); + return 0; + } + ok = (fread(file_data, file_size, 1, in) == 1); + fclose(in); + + if (!ok) { + WFPRINTF(stderr, "Could not read %d bytes of data from file %s\n", + (int)file_size, (const W_CHAR*)file_name); + WebPFree(file_data); + return 0; + } + file_data[file_size] = '\0'; // convenient 0-terminator + *data = file_data; + *data_size = file_size; + return 1; +} + +// ----------------------------------------------------------------------------- + +int ImgIoUtilWriteFile(const char* const file_name, + const uint8_t* data, size_t data_size) { + int ok; + FILE* out; + const int to_stdout = (file_name == NULL) || !WSTRCMP(file_name, "-"); + + if (data == NULL) { + return 0; + } + out = to_stdout ? ImgIoUtilSetBinaryMode(stdout) : WFOPEN(file_name, "wb"); + if (out == NULL) { + WFPRINTF(stderr, "Error! Cannot open output file '%s'\n", + (const W_CHAR*)file_name); + return 0; + } + ok = (fwrite(data, data_size, 1, out) == 1); + if (out != stdout) fclose(out); + return ok; +} + +// ----------------------------------------------------------------------------- + +void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride, + uint8_t* dst, int dst_stride, int width, int height) { + while (height-- > 0) { + memcpy(dst, src, width * sizeof(*dst)); + src += src_stride; + dst += dst_stride; + } +} + +// ----------------------------------------------------------------------------- + +int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t stride, size_t height) { + const uint64_t total_size = stride * height; + int ok = (total_size == (size_t)total_size); + // check that 'stride' is representable as int: + ok = ok && ((uint64_t)(int)stride == stride); +#if defined(WEBP_MAX_IMAGE_SIZE) + ok = ok && (total_size <= (uint64_t)WEBP_MAX_IMAGE_SIZE); +#endif + return ok; +} + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/imageio/imageio_util.h b/internal/libwebp-1.2.1/imageio/imageio_util.h new file mode 100644 index 0000000..f135f56 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/imageio_util.h @@ -0,0 +1,64 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Utility functions used by the image decoders. +// + +#ifndef WEBP_IMAGEIO_IMAGEIO_UTIL_H_ +#define WEBP_IMAGEIO_IMAGEIO_UTIL_H_ + +#include +#include "webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// File I/O + +// Reopen file in binary (O_BINARY) mode. +// Returns 'file' on success, NULL otherwise. +FILE* ImgIoUtilSetBinaryMode(FILE* file); + +// Allocates storage for entire file 'file_name' and returns contents and size +// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should +// be deleted using WebPFree(). +// Note: for convenience, the data will be null-terminated with an extra byte +// (not accounted for in *data_size), in case the file is text and intended +// to be used as a C-string. +// If 'file_name' is NULL or equal to "-", input is read from stdin by calling +// the function ImgIoUtilReadFromStdin(). +int ImgIoUtilReadFile(const char* const file_name, + const uint8_t** data, size_t* data_size); + +// Same as ImgIoUtilReadFile(), but reads until EOF from stdin instead. +int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size); + +// Write a data segment into a file named 'file_name'. Returns true if ok. +// If 'file_name' is NULL or equal to "-", output is written to stdout. +int ImgIoUtilWriteFile(const char* const file_name, + const uint8_t* data, size_t data_size); + +//------------------------------------------------------------------------------ + +// Copy width x height pixels from 'src' to 'dst' honoring the strides. +void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride, + uint8_t* dst, int dst_stride, int width, int height); + +//------------------------------------------------------------------------------ + +// Returns 0 in case of overflow, memory over-allocation or excessive dimension. +int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t stride, size_t height); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_IMAGEIO_UTIL_H_ diff --git a/internal/libwebp-1.2.1/imageio/jpegdec.c b/internal/libwebp-1.2.1/imageio/jpegdec.c new file mode 100644 index 0000000..1af4a82 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/jpegdec.c @@ -0,0 +1,360 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// JPEG decode. + +#include "./jpegdec.h" + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include + +#ifdef WEBP_HAVE_JPEG +#include +#include +#include +#include +#include + +#include "webp/encode.h" +#include "./imageio_util.h" +#include "./metadata.h" + +// ----------------------------------------------------------------------------- +// Metadata processing + +#ifndef JPEG_APP1 +# define JPEG_APP1 (JPEG_APP0 + 1) +#endif +#ifndef JPEG_APP2 +# define JPEG_APP2 (JPEG_APP0 + 2) +#endif + +typedef struct { + const uint8_t* data; + size_t data_length; + int seq; // this segment's sequence number [1, 255] for use in reassembly. +} ICCPSegment; + +static void SaveMetadataMarkers(j_decompress_ptr dinfo) { + const unsigned int max_marker_length = 0xffff; + jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length); // Exif/XMP + jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length); // ICC profile +} + +static int CompareICCPSegments(const void* a, const void* b) { + const ICCPSegment* s1 = (const ICCPSegment*)a; + const ICCPSegment* s2 = (const ICCPSegment*)b; + return s1->seq - s2->seq; +} + +// Extract ICC profile segments from the marker list in 'dinfo', reassembling +// and storing them in 'iccp'. +// Returns true on success and false for memory errors and corrupt profiles. +static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) { + // ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files + static const char kICCPSignature[] = "ICC_PROFILE"; + static const size_t kICCPSignatureLength = 12; // signature includes '\0' + static const size_t kICCPSkipLength = 14; // signature + seq & count + int expected_count = 0; + int actual_count = 0; + int seq_max = 0; + size_t total_size = 0; + ICCPSegment iccp_segments[255]; + jpeg_saved_marker_ptr marker; + + memset(iccp_segments, 0, sizeof(iccp_segments)); + for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker->marker == JPEG_APP2 && + marker->data_length > kICCPSkipLength && + !memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) { + // ICC_PROFILE\0; 'seq' starts at 1. + const int seq = marker->data[kICCPSignatureLength]; + const int count = marker->data[kICCPSignatureLength + 1]; + const size_t segment_size = marker->data_length - kICCPSkipLength; + ICCPSegment* segment; + + if (segment_size == 0 || count == 0 || seq == 0) { + fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)" + " cannot be 0!\n", + (int)segment_size, seq, count); + return 0; + } + + if (expected_count == 0) { + expected_count = count; + } else if (expected_count != count) { + fprintf(stderr, "[ICCP] Inconsistent segment count (%d / %d)!\n", + expected_count, count); + return 0; + } + + segment = iccp_segments + seq - 1; + if (segment->data_length != 0) { + fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq); + return 0; + } + + segment->data = marker->data + kICCPSkipLength; + segment->data_length = segment_size; + segment->seq = seq; + total_size += segment_size; + if (seq > seq_max) seq_max = seq; + ++actual_count; + } + } + + if (actual_count == 0) return 1; + if (seq_max != actual_count) { + fprintf(stderr, "[ICCP] Discontinuous segments, expected: %d actual: %d!\n", + actual_count, seq_max); + return 0; + } + if (expected_count != actual_count) { + fprintf(stderr, "[ICCP] Segment count: %d does not match expected: %d!\n", + actual_count, expected_count); + return 0; + } + + // The segments may appear out of order in the file, sort them based on + // sequence number before assembling the payload. + qsort(iccp_segments, actual_count, sizeof(*iccp_segments), + CompareICCPSegments); + + iccp->bytes = (uint8_t*)malloc(total_size); + if (iccp->bytes == NULL) return 0; + iccp->size = total_size; + + { + int i; + size_t offset = 0; + for (i = 0; i < seq_max; ++i) { + memcpy(iccp->bytes + offset, + iccp_segments[i].data, iccp_segments[i].data_length); + offset += iccp_segments[i].data_length; + } + } + return 1; +} + +// Returns true on success and false for memory errors and corrupt profiles. +// The caller must use MetadataFree() on 'metadata' in all cases. +static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo, + Metadata* const metadata) { + static const struct { + int marker; + const char* signature; + size_t signature_length; + size_t storage_offset; + } kJPEGMetadataMap[] = { + // Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ... + { JPEG_APP1, "Exif\0", 6, METADATA_OFFSET(exif) }, + // XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG + // TODO(jzern) Add support for 'ExtendedXMP' + { JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) }, + { 0, NULL, 0, 0 }, + }; + jpeg_saved_marker_ptr marker; + // Treat ICC profiles separately as they may be segmented and out of order. + if (!StoreICCP(dinfo, &metadata->iccp)) return 0; + + for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) { + int i; + for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) { + if (marker->marker == kJPEGMetadataMap[i].marker && + marker->data_length > kJPEGMetadataMap[i].signature_length && + !memcmp(marker->data, kJPEGMetadataMap[i].signature, + kJPEGMetadataMap[i].signature_length)) { + MetadataPayload* const payload = + (MetadataPayload*)((uint8_t*)metadata + + kJPEGMetadataMap[i].storage_offset); + + if (payload->bytes == NULL) { + const char* marker_data = (const char*)marker->data + + kJPEGMetadataMap[i].signature_length; + const size_t marker_data_length = + marker->data_length - kJPEGMetadataMap[i].signature_length; + if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0; + } else { + fprintf(stderr, "Ignoring additional '%s' marker\n", + kJPEGMetadataMap[i].signature); + } + } + } + } + return 1; +} + +#undef JPEG_APP1 +#undef JPEG_APP2 + +// ----------------------------------------------------------------------------- +// JPEG decoding + +struct my_error_mgr { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +static void my_error_exit(j_common_ptr dinfo) { + struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err; + fprintf(stderr, "libjpeg error: "); + dinfo->err->output_message(dinfo); + longjmp(myerr->setjmp_buffer, 1); +} + +typedef struct { + struct jpeg_source_mgr pub; + const uint8_t* data; + size_t data_size; +} JPEGReadContext; + +static void ContextInit(j_decompress_ptr cinfo) { + JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src; + ctx->pub.next_input_byte = ctx->data; + ctx->pub.bytes_in_buffer = ctx->data_size; +} + +static boolean ContextFill(j_decompress_ptr cinfo) { + // we shouldn't get here. + ERREXIT(cinfo, JERR_FILE_READ); + return FALSE; +} + +static void ContextSkip(j_decompress_ptr cinfo, long jump_size) { + JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src; + size_t jump = (size_t)jump_size; + if (jump > ctx->pub.bytes_in_buffer) { // Don't overflow the buffer. + jump = ctx->pub.bytes_in_buffer; + } + ctx->pub.bytes_in_buffer -= jump; + ctx->pub.next_input_byte += jump; +} + +static void ContextTerm(j_decompress_ptr cinfo) { + (void)cinfo; +} + +static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo, + JPEGReadContext* const ctx) { + cinfo->src = (struct jpeg_source_mgr*)ctx; + ctx->pub.init_source = ContextInit; + ctx->pub.fill_input_buffer = ContextFill; + ctx->pub.skip_input_data = ContextSkip; + ctx->pub.resync_to_restart = jpeg_resync_to_restart; + ctx->pub.term_source = ContextTerm; + ctx->pub.bytes_in_buffer = 0; + ctx->pub.next_input_byte = NULL; +} + +int ReadJPEG(const uint8_t* const data, size_t data_size, + WebPPicture* const pic, int keep_alpha, + Metadata* const metadata) { + volatile int ok = 0; + int width, height; + int64_t stride; + volatile struct jpeg_decompress_struct dinfo; + struct my_error_mgr jerr; + uint8_t* volatile rgb = NULL; + JSAMPROW buffer[1]; + JPEGReadContext ctx; + + if (data == NULL || data_size == 0 || pic == NULL) return 0; + + (void)keep_alpha; + memset(&ctx, 0, sizeof(ctx)); + ctx.data = data; + ctx.data_size = data_size; + + memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp safety + dinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + + if (setjmp(jerr.setjmp_buffer)) { + Error: + MetadataFree(metadata); + jpeg_destroy_decompress((j_decompress_ptr)&dinfo); + goto End; + } + + jpeg_create_decompress((j_decompress_ptr)&dinfo); + ContextSetup(&dinfo, &ctx); + if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo); + jpeg_read_header((j_decompress_ptr)&dinfo, TRUE); + + dinfo.out_color_space = JCS_RGB; + dinfo.do_fancy_upsampling = TRUE; + + jpeg_start_decompress((j_decompress_ptr)&dinfo); + + if (dinfo.output_components != 3) { + goto Error; + } + + width = dinfo.output_width; + height = dinfo.output_height; + stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb); + + if (stride != (int)stride || + !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) { + goto Error; + } + + rgb = (uint8_t*)malloc((size_t)stride * height); + if (rgb == NULL) { + goto Error; + } + buffer[0] = (JSAMPLE*)rgb; + + while (dinfo.output_scanline < dinfo.output_height) { + if (jpeg_read_scanlines((j_decompress_ptr)&dinfo, buffer, 1) != 1) { + goto Error; + } + buffer[0] += stride; + } + + if (metadata != NULL) { + ok = ExtractMetadataFromJPEG((j_decompress_ptr)&dinfo, metadata); + if (!ok) { + fprintf(stderr, "Error extracting JPEG metadata!\n"); + goto Error; + } + } + + jpeg_finish_decompress((j_decompress_ptr)&dinfo); + jpeg_destroy_decompress((j_decompress_ptr)&dinfo); + + // WebP conversion. + pic->width = width; + pic->height = height; + ok = WebPPictureImportRGB(pic, rgb, (int)stride); + if (!ok) goto Error; + + End: + free(rgb); + return ok; +} +#else // !WEBP_HAVE_JPEG +int ReadJPEG(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata) { + (void)data; + (void)data_size; + (void)pic; + (void)keep_alpha; + (void)metadata; + fprintf(stderr, "JPEG support not compiled. Please install the libjpeg " + "development package before building.\n"); + return 0; +} +#endif // WEBP_HAVE_JPEG + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/imageio/jpegdec.h b/internal/libwebp-1.2.1/imageio/jpegdec.h new file mode 100644 index 0000000..effc14f --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/jpegdec.h @@ -0,0 +1,37 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// JPEG decode. + +#ifndef WEBP_IMAGEIO_JPEGDEC_H_ +#define WEBP_IMAGEIO_JPEGDEC_H_ + +#include "webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Metadata; +struct WebPPicture; + +// Reads a JPEG from 'data', returning the decoded output in 'pic'. +// The output is RGB or YUV depending on pic->use_argb value. +// Returns true on success. +// 'keep_alpha' has no effect, but is kept for coherence with other signatures +// for image readers. +int ReadJPEG(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_JPEGDEC_H_ diff --git a/internal/libwebp-1.2.1/imageio/metadata.c b/internal/libwebp-1.2.1/imageio/metadata.c new file mode 100644 index 0000000..936f2f4 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/metadata.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. +// ----------------------------------------------------------------------------- +// +// Metadata types and functions. +// + +#include "./metadata.h" + +#include +#include + +#include "webp/types.h" + +void MetadataInit(Metadata* const metadata) { + if (metadata == NULL) return; + memset(metadata, 0, sizeof(*metadata)); +} + +void MetadataPayloadDelete(MetadataPayload* const payload) { + if (payload == NULL) return; + free(payload->bytes); + payload->bytes = NULL; + payload->size = 0; +} + +void MetadataFree(Metadata* const metadata) { + if (metadata == NULL) return; + MetadataPayloadDelete(&metadata->exif); + MetadataPayloadDelete(&metadata->iccp); + MetadataPayloadDelete(&metadata->xmp); +} + +int MetadataCopy(const char* metadata, size_t metadata_len, + MetadataPayload* const payload) { + if (metadata == NULL || metadata_len == 0 || payload == NULL) return 0; + payload->bytes = (uint8_t*)malloc(metadata_len); + if (payload->bytes == NULL) return 0; + payload->size = metadata_len; + memcpy(payload->bytes, metadata, metadata_len); + return 1; +} + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/imageio/metadata.h b/internal/libwebp-1.2.1/imageio/metadata.h new file mode 100644 index 0000000..1d5be91 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/metadata.h @@ -0,0 +1,47 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Metadata types and functions. +// + +#ifndef WEBP_IMAGEIO_METADATA_H_ +#define WEBP_IMAGEIO_METADATA_H_ + +#include "webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MetadataPayload { + uint8_t* bytes; + size_t size; +} MetadataPayload; + +typedef struct Metadata { + MetadataPayload exif; + MetadataPayload iccp; + MetadataPayload xmp; +} Metadata; + +#define METADATA_OFFSET(x) offsetof(Metadata, x) + +void MetadataInit(Metadata* const metadata); +void MetadataPayloadDelete(MetadataPayload* const payload); +void MetadataFree(Metadata* const metadata); + +// Stores 'metadata' to 'payload->bytes', returns false on allocation error. +int MetadataCopy(const char* metadata, size_t metadata_len, + MetadataPayload* const payload); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_METADATA_H_ diff --git a/internal/libwebp-1.2.1/imageio/pngdec.c b/internal/libwebp-1.2.1/imageio/pngdec.c new file mode 100644 index 0000000..44d0d1a --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/pngdec.c @@ -0,0 +1,374 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// PNG decode. + +#include "./pngdec.h" + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include + +#ifdef WEBP_HAVE_PNG +#ifndef PNG_USER_MEM_SUPPORTED +#define PNG_USER_MEM_SUPPORTED // for png_create_read_struct_2 +#endif +#include +#include // note: this must be included *after* png.h +#include +#include + +#include "webp/encode.h" +#include "./imageio_util.h" +#include "./metadata.h" + +#define LOCAL_PNG_VERSION ((PNG_LIBPNG_VER_MAJOR << 8) | PNG_LIBPNG_VER_MINOR) +#define LOCAL_PNG_PREREQ(maj, min) \ + (LOCAL_PNG_VERSION >= (((maj) << 8) | (min))) + +static void PNGAPI error_function(png_structp png, png_const_charp error) { + if (error != NULL) fprintf(stderr, "libpng error: %s\n", error); + longjmp(png_jmpbuf(png), 1); +} + +#if LOCAL_PNG_PREREQ(1,4) +typedef png_alloc_size_t LocalPngAllocSize; +#else +typedef png_size_t LocalPngAllocSize; +#endif + +static png_voidp MallocFunc(png_structp png_ptr, LocalPngAllocSize size) { + (void)png_ptr; + if (size != (size_t)size) return NULL; + if (!ImgIoUtilCheckSizeArgumentsOverflow(size, 1)) return NULL; + return (png_voidp)malloc((size_t)size); +} + +static void FreeFunc(png_structp png_ptr, png_voidp ptr) { + (void)png_ptr; + free(ptr); +} + +// Converts the NULL terminated 'hexstring' which contains 2-byte character +// representations of hex values to raw data. +// 'hexstring' may contain values consisting of [A-F][a-f][0-9] in pairs, +// e.g., 7af2..., separated by any number of newlines. +// 'expected_length' is the anticipated processed size. +// On success the raw buffer is returned with its length equivalent to +// 'expected_length'. NULL is returned if the processed length is less than +// 'expected_length' or any character aside from those above is encountered. +// The returned buffer must be freed by the caller. +static uint8_t* HexStringToBytes(const char* hexstring, + size_t expected_length) { + const char* src = hexstring; + size_t actual_length = 0; + uint8_t* const raw_data = (uint8_t*)malloc(expected_length); + uint8_t* dst; + + if (raw_data == NULL) return NULL; + + for (dst = raw_data; actual_length < expected_length && *src != '\0'; ++src) { + char* end; + char val[3]; + if (*src == '\n') continue; + val[0] = *src++; + val[1] = *src; + val[2] = '\0'; + *dst++ = (uint8_t)strtol(val, &end, 16); + if (end != val + 2) break; + ++actual_length; + } + + if (actual_length != expected_length) { + free(raw_data); + return NULL; + } + return raw_data; +} + +static int ProcessRawProfile(const char* profile, size_t profile_len, + MetadataPayload* const payload) { + const char* src = profile; + char* end; + int expected_length; + + if (profile == NULL || profile_len == 0) return 0; + + // ImageMagick formats 'raw profiles' as + // '\n\n(%8lu)\n\n'. + if (*src != '\n') { + fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n", + *src); + return 0; + } + ++src; + // skip the profile name and extract the length. + while (*src != '\0' && *src++ != '\n') {} + expected_length = (int)strtol(src, &end, 10); + if (*end != '\n') { + fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n", + *end); + return 0; + } + ++end; + + // 'end' now points to the profile payload. + payload->bytes = HexStringToBytes(end, expected_length); + if (payload->bytes == NULL) return 0; + payload->size = expected_length; + return 1; +} + +static const struct { + const char* name; + int (*process)(const char* profile, size_t profile_len, + MetadataPayload* const payload); + size_t storage_offset; +} kPNGMetadataMap[] = { + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PNG.html#TextualData + // See also: ExifTool on CPAN. + { "Raw profile type exif", ProcessRawProfile, METADATA_OFFSET(exif) }, + { "Raw profile type xmp", ProcessRawProfile, METADATA_OFFSET(xmp) }, + // Exiftool puts exif data in APP1 chunk, too. + { "Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif) }, + // XMP Specification Part 3, Section 3 #PNG + { "XML:com.adobe.xmp", MetadataCopy, METADATA_OFFSET(xmp) }, + { NULL, NULL, 0 }, +}; + +// Looks for metadata at both the beginning and end of the PNG file, giving +// preference to the head. +// Returns true on success. The caller must use MetadataFree() on 'metadata' in +// all cases. +static int ExtractMetadataFromPNG(png_structp png, + png_infop const head_info, + png_infop const end_info, + Metadata* const metadata) { + int p; + + for (p = 0; p < 2; ++p) { + png_infop const info = (p == 0) ? head_info : end_info; + png_textp text = NULL; + const png_uint_32 num = png_get_text(png, info, &text, NULL); + png_uint_32 i; + // Look for EXIF / XMP metadata. + for (i = 0; i < num; ++i, ++text) { + int j; + for (j = 0; kPNGMetadataMap[j].name != NULL; ++j) { + if (!strcmp(text->key, kPNGMetadataMap[j].name)) { + MetadataPayload* const payload = + (MetadataPayload*)((uint8_t*)metadata + + kPNGMetadataMap[j].storage_offset); + png_size_t text_length; + switch (text->compression) { +#ifdef PNG_iTXt_SUPPORTED + case PNG_ITXT_COMPRESSION_NONE: + case PNG_ITXT_COMPRESSION_zTXt: + text_length = text->itxt_length; + break; +#endif + case PNG_TEXT_COMPRESSION_NONE: + case PNG_TEXT_COMPRESSION_zTXt: + default: + text_length = text->text_length; + break; + } + if (payload->bytes != NULL) { + fprintf(stderr, "Ignoring additional '%s'\n", text->key); + } else if (!kPNGMetadataMap[j].process(text->text, text_length, + payload)) { + fprintf(stderr, "Failed to process: '%s'\n", text->key); + return 0; + } + break; + } + } + } + // Look for an ICC profile. + { + png_charp name; + int comp_type; +#if LOCAL_PNG_PREREQ(1,5) + png_bytep profile; +#else + png_charp profile; +#endif + png_uint_32 len; + + if (png_get_iCCP(png, info, + &name, &comp_type, &profile, &len) == PNG_INFO_iCCP) { + if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0; + } + } + } + return 1; +} + +typedef struct { + const uint8_t* data; + size_t data_size; + png_size_t offset; +} PNGReadContext; + +static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) { + PNGReadContext* const ctx = (PNGReadContext*)png_get_io_ptr(png_ptr); + if (ctx->data_size - ctx->offset < length) { + png_error(png_ptr, "ReadFunc: invalid read length (overflow)!"); + } + memcpy(data, ctx->data + ctx->offset, length); + ctx->offset += length; +} + +int ReadPNG(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, + int keep_alpha, struct Metadata* const metadata) { + volatile png_structp png = NULL; + volatile png_infop info = NULL; + volatile png_infop end_info = NULL; + PNGReadContext context = { NULL, 0, 0 }; + int color_type, bit_depth, interlaced; + int has_alpha; + int num_passes; + int p; + volatile int ok = 0; + png_uint_32 width, height, y; + int64_t stride; + uint8_t* volatile rgb = NULL; + + if (data == NULL || data_size == 0 || pic == NULL) return 0; + + context.data = data; + context.data_size = data_size; + + png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, + NULL, MallocFunc, FreeFunc); + if (png == NULL) goto End; + + png_set_error_fn(png, 0, error_function, NULL); + if (setjmp(png_jmpbuf(png))) { + Error: + MetadataFree(metadata); + goto End; + } + +#if LOCAL_PNG_PREREQ(1,5) || \ + (LOCAL_PNG_PREREQ(1,4) && PNG_LIBPNG_VER_RELEASE >= 1) + // If it looks like the bitstream is going to need more memory than libpng's + // internal limit (default: 8M), try to (reasonably) raise it. + if (data_size > png_get_chunk_malloc_max(png) && data_size < (1u << 24)) { + png_set_chunk_malloc_max(png, data_size); + } +#endif + + info = png_create_info_struct(png); + if (info == NULL) goto Error; + end_info = png_create_info_struct(png); + if (end_info == NULL) goto Error; + + png_set_read_fn(png, &context, ReadFunc); + png_read_info(png, info); + if (!png_get_IHDR(png, info, + &width, &height, &bit_depth, &color_type, &interlaced, + NULL, NULL)) goto Error; + + png_set_strip_16(png); + png_set_packing(png); + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png); + } + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + if (bit_depth < 8) { + png_set_expand_gray_1_2_4_to_8(png); + } + png_set_gray_to_rgb(png); + } + if (png_get_valid(png, info, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png); + has_alpha = 1; + } else { + has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA); + } + + // Apply gamma correction if needed. + { + double image_gamma = 1 / 2.2, screen_gamma = 2.2; + int srgb_intent; + if (png_get_sRGB(png, info, &srgb_intent) || + png_get_gAMA(png, info, &image_gamma)) { + png_set_gamma(png, screen_gamma, image_gamma); + } + } + + if (!keep_alpha) { + png_set_strip_alpha(png); + has_alpha = 0; + } + + num_passes = png_set_interlace_handling(png); + png_read_update_info(png, info); + + stride = (int64_t)(has_alpha ? 4 : 3) * width * sizeof(*rgb); + if (stride != (int)stride || + !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) { + goto Error; + } + + rgb = (uint8_t*)malloc((size_t)stride * height); + if (rgb == NULL) goto Error; + for (p = 0; p < num_passes; ++p) { + png_bytep row = rgb; + for (y = 0; y < height; ++y) { + png_read_rows(png, &row, NULL, 1); + row += stride; + } + } + png_read_end(png, end_info); + + if (metadata != NULL && + !ExtractMetadataFromPNG(png, info, end_info, metadata)) { + fprintf(stderr, "Error extracting PNG metadata!\n"); + goto Error; + } + + pic->width = (int)width; + pic->height = (int)height; + ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, (int)stride) + : WebPPictureImportRGB(pic, rgb, (int)stride); + + if (!ok) { + goto Error; + } + + End: + if (png != NULL) { + png_destroy_read_struct((png_structpp)&png, + (png_infopp)&info, (png_infopp)&end_info); + } + free(rgb); + return ok; +} +#else // !WEBP_HAVE_PNG +int ReadPNG(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, + int keep_alpha, struct Metadata* const metadata) { + (void)data; + (void)data_size; + (void)pic; + (void)keep_alpha; + (void)metadata; + fprintf(stderr, "PNG support not compiled. Please install the libpng " + "development package before building.\n"); + return 0; +} +#endif // WEBP_HAVE_PNG + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/imageio/pngdec.h b/internal/libwebp-1.2.1/imageio/pngdec.h new file mode 100644 index 0000000..e0a6122 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/pngdec.h @@ -0,0 +1,37 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// PNG decode. + +#ifndef WEBP_IMAGEIO_PNGDEC_H_ +#define WEBP_IMAGEIO_PNGDEC_H_ + +#include "webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Metadata; +struct WebPPicture; + +// Reads a PNG from 'data', returning the decoded output in 'pic'. +// Output is RGBA or YUVA, depending on pic->use_argb value. +// If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA +// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV. +// Returns true on success. +int ReadPNG(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, + int keep_alpha, struct Metadata* const metadata); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_PNGDEC_H_ diff --git a/internal/libwebp-1.2.1/imageio/pnmdec.c b/internal/libwebp-1.2.1/imageio/pnmdec.c new file mode 100644 index 0000000..2db007d --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/pnmdec.c @@ -0,0 +1,296 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// (limited) PNM decoder + +#include "./pnmdec.h" + +#include +#include +#include +#include +#include + +#include "webp/encode.h" +#include "./imageio_util.h" + +typedef enum { + WIDTH_FLAG = 1 << 0, + HEIGHT_FLAG = 1 << 1, + DEPTH_FLAG = 1 << 2, + MAXVAL_FLAG = 1 << 3, + TUPLE_FLAG = 1 << 4, + ALL_NEEDED_FLAGS = WIDTH_FLAG | HEIGHT_FLAG | DEPTH_FLAG | MAXVAL_FLAG +} PNMFlags; + +typedef struct { + const uint8_t* data; + size_t data_size; + int width, height; + int bytes_per_px; + int depth; // 1 (grayscale), 2 (grayscale + alpha), 3 (rgb), 4 (rgba) + int max_value; + int type; // 5, 6 or 7 + int seen_flags; +} PNMInfo; + +// ----------------------------------------------------------------------------- +// PNM decoding + +#define MAX_LINE_SIZE 1024 +static const size_t kMinPNMHeaderSize = 3; + +static size_t ReadLine(const uint8_t* const data, size_t off, size_t data_size, + char out[MAX_LINE_SIZE + 1], size_t* const out_size) { + size_t i = 0; + *out_size = 0; + redo: + for (i = 0; i < MAX_LINE_SIZE && off < data_size; ++i) { + out[i] = data[off++]; + if (out[i] == '\n') break; + } + if (off < data_size) { + if (i == 0) goto redo; // empty line + if (out[0] == '#') goto redo; // skip comment + } + out[i] = 0; // safety sentinel + *out_size = i; + return off; +} + +static size_t FlagError(const char flag[]) { + fprintf(stderr, "PAM header error: flags '%s' already seen.\n", flag); + return 0; +} + +// inspired from http://netpbm.sourceforge.net/doc/pam.html +static size_t ReadPAMFields(PNMInfo* const info, size_t off) { + char out[MAX_LINE_SIZE + 1]; + size_t out_size; + int tmp; + int expected_depth = -1; + assert(info != NULL); + while (1) { + off = ReadLine(info->data, off, info->data_size, out, &out_size); + if (off == 0) return 0; + if (sscanf(out, "WIDTH %d", &tmp) == 1) { + if (info->seen_flags & WIDTH_FLAG) return FlagError("WIDTH"); + info->seen_flags |= WIDTH_FLAG; + info->width = tmp; + } else if (sscanf(out, "HEIGHT %d", &tmp) == 1) { + if (info->seen_flags & HEIGHT_FLAG) return FlagError("HEIGHT"); + info->seen_flags |= HEIGHT_FLAG; + info->height = tmp; + } else if (sscanf(out, "DEPTH %d", &tmp) == 1) { + if (info->seen_flags & DEPTH_FLAG) return FlagError("DEPTH"); + info->seen_flags |= DEPTH_FLAG; + info->depth = tmp; + } else if (sscanf(out, "MAXVAL %d", &tmp) == 1) { + if (info->seen_flags & MAXVAL_FLAG) return FlagError("MAXVAL"); + info->seen_flags |= MAXVAL_FLAG; + info->max_value = tmp; + } else if (!strcmp(out, "TUPLTYPE RGB_ALPHA")) { + expected_depth = 4; + info->seen_flags |= TUPLE_FLAG; + } else if (!strcmp(out, "TUPLTYPE RGB")) { + expected_depth = 3; + info->seen_flags |= TUPLE_FLAG; + } else if (!strcmp(out, "TUPLTYPE GRAYSCALE_ALPHA")) { + expected_depth = 2; + info->seen_flags |= TUPLE_FLAG; + } else if (!strcmp(out, "TUPLTYPE GRAYSCALE")) { + expected_depth = 1; + info->seen_flags |= TUPLE_FLAG; + } else if (!strcmp(out, "ENDHDR")) { + break; + } else { + static const char kEllipsis[] = " ..."; + int i; + if (out_size > 20) sprintf(out + 20 - strlen(kEllipsis), kEllipsis); + for (i = 0; i < (int)strlen(out); ++i) { + // isprint() might trigger a "char-subscripts" warning if given a char. + if (!isprint((int)out[i])) out[i] = ' '; + } + fprintf(stderr, "PAM header error: unrecognized entry [%s]\n", out); + return 0; + } + } + if (!(info->seen_flags & ALL_NEEDED_FLAGS)) { + fprintf(stderr, "PAM header error: missing tags%s%s%s%s\n", + (info->seen_flags & WIDTH_FLAG) ? "" : " WIDTH", + (info->seen_flags & HEIGHT_FLAG) ? "" : " HEIGHT", + (info->seen_flags & DEPTH_FLAG) ? "" : " DEPTH", + (info->seen_flags & MAXVAL_FLAG) ? "" : " MAXVAL"); + return 0; + } + if (expected_depth != -1 && info->depth != expected_depth) { + fprintf(stderr, "PAM header error: expected DEPTH %d but got DEPTH %d\n", + expected_depth, info->depth); + return 0; + } + return off; +} + +static size_t ReadHeader(PNMInfo* const info) { + size_t off = 0; + char out[MAX_LINE_SIZE + 1]; + size_t out_size; + if (info == NULL) return 0; + if (info->data == NULL || info->data_size < kMinPNMHeaderSize) return 0; + + info->width = info->height = 0; + info->type = -1; + info->seen_flags = 0; + info->bytes_per_px = 0; + info->depth = 0; + info->max_value = 0; + + off = ReadLine(info->data, off, info->data_size, out, &out_size); + if (off == 0 || sscanf(out, "P%d", &info->type) != 1) return 0; + if (info->type == 7) { + off = ReadPAMFields(info, off); + } else { + off = ReadLine(info->data, off, info->data_size, out, &out_size); + if (off == 0 || sscanf(out, "%d %d", &info->width, &info->height) != 2) { + return 0; + } + off = ReadLine(info->data, off, info->data_size, out, &out_size); + if (off == 0 || sscanf(out, "%d", &info->max_value) != 1) return 0; + + // finish initializing missing fields + info->depth = (info->type == 5) ? 1 : 3; + } + // perform some basic numerical validation + if (info->width <= 0 || info->height <= 0 || + info->type <= 0 || info->type >= 9 || + info->depth <= 0 || info->depth > 4 || + info->max_value <= 0 || info->max_value >= 65536) { + return 0; + } + info->bytes_per_px = info->depth * (info->max_value > 255 ? 2 : 1); + return off; +} + +int ReadPNM(const uint8_t* const data, size_t data_size, + WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata) { + int ok = 0; + int i, j; + uint64_t stride, pixel_bytes, sample_size, depth; + uint8_t* rgb = NULL, *tmp_rgb; + size_t offset; + PNMInfo info; + + info.data = data; + info.data_size = data_size; + offset = ReadHeader(&info); + if (offset == 0) { + fprintf(stderr, "Error parsing PNM header.\n"); + goto End; + } + + if (info.type < 5 || info.type > 7) { + fprintf(stderr, "Unsupported P%d PNM format.\n", info.type); + goto End; + } + + // Some basic validations. + if (pic == NULL) goto End; + if (info.width > WEBP_MAX_DIMENSION || info.height > WEBP_MAX_DIMENSION) { + fprintf(stderr, "Invalid %dx%d dimension for PNM\n", + info.width, info.height); + goto End; + } + + pixel_bytes = (uint64_t)info.width * info.height * info.bytes_per_px; + if (data_size < offset + pixel_bytes) { + fprintf(stderr, "Truncated PNM file (P%d).\n", info.type); + goto End; + } + sample_size = (info.max_value > 255) ? 2 : 1; + // final depth + depth = (info.depth == 1 || info.depth == 3 || !keep_alpha) ? 3 : 4; + stride = depth * info.width; + if (stride != (size_t)stride || + !ImgIoUtilCheckSizeArgumentsOverflow(stride, info.height)) { + goto End; + } + + rgb = (uint8_t*)malloc((size_t)stride * info.height); + if (rgb == NULL) goto End; + + // Convert input. + // We only optimize for the sample_size=1, max_value=255, depth=1 case. + tmp_rgb = rgb; + for (j = 0; j < info.height; ++j) { + const uint8_t* in = data + offset; + offset += info.bytes_per_px * info.width; + assert(offset <= data_size); + if (info.max_value == 255 && info.depth >= 3) { + // RGB or RGBA + if (info.depth == 3 || keep_alpha) { + memcpy(tmp_rgb, in, info.depth * info.width * sizeof(*in)); + } else { + assert(info.depth == 4 && !keep_alpha); + for (i = 0; i < info.width; ++i) { + tmp_rgb[3 * i + 0] = in[4 * i + 0]; + tmp_rgb[3 * i + 1] = in[4 * i + 1]; + tmp_rgb[3 * i + 2] = in[4 * i + 2]; + } + } + } else { + // Unoptimized case, we need to handle non-trivial operations: + // * convert 16b to 8b (if max_value > 255) + // * rescale to [0..255] range (if max_value != 255) + // * drop the alpha channel (if keep_alpha is false) + const uint32_t round = info.max_value / 2; + int k = 0; + for (i = 0; i < info.width * info.depth; ++i) { + uint32_t v = (sample_size == 2) ? 256u * in[2 * i + 0] + in[2 * i + 1] + : in[i]; + if (info.max_value != 255) v = (v * 255u + round) / info.max_value; + if (v > 255u) v = 255u; + if (info.depth > 2) { + if (!keep_alpha && info.depth == 4 && (i % 4) == 3) { + // skip alpha + } else { + tmp_rgb[k] = v; + k += 1; + } + } else if (info.depth == 1 || (i % 2) == 0) { + tmp_rgb[k + 0] = tmp_rgb[k + 1] = tmp_rgb[k + 2] = v; + k += 3; + } else if (keep_alpha && info.depth == 2) { + tmp_rgb[k] = v; + k += 1; + } else { + // skip alpha + } + } + } + tmp_rgb += stride; + } + + // WebP conversion. + pic->width = info.width; + pic->height = info.height; + ok = (depth == 4) ? WebPPictureImportRGBA(pic, rgb, (int)stride) + : WebPPictureImportRGB(pic, rgb, (int)stride); + if (!ok) goto End; + + ok = 1; + End: + free((void*)rgb); + + (void)metadata; + (void)keep_alpha; + return ok; +} + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/imageio/pnmdec.h b/internal/libwebp-1.2.1/imageio/pnmdec.h new file mode 100644 index 0000000..c4d5823 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/pnmdec.h @@ -0,0 +1,37 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// partial PNM format decoder (ppm/pgm) + +#ifndef WEBP_IMAGEIO_PNMDEC_H_ +#define WEBP_IMAGEIO_PNMDEC_H_ + +#include "webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Metadata; +struct WebPPicture; + +// Reads a PNM file from 'data', returning the decoded output in 'pic'. +// The output is RGB or YUV depending on pic->use_argb value. +// Returns true on success. +// 'metadata' has no effect, but is kept for coherence with other signatures +// for image readers. +int ReadPNM(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_PNMDEC_H_ diff --git a/internal/libwebp-1.2.1/imageio/tiffdec.c b/internal/libwebp-1.2.1/imageio/tiffdec.c new file mode 100644 index 0000000..a9cb9ee --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/tiffdec.c @@ -0,0 +1,291 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// TIFF decode. + +#include "./tiffdec.h" + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include +#include +#include + +#ifdef WEBP_HAVE_TIFF +#include + +#include "webp/encode.h" +#include "./imageio_util.h" +#include "./metadata.h" + +static const struct { + ttag_t tag; + size_t storage_offset; +} kTIFFMetadataMap[] = { + { TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp) }, + { TIFFTAG_XMLPACKET, METADATA_OFFSET(xmp) }, + { 0, 0 }, +}; + +// Returns true on success. The caller must use MetadataFree() on 'metadata' in +// all cases. +static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) { + int i; + toff_t exif_ifd_offset; + + for (i = 0; kTIFFMetadataMap[i].tag != 0; ++i) { + MetadataPayload* const payload = + (MetadataPayload*)((uint8_t*)metadata + + kTIFFMetadataMap[i].storage_offset); + void* tag_data; + uint32_t tag_data_len; + + if (TIFFGetField(tif, kTIFFMetadataMap[i].tag, &tag_data_len, &tag_data) && + !MetadataCopy((const char*)tag_data, tag_data_len, payload)) { + return 0; + } + } + + // TODO(jzern): To extract the raw EXIF directory some parsing of it would be + // necessary to determine the overall size. In addition, value offsets in + // individual directory entries may need to be updated as, depending on the + // type, they are file based. + // Exif 2.2 Section 4.6.2 Tag Structure + // TIFF Revision 6.0 Part 1 Section 2 TIFF Structure #Image File Directory + if (TIFFGetField(tif, TIFFTAG_EXIFIFD, &exif_ifd_offset)) { + fprintf(stderr, "Warning: EXIF extraction from TIFF is unsupported.\n"); + } + return 1; +} + +// Ad-hoc structure to supply read-from-memory functionalities. +typedef struct { + const uint8_t* data; + toff_t size; + toff_t pos; +} MyData; + +static int MyClose(thandle_t opaque) { + (void)opaque; + return 0; +} + +static toff_t MySize(thandle_t opaque) { + const MyData* const my_data = (MyData*)opaque; + return my_data->size; +} + +static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) { + MyData* const my_data = (MyData*)opaque; + offset += (whence == SEEK_CUR) ? my_data->pos + : (whence == SEEK_SET) ? 0 + : my_data->size; + if (offset > my_data->size) return (toff_t)-1; + my_data->pos = offset; + return offset; +} + +static int MyMapFile(thandle_t opaque, void** base, toff_t* size) { + (void)opaque; + (void)base; + (void)size; + return 0; +} +static void MyUnmapFile(thandle_t opaque, void* base, toff_t size) { + (void)opaque; + (void)base; + (void)size; +} + +static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) { + MyData* const my_data = (MyData*)opaque; + if (my_data->pos + size > my_data->size) { + size = (tsize_t)(my_data->size - my_data->pos); + } + if (size > 0) { + memcpy(dst, my_data->data + my_data->pos, size); + my_data->pos += size; + } + return size; +} + +// Unmultiply Argb data. Taken from dsp/alpha_processing +// (we don't want to force a dependency to a libdspdec library). +#define MFIX 24 // 24bit fixed-point arithmetic +#define HALF ((1u << MFIX) >> 1) + +static uint32_t Unmult(uint8_t x, uint32_t mult) { + const uint32_t v = (x * mult + HALF) >> MFIX; + return (v > 255u) ? 255u : v; +} + +static WEBP_INLINE uint32_t GetScale(uint32_t a) { + return (255u << MFIX) / a; +} + +#undef MFIX +#undef HALF + +static void MultARGBRow(uint8_t* ptr, int width) { + int x; + for (x = 0; x < width; ++x, ptr += 4) { + const uint32_t alpha = ptr[3]; + if (alpha < 255) { + if (alpha == 0) { // alpha == 0 + ptr[0] = ptr[1] = ptr[2] = 0; + } else { + const uint32_t scale = GetScale(alpha); + ptr[0] = Unmult(ptr[0], scale); + ptr[1] = Unmult(ptr[1], scale); + ptr[2] = Unmult(ptr[2], scale); + } + } + } +} + +int ReadTIFF(const uint8_t* const data, size_t data_size, + WebPPicture* const pic, int keep_alpha, + Metadata* const metadata) { + MyData my_data = { data, (toff_t)data_size, 0 }; + TIFF* tif; + uint32_t image_width, image_height, tile_width, tile_height; + uint64_t stride; + uint16_t samples_per_px = 0; + uint16_t extra_samples = 0; + uint16_t* extra_samples_ptr = NULL; + uint32_t* raster; + int64_t alloc_size; + int ok = 0; + tdir_t dircount; + + if (data == NULL || data_size == 0 || data_size > INT_MAX || pic == NULL) { + return 0; + } + + tif = TIFFClientOpen("Memory", "r", &my_data, + MyRead, MyRead, MySeek, MyClose, + MySize, MyMapFile, MyUnmapFile); + if (tif == NULL) { + fprintf(stderr, "Error! Cannot parse TIFF file\n"); + return 0; + } + + dircount = TIFFNumberOfDirectories(tif); + if (dircount > 1) { + fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n" + "Only the first will be used, %d will be ignored.\n", + dircount - 1); + } + if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) { + fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n"); + goto End; + } + if (samples_per_px < 3 || samples_per_px > 4) goto End; // not supported + + if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &image_width) && + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &image_height))) { + fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n"); + goto End; + } + stride = (uint64_t)image_width * sizeof(*raster); + if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, image_height)) { + fprintf(stderr, "Error! TIFF image dimension (%d x %d) is too large.\n", + image_width, image_height); + goto End; + } + + // According to spec, a tile can be bigger than the image. However it should + // be a multiple of 16 and not way too large, so check that it's not more + // than twice the image size, for dimensions above some arbitrary minimum + // 32. We also check that they respect WebP's dimension and memory limit. + // Note that a tile can be 6byte/px in some cases. Here we assume + // 4byte/px with sizeof(*raster), to be conservative. + if (TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width) && + TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height)) { + if ((tile_width > 32 && tile_width / 2 > image_width) || + (tile_height > 32 && tile_height / 2 > image_height) || + ImgIoUtilCheckSizeArgumentsOverflow( + (uint64_t)tile_width * sizeof(*raster), tile_height)) { + fprintf(stderr, "Error! TIFF tile dimension (%d x %d) is too large.\n", + tile_width, tile_height); + goto End; + } + } + + if (samples_per_px > 3 && !TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, + &extra_samples, &extra_samples_ptr)) { + fprintf(stderr, "Error! Cannot retrieve TIFF ExtraSamples info.\n"); + goto End; + } + + // _Tiffmalloc uses a signed type for size. + alloc_size = (int64_t)(stride * image_height); + if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End; + + raster = (uint32_t*)_TIFFmalloc((tsize_t)alloc_size); + if (raster != NULL) { + if (TIFFReadRGBAImageOriented(tif, image_width, image_height, raster, + ORIENTATION_TOPLEFT, 1)) { + pic->width = image_width; + pic->height = image_height; + // TIFF data is ABGR +#ifdef WORDS_BIGENDIAN + TIFFSwabArrayOfLong(raster, image_width * image_height); +#endif + // if we have an alpha channel, we must un-multiply from rgbA to RGBA + if (extra_samples == 1 && extra_samples_ptr != NULL && + extra_samples_ptr[0] == EXTRASAMPLE_ASSOCALPHA) { + uint32_t y; + uint8_t* tmp = (uint8_t*)raster; + for (y = 0; y < image_height; ++y) { + MultARGBRow(tmp, image_width); + tmp += stride; + } + } + ok = keep_alpha + ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, (int)stride) + : WebPPictureImportRGBX(pic, (const uint8_t*)raster, (int)stride); + } + _TIFFfree(raster); + } else { + fprintf(stderr, "Error allocating TIFF RGBA memory!\n"); + } + + if (ok) { + if (metadata != NULL) { + ok = ExtractMetadataFromTIFF(tif, metadata); + if (!ok) { + fprintf(stderr, "Error extracting TIFF metadata!\n"); + MetadataFree(metadata); + WebPPictureFree(pic); + } + } + } + End: + TIFFClose(tif); + return ok; +} +#else // !WEBP_HAVE_TIFF +int ReadTIFF(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata) { + (void)data; + (void)data_size; + (void)pic; + (void)keep_alpha; + (void)metadata; + fprintf(stderr, "TIFF support not compiled. Please install the libtiff " + "development package before building.\n"); + return 0; +} +#endif // WEBP_HAVE_TIFF + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/imageio/tiffdec.h b/internal/libwebp-1.2.1/imageio/tiffdec.h new file mode 100644 index 0000000..0c8becc --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/tiffdec.h @@ -0,0 +1,37 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// TIFF decode. + +#ifndef WEBP_IMAGEIO_TIFFDEC_H_ +#define WEBP_IMAGEIO_TIFFDEC_H_ + +#include "webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Metadata; +struct WebPPicture; + +// Reads a TIFF from 'data', returning the decoded output in 'pic'. +// Output is RGBA or YUVA, depending on pic->use_argb value. +// If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA +// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV. +// Returns true on success. +int ReadTIFF(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_TIFFDEC_H_ diff --git a/internal/libwebp-1.2.1/imageio/webpdec.c b/internal/libwebp-1.2.1/imageio/webpdec.c new file mode 100644 index 0000000..aed60e1 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/webpdec.c @@ -0,0 +1,244 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// WebP decode. + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include "./webpdec.h" + +#include +#include +#include + +#include "webp/decode.h" +#include "webp/demux.h" +#include "webp/encode.h" +#include "../examples/unicode.h" +#include "./imageio_util.h" +#include "./metadata.h" + +//------------------------------------------------------------------------------ +// WebP decoding + +static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = { + "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR", + "UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA" +}; + +static void PrintAnimationWarning(const WebPDecoderConfig* const config) { + if (config->input.has_animation) { + fprintf(stderr, + "Error! Decoding of an animated WebP file is not supported.\n" + " Use webpmux to extract the individual frames or\n" + " vwebp to view this image.\n"); + } +} + +void PrintWebPError(const char* const in_file, int status) { + WFPRINTF(stderr, "Decoding of %s failed.\n", (const W_CHAR*)in_file); + fprintf(stderr, "Status: %d", status); + if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) { + fprintf(stderr, "(%s)", kStatusMessages[status]); + } + fprintf(stderr, "\n"); +} + +int LoadWebP(const char* const in_file, + const uint8_t** data, size_t* data_size, + WebPBitstreamFeatures* bitstream) { + VP8StatusCode status; + WebPBitstreamFeatures local_features; + if (!ImgIoUtilReadFile(in_file, data, data_size)) return 0; + + if (bitstream == NULL) { + bitstream = &local_features; + } + + status = WebPGetFeatures(*data, *data_size, bitstream); + if (status != VP8_STATUS_OK) { + WebPFree((void*)*data); + *data = NULL; + *data_size = 0; + PrintWebPError(in_file, status); + return 0; + } + return 1; +} + +//------------------------------------------------------------------------------ + +VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size, + WebPDecoderConfig* const config) { + if (config == NULL) return VP8_STATUS_INVALID_PARAM; + PrintAnimationWarning(config); + return WebPDecode(data, data_size, config); +} + +VP8StatusCode DecodeWebPIncremental( + const uint8_t* const data, size_t data_size, + WebPDecoderConfig* const config) { + VP8StatusCode status = VP8_STATUS_OK; + if (config == NULL) return VP8_STATUS_INVALID_PARAM; + + PrintAnimationWarning(config); + + // Decoding call. + { + WebPIDecoder* const idec = WebPIDecode(data, data_size, config); + if (idec == NULL) { + fprintf(stderr, "Failed during WebPIDecode().\n"); + return VP8_STATUS_OUT_OF_MEMORY; + } else { + status = WebPIUpdate(idec, data, data_size); + WebPIDelete(idec); + } + } + return status; +} + +// ----------------------------------------------------------------------------- +// Metadata + +static int ExtractMetadata(const uint8_t* const data, size_t data_size, + Metadata* const metadata) { + WebPData webp_data = { data, data_size }; + WebPDemuxer* const demux = WebPDemux(&webp_data); + WebPChunkIterator chunk_iter; + uint32_t flags; + + if (demux == NULL) return 0; + assert(metadata != NULL); + + flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); + + if ((flags & ICCP_FLAG) && WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter)) { + MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size, + &metadata->iccp); + WebPDemuxReleaseChunkIterator(&chunk_iter); + } + if ((flags & EXIF_FLAG) && WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) { + MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size, + &metadata->exif); + WebPDemuxReleaseChunkIterator(&chunk_iter); + } + if ((flags & XMP_FLAG) && WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter)) { + MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size, + &metadata->xmp); + WebPDemuxReleaseChunkIterator(&chunk_iter); + } + WebPDemuxDelete(demux); + return 1; +} + +// ----------------------------------------------------------------------------- + +int ReadWebP(const uint8_t* const data, size_t data_size, + WebPPicture* const pic, + int keep_alpha, Metadata* const metadata) { + int ok = 0; + VP8StatusCode status = VP8_STATUS_OK; + WebPDecoderConfig config; + WebPDecBuffer* const output_buffer = &config.output; + WebPBitstreamFeatures* const bitstream = &config.input; + + if (data == NULL || data_size == 0 || pic == NULL) return 0; + + if (!WebPInitDecoderConfig(&config)) { + fprintf(stderr, "Library version mismatch!\n"); + return 0; + } + + status = WebPGetFeatures(data, data_size, bitstream); + if (status != VP8_STATUS_OK) { + PrintWebPError("input data", status); + return 0; + } + + do { + const int has_alpha = keep_alpha && bitstream->has_alpha; + uint64_t stride; + pic->width = bitstream->width; + pic->height = bitstream->height; + if (pic->use_argb) { + stride = (uint64_t)bitstream->width * 4; + } else { + stride = (uint64_t)bitstream->width * (has_alpha ? 5 : 3) / 2; + pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420; + } + + if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, bitstream->height)) { + status = VP8_STATUS_OUT_OF_MEMORY; + break; + } + + ok = WebPPictureAlloc(pic); + if (!ok) { + status = VP8_STATUS_OUT_OF_MEMORY; + break; + } + if (pic->use_argb) { +#ifdef WORDS_BIGENDIAN + output_buffer->colorspace = MODE_ARGB; +#else + output_buffer->colorspace = MODE_BGRA; +#endif + output_buffer->u.RGBA.rgba = (uint8_t*)pic->argb; + output_buffer->u.RGBA.stride = pic->argb_stride * sizeof(uint32_t); + output_buffer->u.RGBA.size = output_buffer->u.RGBA.stride * pic->height; + } else { + output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV; + output_buffer->u.YUVA.y = pic->y; + output_buffer->u.YUVA.u = pic->u; + output_buffer->u.YUVA.v = pic->v; + output_buffer->u.YUVA.a = has_alpha ? pic->a : NULL; + output_buffer->u.YUVA.y_stride = pic->y_stride; + output_buffer->u.YUVA.u_stride = pic->uv_stride; + output_buffer->u.YUVA.v_stride = pic->uv_stride; + output_buffer->u.YUVA.a_stride = has_alpha ? pic->a_stride : 0; + output_buffer->u.YUVA.y_size = pic->height * pic->y_stride; + output_buffer->u.YUVA.u_size = (pic->height + 1) / 2 * pic->uv_stride; + output_buffer->u.YUVA.v_size = (pic->height + 1) / 2 * pic->uv_stride; + output_buffer->u.YUVA.a_size = pic->height * pic->a_stride; + } + output_buffer->is_external_memory = 1; + + status = DecodeWebP(data, data_size, &config); + ok = (status == VP8_STATUS_OK); + if (ok && !keep_alpha && pic->use_argb) { + // Need to wipe out the alpha value, as requested. + int x, y; + uint32_t* argb = pic->argb; + for (y = 0; y < pic->height; ++y) { + for (x = 0; x < pic->width; ++x) argb[x] |= 0xff000000u; + argb += pic->argb_stride; + } + } + } while (0); // <- so we can 'break' out of the loop + + if (status != VP8_STATUS_OK) { + PrintWebPError("input data", status); + ok = 0; + } + + WebPFreeDecBuffer(output_buffer); + + if (ok && metadata != NULL) { + ok = ExtractMetadata(data, data_size, metadata); + if (!ok) { + PrintWebPError("metadata", VP8_STATUS_BITSTREAM_ERROR); + } + } + if (!ok) WebPPictureFree(pic); + return ok; +} + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/imageio/webpdec.h b/internal/libwebp-1.2.1/imageio/webpdec.h new file mode 100644 index 0000000..d329d41 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/webpdec.h @@ -0,0 +1,67 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// WebP decode. + +#ifndef WEBP_IMAGEIO_WEBPDEC_H_ +#define WEBP_IMAGEIO_WEBPDEC_H_ + +#include "webp/decode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Metadata; +struct WebPPicture; + +//------------------------------------------------------------------------------ +// WebP decoding + +// Prints an informative error message regarding decode failure of 'in_file'. +// 'status' is treated as a VP8StatusCode and if valid will be printed as a +// text string. +void PrintWebPError(const char* const in_file, int status); + +// Reads a WebP from 'in_file', returning the contents and size in 'data' and +// 'data_size'. If not NULL, 'bitstream' is populated using WebPGetFeatures(). +// Returns true on success. +int LoadWebP(const char* const in_file, + const uint8_t** data, size_t* data_size, + WebPBitstreamFeatures* bitstream); + +// Decodes the WebP contained in 'data'. +// 'config' is a structure previously initialized by WebPInitDecoderConfig(). +// 'config->output' should have the desired colorspace selected. +// Returns the decoder status. On success 'config->output' will contain the +// decoded picture. +VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size, + WebPDecoderConfig* const config); + +// Same as DecodeWebP(), but using the incremental decoder. +VP8StatusCode DecodeWebPIncremental( + const uint8_t* const data, size_t data_size, + WebPDecoderConfig* const config); + +//------------------------------------------------------------------------------ + +// Decodes a WebP contained in 'data', returning the decoded output in 'pic'. +// Output is RGBA or YUVA, depending on pic->use_argb value. +// If 'keep_alpha' is true and the WebP has an alpha channel, the output is RGBA +// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV. +// Returns true on success. +int ReadWebP(const uint8_t* const data, size_t data_size, + struct WebPPicture* const pic, + int keep_alpha, struct Metadata* const metadata); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_WEBPDEC_H_ diff --git a/internal/libwebp-1.2.1/imageio/wicdec.c b/internal/libwebp-1.2.1/imageio/wicdec.c new file mode 100644 index 0000000..42001c6 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/wicdec.c @@ -0,0 +1,413 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Windows Imaging Component (WIC) decode. + +#include "./wicdec.h" + +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_WINCODEC_H +#ifdef __MINGW32__ +#define INITGUID // Without this GUIDs are declared extern and fail to link +#endif +#define CINTERFACE +#define COBJMACROS +#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++ + // code with COBJMACROS. +#include // CreateStreamOnHGlobal() +#include +#include +#include +#include + +#include "../examples/unicode.h" +#include "./imageio_util.h" +#include "./metadata.h" +#include "webp/encode.h" + +#define IFS(fn) \ + do { \ + if (SUCCEEDED(hr)) { \ + hr = (fn); \ + if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \ + } \ + } while (0) + +// modified version of DEFINE_GUID from guiddef.h. +#define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + static const GUID name = \ + { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + +#ifdef __cplusplus +#define MAKE_REFGUID(x) (x) +#else +#define MAKE_REFGUID(x) &(x) +#endif + +typedef struct WICFormatImporter { + const GUID* pixel_format; + int bytes_per_pixel; + int (*import)(WebPPicture* const, const uint8_t* const, int); +} WICFormatImporter; + +// From Microsoft SDK 7.0a -- wincodec.h +// Create local copies for compatibility when building against earlier +// versions of the SDK. +WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_, + 0x6fddc324, 0x4e03, 0x4bfe, + 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c); +WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_, + 0x6fddc324, 0x4e03, 0x4bfe, + 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d); +WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_, + 0x6fddc324, 0x4e03, 0x4bfe, + 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f); +WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_, + 0xf5c7ad2d, 0x6a8d, 0x43dd, + 0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9); +WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_, + 0x1562ff7c, 0xd352, 0x46f9, + 0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46); +WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_, + 0x6fddc324, 0x4e03, 0x4bfe, + 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16); + +static HRESULT OpenInputStream(const char* filename, IStream** stream) { + HRESULT hr = S_OK; + if (!WSTRCMP(filename, "-")) { + const uint8_t* data = NULL; + size_t data_size = 0; + const int ok = ImgIoUtilReadFile(filename, &data, &data_size); + if (ok) { + HGLOBAL image = GlobalAlloc(GMEM_MOVEABLE, data_size); + if (image != NULL) { + void* const image_mem = GlobalLock(image); + if (image_mem != NULL) { + memcpy(image_mem, data, data_size); + GlobalUnlock(image); + IFS(CreateStreamOnHGlobal(image, TRUE, stream)); + } else { + hr = E_FAIL; + } + } else { + hr = E_OUTOFMEMORY; + } + free((void*)data); + } else { + hr = E_FAIL; + } + } else { + IFS(SHCreateStreamOnFile((const LPTSTR)filename, STGM_READ, stream)); + } + + if (FAILED(hr)) { + _ftprintf(stderr, _T("Error opening input file %s (%08lx)\n"), + (const LPTSTR)filename, hr); + } + return hr; +} + +// ----------------------------------------------------------------------------- +// Metadata processing + +// Stores the first non-zero sized color profile from 'frame' to 'iccp'. +// Returns an HRESULT to indicate success or failure. The caller is responsible +// for freeing 'iccp->bytes' in either case. +static HRESULT ExtractICCP(IWICImagingFactory* const factory, + IWICBitmapFrameDecode* const frame, + MetadataPayload* const iccp) { + HRESULT hr = S_OK; + UINT i, count; + IWICColorContext** color_contexts; + + IFS(IWICBitmapFrameDecode_GetColorContexts(frame, 0, NULL, &count)); + if (FAILED(hr) || count == 0) { + // Treat unsupported operation as a non-fatal error. See crbug.com/webp/506. + return (hr == WINCODEC_ERR_UNSUPPORTEDOPERATION) ? S_OK : hr; + } + + color_contexts = (IWICColorContext**)calloc(count, sizeof(*color_contexts)); + if (color_contexts == NULL) return E_OUTOFMEMORY; + for (i = 0; SUCCEEDED(hr) && i < count; ++i) { + IFS(IWICImagingFactory_CreateColorContext(factory, &color_contexts[i])); + } + + if (SUCCEEDED(hr)) { + UINT num_color_contexts; + IFS(IWICBitmapFrameDecode_GetColorContexts(frame, + count, color_contexts, + &num_color_contexts)); + assert(FAILED(hr) || num_color_contexts <= count); + for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) { + WICColorContextType type; + IFS(IWICColorContext_GetType(color_contexts[i], &type)); + if (SUCCEEDED(hr) && type == WICColorContextProfile) { + UINT size; + IFS(IWICColorContext_GetProfileBytes(color_contexts[i], + 0, NULL, &size)); + if (SUCCEEDED(hr) && size > 0) { + iccp->bytes = (uint8_t*)malloc(size); + if (iccp->bytes == NULL) { + hr = E_OUTOFMEMORY; + break; + } + iccp->size = size; + IFS(IWICColorContext_GetProfileBytes(color_contexts[i], + (UINT)iccp->size, iccp->bytes, + &size)); + if (SUCCEEDED(hr) && size != iccp->size) { + fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n", + size, (uint32_t)iccp->size); + iccp->size = size; + } + break; + } + } + } + } + for (i = 0; i < count; ++i) { + if (color_contexts[i] != NULL) IUnknown_Release(color_contexts[i]); + } + free(color_contexts); + return hr; +} + +static HRESULT ExtractMetadata(IWICImagingFactory* const factory, + IWICBitmapFrameDecode* const frame, + Metadata* const metadata) { + // TODO(jzern): add XMP/EXIF extraction. + const HRESULT hr = ExtractICCP(factory, frame, &metadata->iccp); + if (FAILED(hr)) MetadataFree(metadata); + return hr; +} + +// ----------------------------------------------------------------------------- + +static int HasPalette(GUID pixel_format) { + return (IsEqualGUID(MAKE_REFGUID(pixel_format), + MAKE_REFGUID(GUID_WICPixelFormat1bppIndexed)) || + IsEqualGUID(MAKE_REFGUID(pixel_format), + MAKE_REFGUID(GUID_WICPixelFormat2bppIndexed)) || + IsEqualGUID(MAKE_REFGUID(pixel_format), + MAKE_REFGUID(GUID_WICPixelFormat4bppIndexed)) || + IsEqualGUID(MAKE_REFGUID(pixel_format), + MAKE_REFGUID(GUID_WICPixelFormat8bppIndexed))); +} + +static int HasAlpha(IWICImagingFactory* const factory, + IWICBitmapDecoder* const decoder, + IWICBitmapFrameDecode* const frame, + GUID pixel_format) { + int has_alpha; + if (HasPalette(pixel_format)) { + IWICPalette* frame_palette = NULL; + IWICPalette* global_palette = NULL; + BOOL frame_palette_has_alpha = FALSE; + BOOL global_palette_has_alpha = FALSE; + + // A palette may exist at the frame or container level, + // check IWICPalette::HasAlpha() for both if present. + if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &frame_palette)) && + SUCCEEDED(IWICBitmapFrameDecode_CopyPalette(frame, frame_palette))) { + IWICPalette_HasAlpha(frame_palette, &frame_palette_has_alpha); + } + if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &global_palette)) && + SUCCEEDED(IWICBitmapDecoder_CopyPalette(decoder, global_palette))) { + IWICPalette_HasAlpha(global_palette, &global_palette_has_alpha); + } + has_alpha = frame_palette_has_alpha || global_palette_has_alpha; + + if (frame_palette != NULL) IUnknown_Release(frame_palette); + if (global_palette != NULL) IUnknown_Release(global_palette); + } else { + has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format), + MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) || + IsEqualGUID(MAKE_REFGUID(pixel_format), + MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_)) || + IsEqualGUID(MAKE_REFGUID(pixel_format), + MAKE_REFGUID(GUID_WICPixelFormat64bppRGBA_)) || + IsEqualGUID(MAKE_REFGUID(pixel_format), + MAKE_REFGUID(GUID_WICPixelFormat64bppBGRA_)); + } + return has_alpha; +} + +int ReadPictureWithWIC(const char* const filename, + WebPPicture* const pic, int keep_alpha, + Metadata* const metadata) { + // From Microsoft SDK 6.0a -- ks.h + // Define a local copy to avoid link errors under mingw. + WEBP_DEFINE_GUID(GUID_NULL_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + static const WICFormatImporter kAlphaFormatImporters[] = { + { &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA }, + { &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA }, + { NULL, 0, NULL }, + }; + static const WICFormatImporter kNonAlphaFormatImporters[] = { + { &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR }, + { &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB }, + { NULL, 0, NULL }, + }; + HRESULT hr = S_OK; + IWICBitmapFrameDecode* frame = NULL; + IWICFormatConverter* converter = NULL; + IWICImagingFactory* factory = NULL; + IWICBitmapDecoder* decoder = NULL; + IStream* stream = NULL; + UINT frame_count = 0; + UINT width = 0, height = 0; + BYTE* rgb = NULL; + WICPixelFormatGUID src_pixel_format = GUID_WICPixelFormatUndefined; + const WICFormatImporter* importer = NULL; + GUID src_container_format = GUID_NULL_; + // From Windows Kits\10\Include\10.0.19041.0\um\wincodec.h + WEBP_DEFINE_GUID(GUID_ContainerFormatWebp_, + 0xe094b0e2, 0x67f2, 0x45b3, + 0xb0, 0xea, 0x11, 0x53, 0x37, 0xca, 0x7c, 0xf3); + static const GUID* kAlphaContainers[] = { + &GUID_ContainerFormatBmp, + &GUID_ContainerFormatPng, + &GUID_ContainerFormatTiff, + &GUID_ContainerFormatWebp_, + NULL + }; + int has_alpha = 0; + int64_t stride; + + if (filename == NULL || pic == NULL) return 0; + + IFS(CoInitialize(NULL)); + IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL, + CLSCTX_INPROC_SERVER, + MAKE_REFGUID(IID_IWICImagingFactory), + (LPVOID*)&factory)); + if (hr == REGDB_E_CLASSNOTREG) { + fprintf(stderr, + "Couldn't access Windows Imaging Component (are you running " + "Windows XP SP3 or newer?). Most formats not available. " + "Use -s for the available YUV input.\n"); + } + // Prepare for image decoding. + IFS(OpenInputStream(filename, &stream)); + IFS(IWICImagingFactory_CreateDecoderFromStream( + factory, stream, NULL, + WICDecodeMetadataCacheOnDemand, &decoder)); + IFS(IWICBitmapDecoder_GetFrameCount(decoder, &frame_count)); + if (SUCCEEDED(hr)) { + if (frame_count == 0) { + fprintf(stderr, "No frame found in input file.\n"); + hr = E_FAIL; + } else if (frame_count > 1) { + // WIC will be tried before native WebP decoding so avoid duplicating the + // error message. + hr = E_FAIL; + } + } + IFS(IWICBitmapDecoder_GetFrame(decoder, 0, &frame)); + IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format)); + IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format)); + + if (SUCCEEDED(hr) && keep_alpha) { + const GUID** guid; + for (guid = kAlphaContainers; *guid != NULL; ++guid) { + if (IsEqualGUID(MAKE_REFGUID(src_container_format), + MAKE_REFGUID(**guid))) { + has_alpha = HasAlpha(factory, decoder, frame, src_pixel_format); + break; + } + } + } + + // Prepare for pixel format conversion (if necessary). + IFS(IWICImagingFactory_CreateFormatConverter(factory, &converter)); + + for (importer = has_alpha ? kAlphaFormatImporters : kNonAlphaFormatImporters; + hr == S_OK && importer->import != NULL; ++importer) { + BOOL can_convert; + const HRESULT cchr = IWICFormatConverter_CanConvert( + converter, + MAKE_REFGUID(src_pixel_format), + MAKE_REFGUID(*importer->pixel_format), + &can_convert); + if (SUCCEEDED(cchr) && can_convert) break; + } + if (importer->import == NULL) hr = E_FAIL; + + IFS(IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame, + importer->pixel_format, + WICBitmapDitherTypeNone, + NULL, 0.0, WICBitmapPaletteTypeCustom)); + + // Decode. + IFS(IWICFormatConverter_GetSize(converter, &width, &height)); + stride = (int64_t)importer->bytes_per_pixel * width * sizeof(*rgb); + if (stride != (int)stride || + !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) { + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) { + rgb = (BYTE*)malloc((size_t)stride * height); + if (rgb == NULL) + hr = E_OUTOFMEMORY; + } + IFS(IWICFormatConverter_CopyPixels(converter, NULL, + (UINT)stride, (UINT)stride * height, rgb)); + + // WebP conversion. + if (SUCCEEDED(hr)) { + int ok; + pic->width = width; + pic->height = height; + pic->use_argb = 1; // For WIC, we always force to argb + ok = importer->import(pic, rgb, (int)stride); + if (!ok) hr = E_FAIL; + } + if (SUCCEEDED(hr)) { + if (metadata != NULL) { + hr = ExtractMetadata(factory, frame, metadata); + if (FAILED(hr)) { + fprintf(stderr, "Error extracting image metadata using WIC!\n"); + } + } + } + + // Cleanup. + if (converter != NULL) IUnknown_Release(converter); + if (frame != NULL) IUnknown_Release(frame); + if (decoder != NULL) IUnknown_Release(decoder); + if (factory != NULL) IUnknown_Release(factory); + if (stream != NULL) IUnknown_Release(stream); + free(rgb); + return SUCCEEDED(hr); +} +#else // !HAVE_WINCODEC_H +int ReadPictureWithWIC(const char* const filename, + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata) { + (void)filename; + (void)pic; + (void)keep_alpha; + (void)metadata; + fprintf(stderr, "Windows Imaging Component (WIC) support not compiled. " + "Visual Studio and mingw-w64 builds support WIC. Make sure " + "wincodec.h detection is working correctly if using autoconf " + "and HAVE_WINCODEC_H is defined before building.\n"); + return 0; +} +#endif // HAVE_WINCODEC_H + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/imageio/wicdec.h b/internal/libwebp-1.2.1/imageio/wicdec.h new file mode 100644 index 0000000..d9eeca8 --- /dev/null +++ b/internal/libwebp-1.2.1/imageio/wicdec.h @@ -0,0 +1,34 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Windows Imaging Component (WIC) decode. + +#ifndef WEBP_IMAGEIO_WICDEC_H_ +#define WEBP_IMAGEIO_WICDEC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct Metadata; +struct WebPPicture; + +// Reads an image from 'filename', returning the decoded output in 'pic'. +// If 'keep_alpha' is true and the image has an alpha channel, the output is +// RGBA otherwise it will be RGB. pic->use_argb is always forced to true. +// Returns true on success. +int ReadPictureWithWIC(const char* const filename, + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_IMAGEIO_WICDEC_H_ diff --git a/internal/libwebp-1.2.1/infra/common.sh b/internal/libwebp-1.2.1/infra/common.sh new file mode 100644 index 0000000..b4892d5 --- /dev/null +++ b/internal/libwebp-1.2.1/infra/common.sh @@ -0,0 +1,106 @@ +# Copyright (c) 2021, Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of Google nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +log_err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +####################################### +# Create build directory. Build directory will be deleted if it exists. +# Arguments: +# None. +# Returns: +# mkdir result. +####################################### +make_build_dir() { + if [[ "$#" -ne 1 ]]; then + return 1 + fi + + local build_dir + build_dir="$1" + rm -rf "${build_dir}" + mkdir -p "${build_dir}" +} + +####################################### +# Cleanup files from the build directory. +# Globals: +# LIBWEBP_ROOT repository's root path. +# Arguments: +# $1 build directory. +####################################### +cleanup() { + # $1 is not completely removed to allow for binary artifacts to be + # extracted. + find "${1:?"Build directory not defined"}" \ + \( -name "*.[ao]" -o -name "*.l[ao]" \) -exec rm -f {} + +} + +####################################### +# Setup ccache for toolchain. +# Globals: +# PATH +# Arguments: +# None. +####################################### +setup_ccache() { + if [[ -x "$(command -v ccache)" ]]; then + export CCACHE_CPP2=yes + export PATH="/usr/lib/ccache:${PATH}" + fi +} + +####################################### +# Detects whether test block should be run in the current test shard. +# Globals: +# TEST_TOTAL_SHARDS: Valid range: [1, N]. Defaults to 1. +# TEST_SHARD_INDEX: Valid range: [0, TEST_TOTAL_SHARDS). Defaults to 0. +# libwebp_test_id: current test number; incremented with each call. +# Arguments: +# None +# Returns: +# true if the shard is active +# false if the shard is inactive +####################################### +shard_should_run() { + TEST_TOTAL_SHARDS=${TEST_TOTAL_SHARDS:=1} + TEST_SHARD_INDEX=${TEST_SHARD_INDEX:=0} + libwebp_test_id=${libwebp_test_id:=-1} + : $((libwebp_test_id += 1)) + + if [[ "${TEST_SHARD_INDEX}" -lt 0 || + "${TEST_SHARD_INDEX}" -ge "${TEST_TOTAL_SHARDS}" ]]; then + log_err "Invalid TEST_SHARD_INDEX (${TEST_SHARD_INDEX})!" \ + "Expected [0, ${TEST_TOTAL_SHARDS})." + fi + + [[ "$((libwebp_test_id % TEST_TOTAL_SHARDS))" -eq "${TEST_SHARD_INDEX}" ]] +} diff --git a/internal/libwebp-1.2.1/infra/compile.sh b/internal/libwebp-1.2.1/infra/compile.sh new file mode 100755 index 0000000..18e9ebe --- /dev/null +++ b/internal/libwebp-1.2.1/infra/compile.sh @@ -0,0 +1,401 @@ +#!/bin/bash +# Copyright (c) 2021, Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of Google nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set -xe +LIBWEBP_ROOT="$(realpath "$(dirname "$0")/..")" +WORKSPACE=${WORKSPACE:-"$(mktemp -d -t webp.XXX)"} + +# shellcheck source=infra/common.sh +source "${LIBWEBP_ROOT}/infra/common.sh" + +usage() { + cat << EOF +Usage: compile.sh BUILD_TYPE TARGET +Options: +BUILD_TYPE supported build type: (shared, static, static-debug) +TARGET supported target platforms: + aarch64-linux-clang + aarch64-linux-gnu + arm-linux-gnueabi + arm-neon-linux-gnueabi + cmake + cmake-aarch64 + cmake-arm + cmake-clang + disable-near-lossless + disable-sse4.1 + disable-stats + force-aligned-32 + force-aligned-64 + gradle + i686-linux-asan + i686-linux-clang + i686-linux-gnu + i686-w64-mingw32 + mips2el-linux-gnu + mips32dspr2el-linux-gnu + mips32eb-linux-gnu + mips32el-linux-gnu + mips32r2el-linux-gnu + mips32r5el-linux-gnu + mips64r2el-linux-gnu + mips64r6el-linux-gnu + native + reduce-csp + reduce-size + reduce-size-disable-stats + visibility-default-gnu + visibility-hidden-clang + visibility-hidden-gnu + wasm + x86_64-linux-clang + x86_64-linux-gnu + x86_64-linux-msan + x86_64-w64-mingw32 +Environment variables: +WORKSPACE directory where the build is done +EOF +} + +################################################################################ +echo "Building libwebp in ${WORKSPACE}" + +if [[ ! -d "${WORKSPACE}" ]]; then + log_err "${WORKSPACE} directory does not exist" + exit 1 +fi + +BUILD_TYPE=${1:?"Build type not defined.$( + echo + usage +)"} +TARGET=${2:?"Target not defined.$( + echo + usage +)"} +readonly BUILD_DIR="${WORKSPACE}/build-${BUILD_TYPE}" + +trap 'cleanup ${BUILD_DIR}' EXIT +make_build_dir "${BUILD_DIR}" + +config_flags=() +case "${BUILD_TYPE}" in + shared*) ;; # Valid BUILD_TYPE but no setup required + static*) config_flags+=("--disable-shared") ;; + experimental) config_flags+=("--enable-experimental") ;; + *) + log_err "Invalid BUILD_TYPE" + usage + exit 1 + ;; +esac + +if grep -m 1 -q "enable-asserts" "${LIBWEBP_ROOT}/configure.ac"; then + config_flags+=("--enable-asserts") +fi + +case "${TARGET}" in + aarch64-linux-clang) + TARGET="aarch64-linux-gnu" + CC="clang" + CC="${CC} --target=aarch64-linux-gnu" + export CC + export CFLAGS="-isystem /usr/aarch64-linux-gnu/include" + ;; + arm-linux-gnueabi) + export CFLAGS="-O3 -march=armv7-a -mfloat-abi=softfp -ftree-vectorize" + ;; + arm-neon-linux-gnueabi) + TARGET="arm-linux-gnueabi" + CFLAGS="-O3 -march=armv7-a -mfpu=neon -mfloat-abi=softfp -ftree-vectorize" + export CFLAGS + ;; + mips2el-linux-gnu) + export CFLAGS="-EL -O2 -mips2" + TARGET="mipsel-linux-gnu" + ;; + mips32el-linux-gnu) + export CFLAGS="-EL -O2 -mips32" + TARGET="mipsel-linux-gnu" + ;; + mips32r2el-linux-gnu) + export CFLAGS="-EL -O2 -mips32r2" + TARGET="mipsel-linux-gnu" + ;; + mips32dspr2el-linux-gnu) + export CFLAGS="-EL -O2 -mdspr2" + TARGET="mipsel-linux-gnu" + ;; + mips32r5el-linux-gnu) + export CFLAGS="-EL -O2 -mips32r5 -mmsa" + TARGET="mipsel-linux-gnu" + ;; + mips32eb-linux-gnu) + export CFLAGS="-EB -O2 -mips32" + TARGET="mips-linux-gnu" + ;; + mips64r2el-linux-gnu) + export CFLAGS="-EL -O2 -mips64r2 -mabi=64" + TARGET="mips64el-linux-gnuabi64" + ;; + mips64r6el-linux-gnu) + export CFLAGS="-EL -O2 -mips64r6 -mabi=64 -mmsa" + TARGET="mips-img-linux-gnu" + ;; + i686-linux-gnu) + export CC="gcc -m32" + ;; + i686-linux-clang) + TARGET="i686-linux-gnu" + export CC="clang -m32" + ;; + i686-linux-asan) + TARGET="i686-linux-gnu" + export CC="clang -m32 -fsanitize=address" + ;; + i686-linux-msan) + TARGET="i686-linux-gnu" + export CC="clang -m32 -fsanitize=memory" + ;; + x86_64-linux-clang) + TARGET="x86_64-linux-gnu" + export CC=clang + ;; + x86_64-linux-msan) + TARGET="x86_64-linux-gnu" + export CC="clang -fsanitize=memory" + ;; + force-aligned-32) + config_flags+=("--enable-aligned") + TARGET="i686-linux-gnu" + export CC="gcc -m32" + ;; + force-aligned-64) + config_flags+=("--enable-aligned") + TARGET="x86_64-linux-gnu" + ;; + visibility-default-*) + export CFLAGS="-O2 -g -fvisibility=default" + TARGET="x86_64-linux-gnu" + ;; + visibility-hidden-*) + export CFLAGS="-O2 -g -fvisibility=hidden" + if [[ "${TARGET}" = "visibility-hidden-clang" ]]; then + export CC=clang + fi + TARGET="x86_64-linux-gnu" + ;; + disable-sse4.1) + grep "${TARGET}" "${LIBWEBP_ROOT}/configure.ac" || exit 0 + config_flags+=("--${TARGET}") + TARGET="x86_64-linux-gnu" + ;; + disable-near-lossless) + grep "${TARGET}" "${LIBWEBP_ROOT}/configure.ac" || exit 0 + config_flags+=("--${TARGET}") + TARGET="x86_64-linux-gnu" + ;; + disable-stats) + git -C "${LIBWEBP_ROOT}" grep WEBP_DISABLE_STATS || exit 0 + export CFLAGS="-O2 -g -DWEBP_DISABLE_STATS" + TARGET="x86_64-linux-gnu" + ;; + reduce-size) + git -C "${LIBWEBP_ROOT}" grep WEBP_REDUCE_SIZE || exit 0 + export CFLAGS="-O2 -g -DWEBP_REDUCE_SIZE" + TARGET="x86_64-linux-gnu" + ;; + reduce-size-disable-stats) + git -C "${LIBWEBP_ROOT}" grep -e WEBP_DISABLE_STATS -e WEBP_REDUCE_SIZE \ + || exit 0 + export CFLAGS="-O2 -g -DWEBP_DISABLE_STATS -DWEBP_REDUCE_SIZE" + TARGET="x86_64-linux-gnu" + ;; + reduce-csp) + git -C "${LIBWEBP_ROOT}" grep WEBP_REDUCE_CSP || exit 0 + export CFLAGS="-O2 -g -DWEBP_REDUCE_CSP" + TARGET="x86_64-linux-gnu" + ;; + x86_64-linux-gnu | *mingw32 | aarch64*) ;; # Default target configuration + # non-configure based builds + native) + setup_ccache + # exercise makefile.unix then quit + make -C "${LIBWEBP_ROOT}" -f makefile.unix -j all + for tgt in extras examples/anim_diff; do + grep -q -m 1 "${tgt}" "${LIBWEBP_ROOT}/makefile.unix" \ + && make -C "${LIBWEBP_ROOT}" -f makefile.unix -j "${tgt}" + done + [[ -d "${LIBWEBP_ROOT}/tests/fuzzer" ]] \ + && make -j -C "${LIBWEBP_ROOT}/tests/fuzzer" -f makefile.unix + exit 0 + ;; + cmake*) + setup_ccache + # exercise cmake then quit + opts=() + case "${TARGET}" in + cmake-clang) + opts+=("-DCMAKE_C_COMPILER=clang") + ;; + cmake-arm) + opts+=("-DCMAKE_C_COMPILER=arm-linux-gnueabi-gcc") + case "${GERRIT_BRANCH:-}" in + portable-intrinsics | 0.6.1) exit 0 ;; + *) ;; # Skip configuration + esac + ;; + cmake-aarch64) + opts+=("-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc") + case "${GERRIT_BRANCH:-}" in + portable-intrinsics | 0.6.1) exit 0 ;; + *) ;; # Skip configuration + esac + ;; + *) ;; # Skip configuration + esac + case "${BUILD_TYPE}" in + static*) + opts+=("-DBUILD_SHARED_LIBS=OFF") + ;; + experimental) + opts+=("-DWEBP_EXPERIMENTAL_FEATURES=ON" "-DBUILD_SHARED_LIBS=ON") + ;; + *) + opts+=("-DBUILD_SHARED_LIBS=ON") + ;; + esac + case "${BUILD_TYPE}" in + *debug) opts+=("-DCMAKE_BUILD_TYPE=Debug") ;; + *) opts+=("-DCMAKE_BUILD_TYPE=RelWithDebInfo") ;; + esac + cd "${BUILD_DIR}" + opts+=("-DWEBP_BUILD_CWEBP=ON" "-DWEBP_BUILD_DWEBP=ON") + grep -m 1 -q WEBP_BUILD_GIF2WEBP "${LIBWEBP_ROOT}/CMakeLists.txt" \ + && opts+=("-DWEBP_BUILD_GIF2WEBP=ON") + grep -m 1 -q WEBP_BUILD_IMG2WEBP "${LIBWEBP_ROOT}/CMakeLists.txt" \ + && opts+=("-DWEBP_BUILD_IMG2WEBP=ON") + cmake "${opts[@]}" "${LIBWEBP_ROOT}" + make VERBOSE=1 -j + case "${BUILD_TYPE}" in + static) + mkdir -p examples + cp [cd]webp examples + ;; + *) ;; # Skip configuration. + esac + + grep "install" "${LIBWEBP_ROOT}/CMakeLists.txt" || exit 0 + + make DESTDIR="${BUILD_DIR}/webp-install" install/strip + mkdir tmp + cd tmp + cat > CMakeLists.txt << EOF +cmake_minimum_required(VERSION 2.8.7) + +project(libwebp C) + +find_package(WebP) +if (NOT WebP_FOUND) + message(FATAL_ERROR "WebP package not found") +endif () +message("WebP_FOUND: \${WebP_FOUND}") +message("WebP_INCLUDE_DIRS: \${WebP_INCLUDE_DIRS}") +message("WebP_LIBRARIES: \${WebP_LIBRARIES}") +message("WEBP_INCLUDE_DIRS: \${WEBP_INCLUDE_DIRS}") +message("WEBP_LIBRARIES: \${WEBP_LIBRARIES}") +EOF + cmake . "${opts[@]}" \ + "-DCMAKE_PREFIX_PATH=${BUILD_DIR}/webp-install/usr/local" + exit 0 + ;; + gradle) + setup_ccache + # exercise gradle then quit + [[ -f "${LIBWEBP_ROOT}/gradlew" ]] || exit 0 + + cd "${BUILD_DIR}" + # TODO -g / --gradle-user-home could be used if there's a race between jobs + "${LIBWEBP_ROOT}/gradlew" -p "${LIBWEBP_ROOT}" buildAllExecutables + exit 0 + ;; + wasm) + grep -m 1 -q WEBP_ENABLE_WASM "${LIBWEBP_ROOT}/CMakeLists.txt" || exit 0 + opts+=("-DCMAKE_C_COMPILER=clang" "-DWEBP_ENABLE_WASM=ON") + opts+=("-DWEBP_BUILD_CWEBP=ON" "-DWEBP_BUILD_DWEBP=ON") + case "${BUILD_TYPE}" in + *debug) opts+=("-DCMAKE_BUILD_TYPE=Debug") ;; + *) opts+=("-DCMAKE_BUILD_TYPE=RelWithDebInfo") ;; + esac + cd "${BUILD_DIR}" + cmake "${opts[@]}" "${LIBWEBP_ROOT}" + make VERBOSE=1 -j + mkdir examples + case "${BUILD_TYPE}" in + static) + mkdir -p examples + cp [cd]webp examples + ;; + *) ;; # Skip configuration + esac + exit 0 + ;; + *) + log_err "Invalid TARGET" + usage + exit 1 + ;; +esac + +case "${TARGET}" in + *mingw32) ;; # Skip configuration + *) + case "${TARGET}-${CC}" in + static-debug-gcc* | static-debug-) + CFLAGS="${CFLAGS} -fprofile-arcs -ftest-coverage -O0 -g" + CXXFLAGS="${CXXFLAGS} -fprofile-arcs -ftest-coverage -O0 -g" + export CFLAGS CXXFLAGS + ;; + *) ;; # This case should not be reached. + esac + ;; +esac + +setup_ccache + +cd "${LIBWEBP_ROOT}" +./autogen.sh + +cd "${BUILD_DIR}" +"${LIBWEBP_ROOT}/configure" \ + --host "${TARGET}" --build "$("${LIBWEBP_ROOT}/config.guess")" \ + --enable-everything "${config_flags[@]}" +make -j V=1 diff --git a/internal/libwebp-1.2.1/infra/compile_android.sh b/internal/libwebp-1.2.1/infra/compile_android.sh new file mode 100755 index 0000000..013b97c --- /dev/null +++ b/internal/libwebp-1.2.1/infra/compile_android.sh @@ -0,0 +1,224 @@ +#!/bin/bash +# Copyright (c) 2021, Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of Google nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set -xe +LIBWEBP_ROOT="$(realpath "$(dirname "$0")/..")" +readonly LIBWEBP_ROOT +readonly WORKSPACE=${WORKSPACE:-"$(mktemp -d -t webp.android.XXX)"} +# shellcheck source=infra/common.sh +source "${LIBWEBP_ROOT}/infra/common.sh" + +usage() { + cat << EOF +Usage: $(basename "$0") BUILD_TYPE APP_ABI +Options: +BUILD_TYPE supported build types: + static + static-debug + shared + shared-debug +APP_ABI supported application binary interfaces: + armeabi-v7a + arm64-v8a + x86 + x86_64 +Environment variables: +WORKSPACE directory where the build is done. +ANDROID_NDK_DIR directory where the android ndk tools are. +EOF +} + +################################################################################ +echo "Building libwebp for Android in ${WORKSPACE}" + +if [[ ! -d "${WORKSPACE}" ]]; then + log_err "${WORKSPACE} directory does not exist." + exit 1 +fi + +readonly BUILD_TYPE=${1:?"BUILD_TYPE is not defined.$( + echo + usage +)"} +readonly APP_ABI=${2:?"APP_ABI not defined.$( + echo + usage +)"} +readonly ANDROID_NDK_DIR=${ANDROID_NDK_DIR:?"ANDROID_NDK_DIR is not defined.$( + echo + usage +)"} +readonly BUILD_DIR="${WORKSPACE}/build-${BUILD_TYPE}" +readonly STANDALONE_ANDROID_DIR="${WORKSPACE}/android" + +if [[ ! -x "${ANDROID_NDK_DIR}/ndk-build" ]]; then + log_err "unable to find ndk-build in ANDROID_NDK_DIR: ${ANDROID_NDK_DIR}." + exit 1 +fi + +CFLAGS= +LDFLAGS= +opts=() +case "${BUILD_TYPE}" in + *debug) + readonly APP_OPTIM="debug" + CFLAGS="-O0 -g" + opts+=("--enable-asserts") + ;; + static* | shared*) + readonly APP_OPTIM="release" + CFLAGS="-O2 -g" + ;; + *) + usage + exit 1 + ;; +esac + +case "${BUILD_TYPE}" in + shared*) readonly SHARED="1" ;; + *) + readonly SHARED="0" + CFLAGS="${CFLAGS} -fPIE" + LDFLAGS="${LDFLAGS} -Wl,-pie" + opts+=("--disable-shared") + ;; +esac + +# Create a fresh build directory +make_build_dir "${BUILD_DIR}" +cd "${BUILD_DIR}" +ln -s "${LIBWEBP_ROOT}" jni + +"${ANDROID_NDK_DIR}/ndk-build" -j2 \ + APP_ABI="${APP_ABI}" \ + APP_OPTIM="${APP_OPTIM}" \ + ENABLE_SHARED="${SHARED}" + +cd "${LIBWEBP_ROOT}" +./autogen.sh + +case "${APP_ABI}" in + armeabi*) arch="arm" ;; + arm64*) arch="arm64" ;; + *) arch="${APP_ABI}" ;; +esac +# TODO(b/185520507): remove this and use the binaries from +# toolchains/llvm/prebuilt/ directly. +rm -rf "${STANDALONE_ANDROID_DIR}" +"${ANDROID_NDK_DIR}/build/tools/make_standalone_toolchain.py" \ + --api 24 --arch "${arch}" --stl gnustl --install-dir \ + "${STANDALONE_ANDROID_DIR}" +export PATH="${STANDALONE_ANDROID_DIR}/bin:${PATH}" + +rm -rf "${BUILD_DIR}" +make_build_dir "${BUILD_DIR}" +cd "${BUILD_DIR}" + +case "${arch}" in + arm) + host="arm-linux-androideabi" + case "${APP_ABI}" in + armeabi) ;; + armeabi-v7a) + CFLAGS="${CFLAGS} -march=armv7-a -mfpu=neon -mfloat-abi=softfp" + ;; + *) ;; # No configuration needed + esac + ;; + arm64) + host="aarch64-linux-android" + ;; + x86) + host="i686-linux-android" + ;; + x86_64) + host="x86_64-linux-android" + ;; + *) ;; # Skip configuration +esac + +setup_ccache +CC="clang" + +"${LIBWEBP_ROOT}/configure" --host "${host}" --build \ + "$("${LIBWEBP_ROOT}/config.guess")" CC="${CC}" CFLAGS="${CFLAGS}" \ + LDFLAGS="${LDFLAGS}" "${opts[@]}" +make -j + +if [[ "${GERRIT_REFSPEC:-}" = "refs/heads/portable-intrinsics" ]] \ + || [[ "${GERRIT_BRANCH:-}" = "portable-intrinsics" ]]; then + cd "${WORKSPACE}" + rm -rf build && mkdir build + cd build + standalone="${WORKSPACE}/android" + cmake ../libwebp \ + -DWEBP_BUILD_DWEBP=1 \ + -DCMAKE_C_COMPILER="${standalone}/bin/clang" \ + -DCMAKE_PREFIX_PATH="${standalone}/sysroot/usr/lib" \ + -DCMAKE_C_FLAGS=-fPIE \ + -DCMAKE_EXE_LINKER_FLAGS=-Wl,-pie \ + -DCMAKE_BUILD_TYPE=Release \ + -DWEBP_ENABLE_WASM=1 + make -j2 + + cd "${WORKSPACE}" + make_build_dir "${BUILD_DIR}" + cd "${BUILD_DIR}" + case "${APP_ABI}" in + armeabi-v7a | arm64*) + cmake "${LIBWEBP_ROOT}" \ + -DWEBP_BUILD_DWEBP=1 \ + -DCMAKE_C_COMPILER="${standalone}/bin/clang" \ + -DCMAKE_PREFIX_PATH="${standalone}/sysroot/usr/lib" \ + -DCMAKE_C_FLAGS='-fPIE -DENABLE_NEON_BUILTIN_MULHI_INT16X8' \ + -DCMAKE_EXE_LINKER_FLAGS=-Wl,-pie \ + -DCMAKE_BUILD_TYPE=Release \ + -DWEBP_ENABLE_WASM=1 + make -j2 + ;; + x86*) + cmake "${LIBWEBP_ROOT}" \ + -DWEBP_BUILD_DWEBP=1 \ + -DCMAKE_C_COMPILER="${standalone}/bin/clang" \ + -DCMAKE_PREFIX_PATH="${standalone}/sysroot/usr/lib" \ + -DCMAKE_C_FLAGS='-fPIE -DENABLE_X86_BUILTIN_MULHI_INT16X8' \ + -DCMAKE_EXE_LINKER_FLAGS=-Wl,-pie \ + -DCMAKE_BUILD_TYPE=Release \ + -DWEBP_ENABLE_WASM=1 + make -j2 + ;; + *) + log_err "APP_ABI not supported." + exit 1 + ;; + esac +fi diff --git a/internal/libwebp-1.2.1/infra/compile_js.sh b/internal/libwebp-1.2.1/infra/compile_js.sh new file mode 100755 index 0000000..6a13fe6 --- /dev/null +++ b/internal/libwebp-1.2.1/infra/compile_js.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# Copyright (c) 2021, Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of Google nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set -ex + +readonly WORKSPACE="${WORKSPACE:-"$(mktemp -d -t webp.js.XXX)"}" +readonly BUILD_DIR="${WORKSPACE}/webp_js/" +readonly LIBWEBP_ROOT="$(realpath "$(dirname "$0")/..")" + +# shellcheck source=infra/common.sh +source "${LIBWEBP_ROOT}/infra/common.sh" + +usage() { + cat << EOF +Usage: $(basename "$0") +Environment variables: +WORKSPACE directory where the build is done +EMSDK_DIR directory where emsdk is installed +EOF +} + +[[ -d "${EMSDK_DIR:?Not defined}" ]] \ + || (log_err "${EMSDK_DIR} is not a valid directory." && exit 1) + +# shellcheck source=/opt/emsdk/emsdk_env.sh +source "${EMSDK_DIR}/emsdk_env.sh" + +readonly EMSCRIPTEN=${EMSCRIPTEN:-"${EMSDK}/upstream/emscripten"} +readonly \ + EMSCRIPTEN_CMAKE_FILE="${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake" +make_build_dir "${BUILD_DIR}" + +pushd "${BUILD_DIR}" +opts=("-GUnix Makefiles" "-DWEBP_BUILD_WEBP_JS=ON") +if [[ -z "$(command -v emcmake)" ]]; then + opts+=("-DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_CMAKE_FILE}") + cmake \ + "${opts[@]}" \ + "${LIBWEBP_ROOT}" + make -j +else + emcmake cmake \ + "${opts[@]}" \ + "${LIBWEBP_ROOT}" + emmake make -j +fi +popd diff --git a/internal/libwebp-1.2.1/infra/run_static_analysis.sh b/internal/libwebp-1.2.1/infra/run_static_analysis.sh new file mode 100755 index 0000000..cd5dd5d --- /dev/null +++ b/internal/libwebp-1.2.1/infra/run_static_analysis.sh @@ -0,0 +1,98 @@ +#!/bin/bash +# Copyright (c) 2021, Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of Google nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set -xe + +LIBWEBP_ROOT="$(realpath "$(dirname "$0")/..")" +readonly LIBWEBP_ROOT +readonly WORKSPACE=${WORKSPACE:-"$(mktemp -d -t webp.scanbuild.XXX)"} + +# shellcheck source=infra/common.sh +source "${LIBWEBP_ROOT}/infra/common.sh" + +usage() { + cat << EOF +Usage: $(basename "$0") MODE +Options: +MODE supported scan modes: (shallow|deep) +Environment variables: +WORKSPACE directory where the build is done. +EOF +} + +####################################### +# Wrap clang-tools scan-build. +# Globals: +# OUTPUT_DIR target directory where scan-build report is generated. +# MODE scan-build mode +# Arguments: +# $* scan-build additional args. +# Returns: +# scan-build retcode +####################################### +scan_build() { + scan-build -o "${OUTPUT_DIR}" --use-analyzer="$(command -v clang)" \ + -analyzer-config mode="${MODE}" "$*" +} + +MODE=${1:?"MODE is not specified.$( + echo + usage +)"} + +readonly OUTPUT_DIR="${WORKSPACE}/output-${MODE}" +readonly BUILD_DIR="${WORKSPACE}/build" + +make_build_dir "${OUTPUT_DIR}" +make_build_dir "${BUILD_DIR}" + +cd "${LIBWEBP_ROOT}" +./autogen.sh + +cd "${BUILD_DIR}" +grep -m 1 -q 'enable-asserts' "${LIBWEBP_ROOT}/configure.ac" \ + && args='--enable-asserts' +scan_build "${LIBWEBP_ROOT}/configure" --enable-everything "${args}" +scan_build make -j4 + +index="$(find "${OUTPUT_DIR}" -name index.html)" +if [[ -f "${index}" ]]; then + mv "$(dirname "${index}")/"* . +else + # make a empty report to wipe out any old bug reports. + cat << EOT > index.html + + +No bugs reported. + + +EOT +fi diff --git a/internal/libwebp-1.2.1/iosbuild.sh b/internal/libwebp-1.2.1/iosbuild.sh new file mode 100755 index 0000000..7c5e691 --- /dev/null +++ b/internal/libwebp-1.2.1/iosbuild.sh @@ -0,0 +1,170 @@ +#!/bin/bash +# +# This script generates 'WebP.framework' and 'WebPDecoder.framework', +# 'WebPDemux.framework' and 'WebPMux.framework'. +# An iOS app can decode WebP images by including 'WebPDecoder.framework' and +# both encode and decode WebP images by including 'WebP.framework'. +# +# Run ./iosbuild.sh to generate the frameworks under the current directory +# (the previous build will be erased if it exists). +# +# This script is inspired by the build script written by Carson McDonald. +# (http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/). + +set -e + +# Set this variable based on the desired minimum deployment target. +readonly IOS_MIN_VERSION=6.0 + +# Extract the latest SDK version from the final field of the form: iphoneosX.Y +readonly SDK=$(xcodebuild -showsdks \ + | grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}' +) +# Extract Xcode version. +readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2) +if [[ -z "${XCODE}" ]]; then + echo "Xcode not available" + exit 1 +fi + +readonly OLDPATH=${PATH} + +# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support. +# Note that iPhoneOS-V6 support is not available with the iOS6 SDK. +PLATFORMS="iPhoneSimulator iPhoneSimulator64" +PLATFORMS+=" iPhoneOS-V7 iPhoneOS-V7s iPhoneOS-V7-arm64" +readonly PLATFORMS +readonly SRCDIR=$(dirname $0) +readonly TOPDIR=$(pwd) +readonly BUILDDIR="${TOPDIR}/iosbuild" +readonly TARGETDIR="${TOPDIR}/WebP.framework" +readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.framework" +readonly MUXTARGETDIR="${TOPDIR}/WebPMux.framework" +readonly DEMUXTARGETDIR="${TOPDIR}/WebPDemux.framework" +readonly DEVELOPER=$(xcode-select --print-path) +readonly PLATFORMSROOT="${DEVELOPER}/Platforms" +readonly LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo) +LIBLIST='' +DECLIBLIST='' +MUXLIBLIST='' +DEMUXLIBLIST='' + +if [[ -z "${SDK}" ]]; then + echo "iOS SDK not available" + exit 1 +elif [[ ${SDK%%.*} -gt 8 ]]; then + EXTRA_CFLAGS="-fembed-bitcode" +elif [[ ${SDK%%.*} -le 6 ]]; then + echo "You need iOS SDK version 6.0 or above" + exit 1 +fi + +echo "Xcode Version: ${XCODE}" +echo "iOS SDK Version: ${SDK}" + +if [[ -e "${BUILDDIR}" || -e "${TARGETDIR}" || -e "${DECTARGETDIR}" \ + || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" ]]; then + cat << EOF +WARNING: The following directories will be deleted: +WARNING: ${BUILDDIR} +WARNING: ${TARGETDIR} +WARNING: ${DECTARGETDIR} +WARNING: ${MUXTARGETDIR} +WARNING: ${DEMUXTARGETDIR} +WARNING: The build will continue in 5 seconds... +EOF + sleep 5 +fi +rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR} \ + ${MUXTARGETDIR} ${DEMUXTARGETDIR} +mkdir -p ${BUILDDIR} ${TARGETDIR}/Headers/ ${DECTARGETDIR}/Headers/ \ + ${MUXTARGETDIR}/Headers/ ${DEMUXTARGETDIR}/Headers/ + +if [[ ! -e ${SRCDIR}/configure ]]; then + if ! (cd ${SRCDIR} && sh autogen.sh); then + cat << EOF +Error creating configure script! +This script requires the autoconf/automake and libtool to build. MacPorts can +be used to obtain these: +http://www.macports.org/install.php +EOF + exit 1 + fi +fi + +for PLATFORM in ${PLATFORMS}; do + ARCH2="" + if [[ "${PLATFORM}" == "iPhoneOS-V7-arm64" ]]; then + PLATFORM="iPhoneOS" + ARCH="aarch64" + ARCH2="arm64" + elif [[ "${PLATFORM}" == "iPhoneOS-V7s" ]]; then + PLATFORM="iPhoneOS" + ARCH="armv7s" + elif [[ "${PLATFORM}" == "iPhoneOS-V7" ]]; then + PLATFORM="iPhoneOS" + ARCH="armv7" + elif [[ "${PLATFORM}" == "iPhoneOS-V6" ]]; then + PLATFORM="iPhoneOS" + ARCH="armv6" + elif [[ "${PLATFORM}" == "iPhoneSimulator64" ]]; then + PLATFORM="iPhoneSimulator" + ARCH="x86_64" + else + ARCH="i386" + fi + + ROOTDIR="${BUILDDIR}/${PLATFORM}-${SDK}-${ARCH}" + mkdir -p "${ROOTDIR}" + + DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain" + SDKROOT="${PLATFORMSROOT}/" + SDKROOT+="${PLATFORM}.platform/Developer/SDKs/${PLATFORM}${SDK}.sdk/" + CFLAGS="-arch ${ARCH2:-${ARCH}} -pipe -isysroot ${SDKROOT} -O3 -DNDEBUG" + CFLAGS+=" -miphoneos-version-min=${IOS_MIN_VERSION} ${EXTRA_CFLAGS}" + + set -x + export PATH="${DEVROOT}/usr/bin:${OLDPATH}" + ${SRCDIR}/configure --host=${ARCH}-apple-darwin --prefix=${ROOTDIR} \ + --build=$(${SRCDIR}/config.guess) \ + --disable-shared --enable-static \ + --enable-libwebpdecoder --enable-swap-16bit-csp \ + --enable-libwebpmux \ + CFLAGS="${CFLAGS}" + set +x + + # run make only in the src/ directory to create libwebp.a/libwebpdecoder.a + cd src/ + make V=0 + make install + + LIBLIST+=" ${ROOTDIR}/lib/libwebp.a" + DECLIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a" + MUXLIBLIST+=" ${ROOTDIR}/lib/libwebpmux.a" + DEMUXLIBLIST+=" ${ROOTDIR}/lib/libwebpdemux.a" + + make clean + cd .. + + export PATH=${OLDPATH} +done + +echo "LIBLIST = ${LIBLIST}" +cp -a ${SRCDIR}/src/webp/{decode,encode,types}.h ${TARGETDIR}/Headers/ +${LIPO} -create ${LIBLIST} -output ${TARGETDIR}/WebP + +echo "DECLIBLIST = ${DECLIBLIST}" +cp -a ${SRCDIR}/src/webp/{decode,types}.h ${DECTARGETDIR}/Headers/ +${LIPO} -create ${DECLIBLIST} -output ${DECTARGETDIR}/WebPDecoder + +echo "MUXLIBLIST = ${MUXLIBLIST}" +cp -a ${SRCDIR}/src/webp/{types,mux,mux_types}.h \ + ${MUXTARGETDIR}/Headers/ +${LIPO} -create ${MUXLIBLIST} -output ${MUXTARGETDIR}/WebPMux + +echo "DEMUXLIBLIST = ${DEMUXLIBLIST}" +cp -a ${SRCDIR}/src/webp/{decode,types,mux_types,demux}.h \ + ${DEMUXTARGETDIR}/Headers/ +${LIPO} -create ${DEMUXLIBLIST} -output ${DEMUXTARGETDIR}/WebPDemux + +echo "SUCCESS" diff --git a/internal/libwebp-1.2.1/m4/.gitignore b/internal/libwebp-1.2.1/m4/.gitignore new file mode 100644 index 0000000..64d9bbc --- /dev/null +++ b/internal/libwebp-1.2.1/m4/.gitignore @@ -0,0 +1,2 @@ +/libtool.m4 +/lt*.m4 diff --git a/internal/libwebp-1.2.1/m4/ax_pthread.m4 b/internal/libwebp-1.2.1/m4/ax_pthread.m4 new file mode 100644 index 0000000..d383ad5 --- /dev/null +++ b/internal/libwebp-1.2.1/m4/ax_pthread.m4 @@ -0,0 +1,332 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. e.g. you should link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threads programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name +# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 21 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case ${host_os} in + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + darwin*) + ax_pthread_flags="-pthread $ax_pthread_flags" + ;; +esac + +# Clang doesn't consider unrecognized options an error unless we specify +# -Werror. We throw in some extra Clang-specific options to ensure that +# this doesn't happen for GCC, which also accepts -Werror. + +AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags]) +save_CFLAGS="$CFLAGS" +ax_pthread_extra_flags="-Werror" +CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])], + [AC_MSG_RESULT([yes])], + [ax_pthread_extra_flags= + AC_MSG_RESULT([no])]) +CFLAGS="$save_CFLAGS" + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $attr; return attr /* ; */])], + [attr_name=$attr; break], + []) + done + AC_MSG_RESULT([$attr_name]) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case ${host_os} in + aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; + osf* | hpux*) flag="-D_REENTRANT";; + solaris*) + if test "$GCC" = "yes"; then + flag="-D_REENTRANT" + else + # TODO: What about Clang on Solaris? + flag="-mt -D_REENTRANT" + fi + ;; + esac + AC_MSG_RESULT([$flag]) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != xyes; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/internal/libwebp-1.2.1/makefile.unix b/internal/libwebp-1.2.1/makefile.unix new file mode 100644 index 0000000..aa7f3a2 --- /dev/null +++ b/internal/libwebp-1.2.1/makefile.unix @@ -0,0 +1,501 @@ +# This makefile is a simpler alternative to the autoconf-based build +# system, for simple local building of the libraries and tools. +# It will not install the libraries system-wide, but just create the 'cwebp' +# and 'dwebp' tools in the examples/ directory, along with the static +# libraries 'src/libwebp.a', 'src/libwebpdecoder.a', 'src/mux/libwebpmux.a', +# 'src/demux/libwebpdemux.a' and 'extras/libwebpextras.a'. +# +# To build the library and examples, use: +# make -f makefile.unix +# from this top directory. + +#### Customizable part #### + +# These flags assume you have libpng, libjpeg, libtiff and libgif installed. If +# not, either follow the install instructions below or just comment out the next +# four lines. +EXTRA_FLAGS= -DWEBP_HAVE_PNG -DWEBP_HAVE_JPEG -DWEBP_HAVE_TIFF +DWEBP_LIBS= -lpng -lz +CWEBP_LIBS= $(DWEBP_LIBS) -ljpeg -ltiff +GIF_LIBS = -lgif + +ifeq ($(strip $(shell uname)), Darwin) + # Work around a problem linking tables marked as common symbols, + # cf., src/enc/yuv.[hc] + # Failure observed with: gcc 4.2.1 and 4.0.1. + EXTRA_FLAGS += -fno-common + EXTRA_FLAGS += -DHAVE_GLUT_GLUT_H + EXTRA_FLAGS += -Wno-deprecated-declarations + EXTRA_FLAGS += -I/opt/local/include + EXTRA_LIBS += -L/opt/local/lib + GL_LIBS = -framework GLUT -framework OpenGL +else + EXTRA_FLAGS += -I/usr/local/include + EXTRA_LIBS += -L/usr/local/lib + GL_LIBS = -lglut -lGL +endif + +# SDL flags: use sdl-config if it exists +SDL_CONFIG = $(shell sdl-config --version 2> /dev/null) +ifneq ($(SDL_CONFIG),) + SDL_LIBS = $(shell sdl-config --libs) + SDL_FLAGS = $(shell sdl-config --cflags) +else + # use best-guess + SDL_LIBS = -lSDL + SDL_FLAGS = +endif + +# To install libraries on Mac OS X: +# 1. Install MacPorts (http://www.macports.org/install.php) +# 2. Run "sudo port install jpeg" +# 3. Run "sudo port install libpng" +# 4. Run "sudo port install tiff" +# 5. Run "sudo port install giflib" + +# To install libraries on Linux: +# 1. Run "sudo apt-get install libjpeg62-dev" +# 2. Run "sudo apt-get install libpng12-dev" +# 3. Run "sudo apt-get install libtiff4-dev" +# 4. Run "sudo apt-get install libgif-dev" + +# Uncomment for build for 32bit platform +# Alternatively, you can just use the command +# 'make -f makefile.unix EXTRA_FLAGS=-m32' to that effect. +# EXTRA_FLAGS += -m32 + +# Extra flags to enable byte swap for 16 bit colorspaces. +# EXTRA_FLAGS += -DWEBP_SWAP_16BIT_CSP=1 + +# Extra flags to enable multi-threading +EXTRA_FLAGS += -DWEBP_USE_THREAD +EXTRA_LIBS += -lpthread + +# Control symbol visibility. Comment out if your compiler doesn't support it. +EXTRA_FLAGS += -fvisibility=hidden + +# Extra flags to emulate C89 strictness with the full ANSI +EXTRA_FLAGS += -Wextra -Wold-style-definition +EXTRA_FLAGS += -Wmissing-prototypes +EXTRA_FLAGS += -Wmissing-declarations +EXTRA_FLAGS += -Wdeclaration-after-statement +EXTRA_FLAGS += -Wshadow +EXTRA_FLAGS += -Wformat-security -Wformat-nonliteral +# EXTRA_FLAGS += -Wvla + +# SSE4.1-specific flags: +ifeq ($(HAVE_SSE41), 1) +EXTRA_FLAGS += -DWEBP_HAVE_SSE41 +src/dsp/%_sse41.o: EXTRA_FLAGS += -msse4.1 +endif + +# NEON-specific flags: +# EXTRA_FLAGS += -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8 +# -> seems to make the overall lib slower: -fno-split-wide-types + +# MIPS (MSA) 32-bit build specific flags for mips32r5 (p5600): +# EXTRA_FLAGS += -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 +# EXTRA_FLAGS += -msched-weight -mload-store-pairs + +# MIPS (MSA) 64-bit build specific flags for mips64r6 (i6400): +# EXTRA_FLAGS += -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 +# EXTRA_FLAGS += -msched-weight -mload-store-pairs + +#### Nothing should normally be changed below this line #### + +AR = ar +ARFLAGS = r +CPPFLAGS = -I. -Isrc/ -Wall +ifeq ($(DEBUG), 1) + CFLAGS = -g +else + CFLAGS = -O3 -DNDEBUG +endif +CFLAGS += $(EXTRA_FLAGS) +CC = gcc +INSTALL = install +GROFF = /usr/bin/groff +COL = /usr/bin/col +LDFLAGS = $(EXTRA_LIBS) $(EXTRA_FLAGS) -lm + +ifdef BITTRACE +CFLAGS += -DBITTRACE=$(BITTRACE) +endif + +ANIM_UTIL_OBJS = \ + examples/anim_util.o \ + +DEC_OBJS = \ + src/dec/alpha_dec.o \ + src/dec/buffer_dec.o \ + src/dec/frame_dec.o \ + src/dec/idec_dec.o \ + src/dec/io_dec.o \ + src/dec/quant_dec.o \ + src/dec/tree_dec.o \ + src/dec/vp8_dec.o \ + src/dec/vp8l_dec.o \ + src/dec/webp_dec.o \ + +DEMUX_OBJS = \ + src/demux/anim_decode.o \ + src/demux/demux.o \ + +DSP_DEC_OBJS = \ + src/dsp/alpha_processing.o \ + src/dsp/alpha_processing_mips_dsp_r2.o \ + src/dsp/alpha_processing_neon.o \ + src/dsp/alpha_processing_sse2.o \ + src/dsp/alpha_processing_sse41.o \ + src/dsp/cpu.o \ + src/dsp/dec.o \ + src/dsp/dec_clip_tables.o \ + src/dsp/dec_mips32.o \ + src/dsp/dec_mips_dsp_r2.o \ + src/dsp/dec_msa.o \ + src/dsp/dec_neon.o \ + src/dsp/dec_sse2.o \ + src/dsp/dec_sse41.o \ + src/dsp/filters.o \ + src/dsp/filters_mips_dsp_r2.o \ + src/dsp/filters_msa.o \ + src/dsp/filters_neon.o \ + src/dsp/filters_sse2.o \ + src/dsp/lossless.o \ + src/dsp/lossless_mips_dsp_r2.o \ + src/dsp/lossless_msa.o \ + src/dsp/lossless_neon.o \ + src/dsp/lossless_sse2.o \ + src/dsp/lossless_sse41.o \ + src/dsp/rescaler.o \ + src/dsp/rescaler_mips32.o \ + src/dsp/rescaler_mips_dsp_r2.o \ + src/dsp/rescaler_msa.o \ + src/dsp/rescaler_neon.o \ + src/dsp/rescaler_sse2.o \ + src/dsp/upsampling.o \ + src/dsp/upsampling_mips_dsp_r2.o \ + src/dsp/upsampling_msa.o \ + src/dsp/upsampling_neon.o \ + src/dsp/upsampling_sse2.o \ + src/dsp/upsampling_sse41.o \ + src/dsp/yuv.o \ + src/dsp/yuv_mips32.o \ + src/dsp/yuv_mips_dsp_r2.o \ + src/dsp/yuv_neon.o \ + src/dsp/yuv_sse2.o \ + src/dsp/yuv_sse41.o \ + +DSP_ENC_OBJS = \ + src/dsp/cost.o \ + src/dsp/cost_mips32.o \ + src/dsp/cost_mips_dsp_r2.o \ + src/dsp/cost_neon.o \ + src/dsp/cost_sse2.o \ + src/dsp/enc.o \ + src/dsp/enc_mips32.o \ + src/dsp/enc_mips_dsp_r2.o \ + src/dsp/enc_msa.o \ + src/dsp/enc_neon.o \ + src/dsp/enc_sse2.o \ + src/dsp/enc_sse41.o \ + src/dsp/lossless_enc.o \ + src/dsp/lossless_enc_mips32.o \ + src/dsp/lossless_enc_mips_dsp_r2.o \ + src/dsp/lossless_enc_msa.o \ + src/dsp/lossless_enc_neon.o \ + src/dsp/lossless_enc_sse2.o \ + src/dsp/lossless_enc_sse41.o \ + src/dsp/ssim.o \ + src/dsp/ssim_sse2.o \ + +ENC_OBJS = \ + src/enc/alpha_enc.o \ + src/enc/analysis_enc.o \ + src/enc/backward_references_cost_enc.o \ + src/enc/backward_references_enc.o \ + src/enc/config_enc.o \ + src/enc/cost_enc.o \ + src/enc/filter_enc.o \ + src/enc/frame_enc.o \ + src/enc/histogram_enc.o \ + src/enc/iterator_enc.o \ + src/enc/near_lossless_enc.o \ + src/enc/picture_enc.o \ + src/enc/picture_csp_enc.o \ + src/enc/picture_psnr_enc.o \ + src/enc/picture_rescale_enc.o \ + src/enc/picture_tools_enc.o \ + src/enc/predictor_enc.o \ + src/enc/quant_enc.o \ + src/enc/syntax_enc.o \ + src/enc/token_enc.o \ + src/enc/tree_enc.o \ + src/enc/vp8l_enc.o \ + src/enc/webp_enc.o \ + +EX_FORMAT_DEC_OBJS = \ + imageio/image_dec.o \ + imageio/jpegdec.o \ + imageio/metadata.o \ + imageio/pngdec.o \ + imageio/pnmdec.o \ + imageio/tiffdec.o \ + imageio/webpdec.o \ + +EX_FORMAT_ENC_OBJS = \ + imageio/image_enc.o \ + +EX_UTIL_OBJS = \ + examples/example_util.o \ + +GIFDEC_OBJS = \ + examples/gifdec.o \ + +IMAGE_UTIL_OBJS = \ + imageio/imageio_util.o \ + +MUX_OBJS = \ + src/mux/anim_encode.o \ + src/mux/muxedit.o \ + src/mux/muxinternal.o \ + src/mux/muxread.o \ + +UTILS_DEC_OBJS = \ + src/utils/bit_reader_utils.o \ + src/utils/color_cache_utils.o \ + src/utils/filters_utils.o \ + src/utils/huffman_utils.o \ + src/utils/quant_levels_dec_utils.o \ + src/utils/random_utils.o \ + src/utils/rescaler_utils.o \ + src/utils/thread_utils.o \ + src/utils/utils.o \ + +UTILS_ENC_OBJS = \ + src/utils/bit_writer_utils.o \ + src/utils/huffman_encode_utils.o \ + src/utils/quant_levels_utils.o \ + +EXTRA_OBJS = \ + extras/extras.o \ + extras/quality_estimate.o \ + +LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS) +LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \ + $(UTILS_ENC_OBJS) +LIBWEBPMUX_OBJS = $(MUX_OBJS) +LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) +LIBWEBPEXTRA_OBJS = $(EXTRA_OBJS) + +HDRS_INSTALLED = \ + src/webp/decode.h \ + src/webp/demux.h \ + src/webp/encode.h \ + src/webp/mux.h \ + src/webp/mux_types.h \ + src/webp/types.h \ + +HDRS = \ + src/dec/alphai_dec.h \ + src/dec/common_dec.h \ + src/dec/vp8_dec.h \ + src/dec/vp8i_dec.h \ + src/dec/vp8li_dec.h \ + src/dec/webpi_dec.h \ + src/dsp/common_sse2.h \ + src/dsp/dsp.h \ + src/dsp/lossless.h \ + src/dsp/lossless_common.h \ + src/dsp/mips_macro.h \ + src/dsp/msa_macro.h \ + src/dsp/neon.h \ + src/dsp/yuv.h \ + src/enc/backward_references_enc.h \ + src/enc/cost_enc.h \ + src/enc/histogram_enc.h \ + src/enc/vp8i_enc.h \ + src/enc/vp8li_enc.h \ + src/mux/animi.h \ + src/mux/muxi.h \ + src/utils/bit_reader_utils.h \ + src/utils/bit_reader_inl_utils.h \ + src/utils/bit_writer_utils.h \ + src/utils/color_cache_utils.h \ + src/utils/endian_inl_utils.h \ + src/utils/filters_utils.h \ + src/utils/huffman_utils.h \ + src/utils/huffman_encode_utils.h \ + src/utils/quant_levels_utils.h \ + src/utils/quant_levels_dec_utils.h \ + src/utils/random_utils.h \ + src/utils/rescaler_utils.h \ + src/utils/thread_utils.h \ + src/utils/utils.h \ + src/webp/format_constants.h \ + $(HDRS_INSTALLED) \ + +OUT_LIBS = examples/libexample_util.a +OUT_LIBS += imageio/libimageio_util.a +OUT_LIBS += imageio/libimagedec.a +OUT_LIBS += imageio/libimageenc.a +OUT_LIBS += src/libwebpdecoder.a +OUT_LIBS += src/libwebp.a +EXTRA_LIB = extras/libwebpextras.a +OUT_EXAMPLES = examples/cwebp examples/dwebp +EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux \ + examples/anim_diff examples/anim_dump \ + examples/img2webp examples/webpinfo +OTHER_EXAMPLES = extras/get_disto extras/webp_quality extras/vwebp_sdl + +OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES) +ifeq ($(MAKECMDGOALS),clean) + OUTPUT += $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES) + OUTPUT += src/demux/libwebpdemux.a src/mux/libwebpmux.a $(EXTRA_LIB) + OUTPUT += examples/libgifdec.a examples/libanim_util.a +endif + +ex: $(OUT_EXAMPLES) +all: ex $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES) +extras: $(EXTRA_LIB) + +$(EX_FORMAT_DEC_OBJS): %.o: %.h + +# special dependencies: +# tree_dec.c/vp8_dec.c/bit_reader_utils.c <-> +# bit_reader_inl_utils.h, endian_inl_utils.h +# bit_writer_utils.c <-> endian_inl_utils.h +src/dec/tree_dec.o: src/utils/bit_reader_inl_utils.h +src/dec/tree_dec.o: src/utils/endian_inl_utils.h +src/dec/vp8_dec.o: src/utils/bit_reader_inl_utils.h src/utils/endian_inl_utils.h +src/utils/bit_reader_utils.o: src/utils/bit_reader_inl_utils.h +src/utils/bit_reader_utils.o: src/utils/endian_inl_utils.h +src/utils/bit_writer_utils.o: src/utils/endian_inl_utils.h + +%.o: %.c $(HDRS) + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +examples/libanim_util.a: $(ANIM_UTIL_OBJS) +examples/libexample_util.a: $(EX_UTIL_OBJS) +examples/libgifdec.a: $(GIFDEC_OBJS) +extras/libwebpextras.a: $(LIBWEBPEXTRA_OBJS) +imageio/libimagedec.a: $(EX_FORMAT_DEC_OBJS) +imageio/libimageenc.a: $(EX_FORMAT_ENC_OBJS) +imageio/libimageio_util.a: $(IMAGE_UTIL_OBJS) +src/libwebpdecoder.a: $(LIBWEBPDECODER_OBJS) +src/libwebp.a: $(LIBWEBP_OBJS) +src/mux/libwebpmux.a: $(LIBWEBPMUX_OBJS) +src/demux/libwebpdemux.a: $(LIBWEBPDEMUX_OBJS) + +%.a: + $(AR) $(ARFLAGS) $@ $^ + +examples/anim_diff: examples/anim_diff.o $(ANIM_UTIL_OBJS) $(GIFDEC_OBJS) +examples/anim_dump: examples/anim_dump.o $(ANIM_UTIL_OBJS) $(GIFDEC_OBJS) +examples/cwebp: examples/cwebp.o +examples/dwebp: examples/dwebp.o +examples/gif2webp: examples/gif2webp.o $(GIFDEC_OBJS) +examples/vwebp: examples/vwebp.o +examples/webpmux: examples/webpmux.o +examples/img2webp: examples/img2webp.o +examples/webpinfo: examples/webpinfo.o + +examples/anim_diff: examples/libanim_util.a examples/libgifdec.a +examples/anim_diff: src/demux/libwebpdemux.a examples/libexample_util.a +examples/anim_diff: imageio/libimageio_util.a src/libwebp.a +examples/anim_diff: override EXTRA_LIBS += $(GIF_LIBS) +examples/anim_diff: EXTRA_FLAGS += -DWEBP_HAVE_GIF +examples/anim_dump: examples/libanim_util.a examples/libgifdec.a +examples/anim_dump: src/demux/libwebpdemux.a +examples/anim_dump: examples/libexample_util.a +examples/anim_dump: imageio/libimageio_util.a +examples/anim_dump: imageio/libimageenc.a +examples/anim_dump: src/libwebp.a +examples/anim_dump: override EXTRA_LIBS += $(GIF_LIBS) $(DWEBP_LIBS) +examples/cwebp: examples/libexample_util.a +examples/cwebp: imageio/libimagedec.a +examples/cwebp: src/demux/libwebpdemux.a +examples/cwebp: imageio/libimageio_util.a +examples/cwebp: src/libwebp.a +examples/cwebp: override EXTRA_LIBS += $(CWEBP_LIBS) +examples/dwebp: examples/libexample_util.a +examples/dwebp: imageio/libimagedec.a +examples/dwebp: src/demux/libwebpdemux.a +examples/dwebp: imageio/libimageenc.a +examples/dwebp: imageio/libimageio_util.a +examples/dwebp: src/libwebp.a +examples/dwebp: override EXTRA_LIBS += $(DWEBP_LIBS) +examples/gif2webp: examples/libexample_util.a imageio/libimageio_util.a +examples/gif2webp: examples/libgifdec.a src/mux/libwebpmux.a src/libwebp.a +examples/gif2webp: override EXTRA_LIBS += $(GIF_LIBS) +examples/gif2webp: EXTRA_FLAGS += -DWEBP_HAVE_GIF +examples/vwebp: examples/libexample_util.a src/demux/libwebpdemux.a +examples/vwebp: imageio/libimageio_util.a src/libwebp.a +examples/vwebp: override EXTRA_LIBS += $(GL_LIBS) +examples/vwebp: EXTRA_FLAGS += -DWEBP_HAVE_GL +examples/webpmux: examples/libexample_util.a imageio/libimageio_util.a +examples/webpmux: src/mux/libwebpmux.a src/libwebpdecoder.a +examples/img2webp: examples/libexample_util.a imageio/libimageio_util.a +examples/img2webp: imageio/libimagedec.a +examples/img2webp: src/demux/libwebpdemux.a +examples/img2webp: src/mux/libwebpmux.a src/libwebp.a +examples/img2webp: override EXTRA_LIBS += $(CWEBP_LIBS) +examples/webpinfo: examples/libexample_util.a imageio/libimageio_util.a +examples/webpinfo: src/libwebpdecoder.a + +extras/get_disto: extras/get_disto.o +extras/get_disto: imageio/libimagedec.a +extras/get_disto: src/demux/libwebpdemux.a +extras/get_disto: imageio/libimageio_util.a +extras/get_disto: src/libwebp.a +extras/get_disto: override EXTRA_LIBS += $(CWEBP_LIBS) + +extras/webp_quality: extras/webp_quality.o +extras/webp_quality: imageio/libimageio_util.a +extras/webp_quality: $(EXTRA_LIB) src/libwebp.a + +extras/vwebp_sdl: extras/vwebp_sdl.o +extras/vwebp_sdl: extras/webp_to_sdl.o +extras/vwebp_sdl: imageio/libimageio_util.a +extras/vwebp_sdl: src/libwebp.a +extras/vwebp_sdl: EXTRA_FLAGS += -DWEBP_HAVE_SDL $(SDL_FLAGS) +extras/vwebp_sdl: override EXTRA_LIBS += $(SDL_LIBS) + +$(OUT_EXAMPLES) $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES): + $(CC) -o $@ $^ $(LDFLAGS) + +dist: DESTDIR := dist +dist: OUT_EXAMPLES += $(EXTRA_EXAMPLES) +dist: all + $(INSTALL) -m755 -d $(DESTDIR)/include/webp \ + $(DESTDIR)/bin $(DESTDIR)/doc $(DESTDIR)/lib + $(INSTALL) -m755 -s $(OUT_EXAMPLES) $(DESTDIR)/bin + $(INSTALL) -m644 $(HDRS_INSTALLED) $(DESTDIR)/include/webp + $(INSTALL) -m644 src/libwebp.a $(DESTDIR)/lib + $(INSTALL) -m644 src/demux/libwebpdemux.a $(DESTDIR)/lib + $(INSTALL) -m644 src/mux/libwebpmux.a $(DESTDIR)/lib + umask 022; \ + for m in man/[cdv]webp.1 man/gif2webp.1 man/webpmux.1 \ + man/img2webp.1 man/webpinfo.1; do \ + basenam=$$(basename $$m .1); \ + $(GROFF) -t -e -man -T ascii $$m \ + | $(COL) -bx >$(DESTDIR)/doc/$${basenam}.txt; \ + $(GROFF) -t -e -man -T html $$m \ + | $(COL) -bx >$(DESTDIR)/doc/$${basenam}.html; \ + done + +clean: + $(RM) $(OUTPUT) *~ \ + examples/*.o examples/*~ \ + extras/*.o extras/*~ \ + imageio/*.o imageio/*~ \ + src/dec/*.o src/dec/*~ \ + src/demux/*.o src/demux/*~ \ + src/dsp/*.o src/dsp/*~ \ + src/enc/*.o src/enc/*~ \ + src/mux/*.o src/mux/*~ \ + src/utils/*.o src/utils/*~ \ + src/webp/*~ man/*~ doc/*~ swig/*~ \ + +.PHONY: all clean dist ex +.SUFFIXES: diff --git a/internal/libwebp-1.2.1/man/Makefile.am b/internal/libwebp-1.2.1/man/Makefile.am new file mode 100644 index 0000000..57e2483 --- /dev/null +++ b/internal/libwebp-1.2.1/man/Makefile.am @@ -0,0 +1,17 @@ +man_MANS = cwebp.1 dwebp.1 +if BUILD_MUX + man_MANS += webpmux.1 +endif +if BUILD_GIF2WEBP + man_MANS += gif2webp.1 +endif +if BUILD_IMG2WEBP + man_MANS += img2webp.1 +endif +if BUILD_VWEBP + man_MANS += vwebp.1 +endif +if BUILD_WEBPINFO + man_MANS += webpinfo.1 +endif +EXTRA_DIST = $(man_MANS) diff --git a/internal/libwebp-1.2.1/man/cwebp.1 b/internal/libwebp-1.2.1/man/cwebp.1 new file mode 100644 index 0000000..89fc8ad --- /dev/null +++ b/internal/libwebp-1.2.1/man/cwebp.1 @@ -0,0 +1,326 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH CWEBP 1 "November 19, 2020" +.SH NAME +cwebp \- compress an image file to a WebP file +.SH SYNOPSIS +.B cwebp +.RI [ options ] " input_file \-o output_file.webp +.br +.SH DESCRIPTION +This manual page documents the +.B cwebp +command. +.PP +\fBcwebp\fP compresses an image using the WebP format. +Input format can be either PNG, JPEG, TIFF, WebP or raw Y'CbCr samples. +Note: Animated PNG and WebP files are not supported. +.SH OPTIONS +The basic options are: +.TP +.BI \-o " string +Specify the name of the output WebP file. If omitted, \fBcwebp\fP will +perform compression but only report statistics. +Using "\-" as output name will direct output to 'stdout'. +.TP +.BI \-\- " string +Explicitly specify the input file. This option is useful if the input +file starts with a '\-' for instance. This option must appear \fBlast\fP. +Any other options afterward will be ignored. +.TP +.B \-h, \-help +A short usage summary. +.TP +.B \-H, \-longhelp +A summary of all the possible options. +.TP +.B \-version +Print the version number (as major.minor.revision) and exit. +.TP +.B \-lossless +Encode the image without any loss. For images with fully transparent area, +the invisible pixel values (R/G/B or Y/U/V) will be preserved only if the +\-exact option is used. +.TP +.BI \-near_lossless " int +Specify the level of near\-lossless image preprocessing. This option adjusts +pixel values to help compressibility, but has minimal impact on the visual +quality. It triggers lossless compression mode automatically. The range is 0 +(maximum preprocessing) to 100 (no preprocessing, the default). The typical +value is around 60. Note that lossy with \fB\-q 100\fP can at times yield +better results. +.TP +.BI \-q " float +Specify the compression factor for RGB channels between 0 and 100. The default +is 75. +.br +In case of lossy compression (default), a small factor produces a smaller file +with lower quality. Best quality is achieved by using a value of 100. +.br +In case of lossless compression (specified by the \fB\-lossless\fP option), a +small factor enables faster compression speed, but produces a larger file. +Maximum compression is achieved by using a value of 100. +.TP +.BI \-z " int +Switch on \fBlossless\fP compression mode with the specified level between 0 +and 9, with level 0 being the fastest, 9 being the slowest. Fast mode +produces larger file size than slower ones. A good default is \fB\-z 6\fP. +This option is actually a shortcut for some predefined settings for quality +and method. If options \fB\-q\fP or \fB\-m\fP are subsequently used, they will +invalidate the effect of this option. +.TP +.BI \-alpha_q " int +Specify the compression factor for alpha compression between 0 and 100. +Lossless compression of alpha is achieved using a value of 100, while the lower +values result in a lossy compression. The default is 100. +.TP +.BI \-preset " string +Specify a set of pre\-defined parameters to suit a particular type of +source material. Possible values are: \fBdefault\fP, \fBphoto\fP, +\fBpicture\fP, \fBdrawing\fP, \fBicon\fP, \fBtext\fP. Since +\fB\-preset\fP overwrites the other parameters' values (except the +\fB\-q\fP one), this option should preferably appear first in the +order of the arguments. +.TP +.BI \-m " int +Specify the compression method to use. This parameter controls the +trade off between encoding speed and the compressed file size and quality. +Possible values range from 0 to 6. Default value is 4. +When higher values are used, the encoder will spend more time inspecting +additional encoding possibilities and decide on the quality gain. +Lower value can result in faster processing time at the expense of +larger file size and lower compression quality. +.TP +.BI \-resize " width height +Resize the source to a rectangle with size \fBwidth\fP x \fBheight\fP. +If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0, +the value will be calculated preserving the aspect\-ratio. +.TP +.BI \-crop " x_position y_position width height +Crop the source to a rectangle with top\-left corner at coordinates +(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP. +This cropping area must be fully contained within the source rectangle. +.TP +.B \-mt +Use multi\-threading for encoding, if possible. +.TP +.B \-low_memory +Reduce memory usage of lossy encoding by saving four times the compressed +size (typically). This will make the encoding slower and the output slightly +different in size and distortion. This flag is only effective for methods +3 and up, and is off by default. Note that leaving this flag off will have +some side effects on the bitstream: it forces certain bitstream features +like number of partitions (forced to 1). Note that a more detailed report +of bitstream size is printed by \fBcwebp\fP when using this option. + +.SS LOSSY OPTIONS +These options are only effective when doing lossy encoding (the default, with +or without alpha). + +.TP +.BI \-size " int +Specify a target size (in bytes) to try and reach for the compressed output. +The compressor will make several passes of partial encoding in order to get as +close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP +are used, \fB\-size\fP value will prevail. +.TP +.BI \-psnr " float +Specify a target PSNR (in dB) to try and reach for the compressed output. +The compressor will make several passes of partial encoding in order to get as +close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP +are used, \fB\-size\fP value will prevail. +.TP +.BI \-pass " int +Set a maximum number of passes to use during the dichotomy used by +options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10, default is 1. +If options \fB\-size\fP or \fB\-psnr\fP were used, but \fB\-pass\fP wasn't +specified, a default value of '6' passes will be used. +.TP +.BI \-qrange " int int +Specifies the permissible interval for the quality factor. This is particularly +useful when using multi-pass (\fB\-size\fP or \fB\-psnr\fP options). +Default is 0 100. +If the quality factor is outside this range, it will be clamped. +If the minimum value must be less or equal to the maximum one. +.TP +.B \-af +Turns auto\-filter on. This algorithm will spend additional time optimizing +the filtering strength to reach a well\-balanced quality. +.TP +.B \-jpeg_like +Change the internal parameter mapping to better match the expected size +of JPEG compression. This flag will generally produce an output file of +similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but +with less visual distortion. + +.TP +Advanced options: + +.TP +.BI \-f " int +Specify the strength of the deblocking filter, between 0 (no filtering) +and 100 (maximum filtering). A value of 0 will turn off any filtering. +Higher value will increase the strength of the filtering process applied +after decoding the picture. The higher the value the smoother the picture will +appear. Typical values are usually in the range of 20 to 50. +.TP +.BI \-sharpness " int +Specify the sharpness of the filtering (if used). +Range is 0 (sharpest) to 7 (least sharp). Default is 0. +.TP +.B \-strong +Use strong filtering (if filtering is being used thanks to the +\fB\-f\fP option). Strong filtering is on by default. +.TP +.B \-nostrong +Disable strong filtering (if filtering is being used thanks to the +\fB\-f\fP option) and use simple filtering instead. +.TP +.B \-sharp_yuv +Use more accurate and sharper RGB->YUV conversion if needed. Note that this +process is slower than the default 'fast' RGB->YUV conversion. +.TP +.BI \-sns " int +Specify the amplitude of the spatial noise shaping. Spatial noise shaping +(or \fBsns\fP for short) refers to a general collection of built\-in algorithms +used to decide which area of the picture should use relatively less bits, +and where else to better transfer these bits. The possible range goes from +0 (algorithm is off) to 100 (the maximal effect). The default value is 50. +.TP +.BI \-segments " int +Change the number of partitions to use during the segmentation of the +sns algorithm. Segments should be in range 1 to 4. Default value is 4. +This option has no effect for methods 3 and up, unless \fB\-low_memory\fP +is used. +.TP +.BI \-partition_limit " int +Degrade quality by limiting the number of bits used by some macroblocks. +Range is 0 (no degradation, the default) to 100 (full degradation). +Useful values are usually around 30\-70 for moderately large images. +In the VP8 format, the so\-called control partition has a limit of 512k and +is used to store the following information: whether the macroblock is skipped, +which segment it belongs to, whether it is coded as intra 4x4 or intra 16x16 +mode, and finally the prediction modes to use for each of the sub\-blocks. +For a very large image, 512k only leaves room to few bits per 16x16 macroblock. +The absolute minimum is 4 bits per macroblock. Skip, segment, and mode +information can use up almost all these 4 bits (although the case is unlikely), +which is problematic for very large images. The partition_limit factor controls +how frequently the most bit\-costly mode (intra 4x4) will be used. This is +useful in case the 512k limit is reached and the following message is displayed: +\fIError code: 6 (PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k)\fP. +If using \fB\-partition_limit\fP is not enough to meet the 512k constraint, one +should use less segments in order to save more header bits per macroblock. +See the \fB\-segments\fP option. + +.SS LOGGING OPTIONS +These options control the level of output: +.TP +.B \-v +Print extra information (encoding time in particular). +.TP +.B \-print_psnr +Compute and report average PSNR (Peak\-Signal\-To\-Noise ratio). +.TP +.B \-print_ssim +Compute and report average SSIM (structural similarity +metric, see http://en.wikipedia.org/wiki/SSIM for additional details). +.TP +.B \-print_lsim +Compute and report local similarity metric (sum of lowest error amongst the +collocated pixel neighbors). +.TP +.B \-progress +Report encoding progress in percent. +.TP +.B \-quiet +Do not print anything. +.TP +.B \-short +Only print brief information (output file size and PSNR) for testing purposes. +.TP +.BI \-map " int +Output additional ASCII\-map of encoding information. Possible map values +range from 1 to 6. This is only meant to help debugging. + +.SS ADDITIONAL OPTIONS +More advanced options are: +.TP +.BI \-s " width height +Specify that the input file actually consists of raw Y'CbCr samples following +the ITU\-R BT.601 recommendation, in 4:2:0 linear format. +The luma plane has size \fBwidth\fP x \fBheight\fP. +.TP +.BI \-pre " int +Specify some preprocessing steps. Using a value of '2' will trigger +quality\-dependent pseudo\-random dithering during RGBA\->YUVA conversion +(lossy compression only). +.TP +.BI \-alpha_filter " string +Specify the predictive filtering method for the alpha plane. One of 'none', +\&'fast' or 'best', in increasing complexity and slowness order. Default is +\&'fast'. Internally, alpha filtering is performed using four possible +predictions (none, horizontal, vertical, gradient). The 'best' mode will try +each mode in turn and pick the one which gives the smaller size. The 'fast' +mode will just try to form an a priori guess without testing all modes. +.TP +.BI \-alpha_method " int +Specify the algorithm used for alpha compression: 0 or 1. Algorithm 0 denotes +no compression, 1 uses WebP lossless format for compression. The default is 1. +.TP +.B \-exact +Preserve RGB values in transparent area. The default is off, to help +compressibility. +.TP +.BI \-blend_alpha " int +This option blends the alpha channel (if present) with the source using the +background color specified in hexadecimal as 0xrrggbb. The alpha channel is +afterward reset to the opaque value 255. +.TP +.B \-noalpha +Using this option will discard the alpha channel. +.TP +.BI \-hint " string +Specify the hint about input image type. Possible values are: +\fBphoto\fP, \fBpicture\fP or \fBgraph\fP. +.TP +.BI \-metadata " string +A comma separated list of metadata to copy from the input to the output if +present. +Valid values: \fBall\fP, \fBnone\fP, \fBexif\fP, \fBicc\fP, \fBxmp\fP. +The default is \fBnone\fP. + +Note: each input format may not support all combinations. +.TP +.B \-noasm +Disable all assembly optimizations. + +.SH BUGS +Please report all bugs to the issue tracker: +https://bugs.chromium.org/p/webp +.br +Patches welcome! See this page to get started: +http://www.webmproject.org/code/contribute/submitting\-patches/ + +.SH EXAMPLES +cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp +.br +cwebp \-q 70 picture_with_alpha.png \-o picture_with_alpha.webp +.br +cwebp \-sns 70 \-f 50 \-size 60000 picture.png \-o picture.webp +.br +cwebp \-o picture.webp \-\- \-\-\-picture.png + +.SH AUTHORS +\fBcwebp\fP is a part of libwebp and was written by the WebP team. +.br +The latest source tree is available at +https://chromium.googlesource.com/webm/libwebp +.PP +This manual page was written by Pascal Massimino , +for the Debian project (and may be used by others). + +.SH SEE ALSO +.BR dwebp (1), +.BR gif2webp (1) +.br +Please refer to http://developers.google.com/speed/webp/ for additional +information. diff --git a/internal/libwebp-1.2.1/man/dwebp.1 b/internal/libwebp-1.2.1/man/dwebp.1 new file mode 100644 index 0000000..04168c4 --- /dev/null +++ b/internal/libwebp-1.2.1/man/dwebp.1 @@ -0,0 +1,150 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH DWEBP 1 "November 19, 2020" +.SH NAME +dwebp \- decompress a WebP file to an image file +.SH SYNOPSIS +.B dwebp +.RI [ options ] " input_file.webp +.br +.SH DESCRIPTION +This manual page documents the +.B dwebp +command. +.PP +\fBdwebp\fP decompresses WebP files into PNG, PAM, PPM or PGM images. +Note: Animated WebP files are not supported. +.SH OPTIONS +The basic options are: +.TP +.B \-h +Print usage summary. +.TP +.B \-version +Print the version number (as major.minor.revision) and exit. +.TP +.BI \-o " string +Specify the name of the output file (as PNG format by default). +Using "-" as output name will direct output to 'stdout'. +.TP +.BI \-\- " string +Explicitly specify the input file. This option is useful if the input +file starts with an '\-' for instance. This option must appear \fBlast\fP. +Any other options afterward will be ignored. If the input file is "\-", +the data will be read from \fIstdin\fP instead of a file. +.TP +.B \-bmp +Change the output format to uncompressed BMP. +.TP +.B \-tiff +Change the output format to uncompressed TIFF. +.TP +.B \-pam +Change the output format to PAM (retains alpha). +.TP +.B \-ppm +Change the output format to PPM (discards alpha). +.TP +.B \-pgm +Change the output format to PGM. The output consists of luma/chroma +samples instead of RGB, using the IMC4 layout. This option is mainly +for verification and debugging purposes. +.TP +.B \-yuv +Change the output format to raw YUV. The output consists of +luma/chroma-U/chroma-V samples instead of RGB, saved sequentially as +individual planes. This option is mainly for verification and debugging +purposes. +.TP +.B \-nofancy +Don't use the fancy upscaler for YUV420. This may lead to jaggy +edges (especially the red ones), but should be faster. +.TP +.B \-nofilter +Don't use the in-loop filtering process even if it is required by +the bitstream. This may produce visible blocks on the non-compliant output, +but it will make the decoding faster. +.TP +.BI \-dither " strength +Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a +post-processing effect applied to chroma components in lossy compression. +It helps by smoothing gradients and avoiding banding artifacts. +.TP +.BI \-alpha_dither +If the compressed file contains a transparency plane that was quantized +during compression, this flag will allow dithering the reconstructed plane +in order to generate smoother transparency gradients. +.TP +.B \-nodither +Disable all dithering (default). +.TP +.B \-mt +Use multi-threading for decoding, if possible. +.TP +.BI \-crop " x_position y_position width height +Crop the decoded picture to a rectangle with top-left corner at coordinates +(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP. +This cropping area must be fully contained within the source rectangle. +The top-left corner will be snapped to even coordinates if needed. +This option is meant to reduce the memory needed for cropping large images. +Note: the cropping is applied \fIbefore\fP any scaling. +.TP +.B \-flip +Flip decoded image vertically (can be useful for OpenGL textures for instance). +.TP +\fB\-resize\fR, \fB\-scale\fI width height\fR +Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This +option is mostly intended to reducing the memory needed to decode large images, +when only a small version is needed (thumbnail, preview, etc.). Note: scaling +is applied \fIafter\fP cropping. +If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0, +the value will be calculated preserving the aspect-ratio. +.TP +.B \-quiet +Do not print anything. +.TP +.B \-v +Print extra information (decoding time in particular). +.TP +.B \-noasm +Disable all assembly optimizations. + +.SH BUGS +Please report all bugs to the issue tracker: +https://bugs.chromium.org/p/webp +.br +Patches welcome! See this page to get started: +http://www.webmproject.org/code/contribute/submitting-patches/ + +.SH EXAMPLES +dwebp picture.webp \-o output.png +.br +dwebp picture.webp \-ppm \-o output.ppm +.br +dwebp \-o output.ppm \-\- \-\-\-picture.webp +.br +cat picture.webp | dwebp \-o \- \-\- \- > output.ppm + +.SH AUTHORS +\fBdwebp\fP is a part of libwebp and was written by the WebP team. +.br +The latest source tree is available at +https://chromium.googlesource.com/webm/libwebp +.PP +This manual page was written by Pascal Massimino , +for the Debian project (and may be used by others). + +.SH SEE ALSO +.BR cwebp (1), +.BR gif2webp (1), +.BR webpmux (1) +.br +Please refer to http://developers.google.com/speed/webp/ for additional +information. +.SS Output file format details +PAM: http://netpbm.sourceforge.net/doc/pam.html +.br +PGM: http://netpbm.sourceforge.net/doc/pgm.html +.br +PPM: http://netpbm.sourceforge.net/doc/ppm.html +.br +PNG: http://www.libpng.org/pub/png/png-sitemap.html#info diff --git a/internal/libwebp-1.2.1/man/gif2webp.1 b/internal/libwebp-1.2.1/man/gif2webp.1 new file mode 100644 index 0000000..b4e5044 --- /dev/null +++ b/internal/libwebp-1.2.1/man/gif2webp.1 @@ -0,0 +1,164 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH GIF2WEBP 1 "May 1, 2020" +.SH NAME +gif2webp \- Convert a GIF image to WebP +.SH SYNOPSIS +.B gif2webp +.RI [ options ] " input_file.gif \-o output_file.webp +.br +.SH DESCRIPTION +This manual page documents the +.B gif2webp +command. +.PP +\fBgif2webp\fP converts a GIF image to a WebP image. +.SH OPTIONS +The basic options are: +.TP +.BI \-o " string +Specify the name of the output WebP file. If omitted, \fBgif2webp\fP will +perform conversion but only report statistics. +Using "\-" as output name will direct output to 'stdout'. +.TP +.BI \-\- " string +Explicitly specify the input file. This option is useful if the input +file starts with an '\-' for instance. This option must appear \fBlast\fP. +Any other options afterward will be ignored. If the input file is "\-", +the data will be read from \fIstdin\fP instead of a file. +.TP +.B \-h, \-help +Usage information. +.TP +.B \-version +Print the version number (as major.minor.revision) and exit. +.TP +.B \-lossy +Encode the image using lossy compression. +.TP +.B \-mixed +Mixed compression mode: optimize compression of the image by picking either +lossy or lossless compression for each frame heuristically. +.TP +.BI \-q " float +Specify the compression factor for RGB channels between 0 and 100. The default +is 75. +.br +In case of lossless compression (default), a small factor enables faster +compression speed, but produces a larger file. Maximum compression is achieved +by using a value of 100. +.br +In case of lossy compression (specified by the \-lossy option), a small factor +produces a smaller file with lower quality. Best quality is achieved by using a +value of 100. +.TP +.BI \-m " int +Specify the compression method to use. This parameter controls the +trade off between encoding speed and the compressed file size and quality. +Possible values range from 0 to 6. Default value is 4. +When higher values are used, the encoder will spend more time inspecting +additional encoding possibilities and decide on the quality gain. +Lower value can result is faster processing time at the expense of +larger file size and lower compression quality. +.TP +.BI \-min_size +Encode image to achieve smallest size. This disables key frame insertion and +picks the dispose method resulting in the smallest output for each frame. It +uses lossless compression by default, but can be combined with \-q, \-m, +\-lossy or \-mixed options. +.TP +.BI \-kmin " int +.TP +.BI \-kmax " int +Specify the minimum and maximum distance between consecutive key frames +(independently decodable frames) in the output animation. The tool will insert +some key frames into the output animation as needed so that this criteria is +satisfied. +.br +A 'kmax' value of 0 will turn off insertion of key frames. A 'kmax' value of 1 +will result in all frames being key frames. 'kmin' value is not taken into +account in both these special cases. +Typical values are in the range 3 to 30. Default values are kmin = 9, +kmax = 17 for lossless compression and kmin = 3, kmax = 5 for lossy compression. +.br +These two options are relevant only for animated images with large number of +frames (>50). +.br +When lower values are used, more frames will be converted to key frames. This +may lead to smaller number of frames required to decode a frame on average, +thereby improving the decoding performance. But this may lead to slightly bigger +file sizes. +Higher values may lead to worse decoding performance, but smaller file sizes. +.br +Some restrictions: +.br +(i) kmin < kmax, +.br +(ii) kmin >= kmax / 2 + 1 and +.br +(iii) kmax - kmin <= 30. +.br +If any of these restrictions are not met, they will be enforced automatically. +.TP +.BI \-metadata " string +A comma separated list of metadata to copy from the input to the output if +present. +Valid values: \fBall\fP, \fBnone\fP, \fBicc\fP, \fBxmp\fP. +The default is \fBxmp\fP. +.TP +.BI \-f " int +For lossy encoding only (specified by the \-lossy option). Specify the strength +of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering). +A value of 0 will turn off any filtering. Higher value will increase the +strength of the filtering process applied after decoding the picture. The higher +the value the smoother the picture will appear. Typical values are usually in +the range of 20 to 50. +.TP +.B \-mt +Use multi-threading for encoding, if possible. +.TP +.B \-loop_compatibility +If enabled, handle the loop information in a compatible fashion for Chrome +version prior to M62 (inclusive) and Firefox. +.TP +.B \-v +Print extra information. +.TP +.B \-quiet +Do not print anything. + +.SH BUGS +Please report all bugs to the issue tracker: +https://bugs.chromium.org/p/webp +.br +Patches welcome! See this page to get started: +http://www.webmproject.org/code/contribute/submitting-patches/ + +.SH EXAMPLES +gif2webp picture.gif \-o picture.webp +.br +gif2webp \-q 70 picture.gif \-o picture.webp +.br +gif2webp \-lossy \-m 3 picture.gif \-o picture_lossy.webp +.br +gif2webp \-lossy \-f 50 picture.gif \-o picture.webp +.br +gif2webp \-q 70 \-o picture.webp \-\- \-\-\-picture.gif +.br +cat picture.gif | gif2webp \-o \- \-\- \- > output.webp + +.SH AUTHORS +\fBgif2webp\fP is a part of libwebp and was written by the WebP team. +.br +The latest source tree is available at +https://chromium.googlesource.com/webm/libwebp +.PP +This manual page was written by Urvang Joshi , for the +Debian project (and may be used by others). + +.SH SEE ALSO +.BR cwebp (1), +.BR dwebp (1), +.BR webpmux (1) +.br +Please refer to http://developers.google.com/speed/webp/ for additional +information. diff --git a/internal/libwebp-1.2.1/man/img2webp.1 b/internal/libwebp-1.2.1/man/img2webp.1 new file mode 100644 index 0000000..ebf993f --- /dev/null +++ b/internal/libwebp-1.2.1/man/img2webp.1 @@ -0,0 +1,105 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH IMG2WEBP 1 "May 1, 2020" +.SH NAME +img2webp \- create animated WebP file from a sequence of input images. +.SH SYNOPSIS +.B img2webp +[file_level_options] [files] [per_frame_options...] +.br +.B img2webp argument_file_name +.br +.SH DESCRIPTION +This manual page documents the +.B img2webp +command. +.PP +\fBimg2webp\fP compresses a sequence of images using the animated WebP format. +Input images can either be PNG, JPEG, TIFF or WebP. +If a single file name (not starting with the character '\-') is supplied as +the argument, the command line arguments are actually tokenized from this file. +This allows for easy scripting or using a large number of arguments. +.SH FILE-LEVEL OPTIONS +The file-level options are applied at the beginning of the compression process, +before the input frames are read. +.TP +.BI \-o " string +Specify the name of the output WebP file. +.TP +.BI \-min_size +Encode images to achieve smallest size. This disables key frame insertion and +picks the parameters resulting in the smallest output for each frame. It uses +lossless compression by default, but can be combined with \-q, \-m, \-lossy or +\-mixed options. +.TP +.BI \-kmin " int +.TP +.BI \-kmax " int +Specify the minimum and maximum distance between consecutive key frames +(independently decodable frames) in the output animation. The tool will insert +some key frames into the output animation as needed so that this criteria is +satisfied. +.br +.B \-mixed +Mixed compression mode: optimize compression of the image by picking either +lossy or lossless compression for each frame heuristically. This global +option disables the local option \fB-lossy\fP and \fB-lossless\fP . +.TP +.BI \-loop " int +Specifies the number of times the animation should loop. Using '0' +means 'loop indefinitely'. +.TP +.BI \-v +Be more verbose. +.TP +.B \-h, \-help +A short usage summary. +.TP +.B \-version +Print the version numbers of the relevant libraries used. + +.SH PER-FRAME OPTIONS +The per-frame options are applied for the images following as arguments in the +command line. They can be modified any number of times preceding each particular +input image. +.TP +.BI \-d " int +Specify the image duration in milliseconds. +.TP +.B \-lossless, \-lossy +Compress the next image(s) using lossless or lossy compression mode. The +default mode is lossless. +.TP +.BI \-q " float +Specify the compression factor between 0 and 100. The default is 75. +.TP +.BI \-m " int +Specify the compression method to use. This parameter controls the +trade off between encoding speed and the compressed file size and quality. +Possible values range from 0 to 6. Default value is 4. + +.SH EXAMPLE +img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp +.br + +.SH BUGS +Please report all bugs to the issue tracker: +https://bugs.chromium.org/p/webp +.br +Patches welcome! See this page to get started: +http://www.webmproject.org/code/contribute/submitting\-patches/ + +.SH AUTHORS +\fBimg2webp\fP is a part of libwebp and was written by the WebP team. +.br +The latest source tree is available at +https://chromium.googlesource.com/webm/libwebp +.PP +This manual page was written by Pascal Massimino , +for the Debian project (and may be used by others). + +.SH SEE ALSO +.BR webpmux (1), +.BR gif2webp (1) +.br +Please refer to http://developers.google.com/speed/webp/ for additional +information. diff --git a/internal/libwebp-1.2.1/man/vwebp.1 b/internal/libwebp-1.2.1/man/vwebp.1 new file mode 100644 index 0000000..1da2d93 --- /dev/null +++ b/internal/libwebp-1.2.1/man/vwebp.1 @@ -0,0 +1,101 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH VWEBP 1 "June 5, 2019" +.SH NAME +vwebp \- decompress a WebP file and display it in a window +.SH SYNOPSIS +.B vwebp +.RI [ options ] " input_file.webp +.br +.SH DESCRIPTION +This manual page documents the +.B vwebp +command. +.PP +\fBvwebp\fP decompresses a WebP file and displays it in a window using OpenGL. +.SH OPTIONS +.TP +.B \-h +Print usage summary. +.TP +.B \-version +Print version number and exit. +.TP +.B \-noicc +Don't use the ICC profile if present. +.TP +.B \-nofancy +Don't use the fancy YUV420 upscaler. +.TP +.B \-nofilter +Disable in-loop filtering. +.TP +.BI \-dither " strength +Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a +post-processing effect applied to chroma components in lossy compression. +It helps by smoothing gradients and avoiding banding artifacts. Default: 50. +.TP +.BI \-noalphadither +By default, quantized transparency planes are dithered during decompression, +to smooth the gradients. This flag will prevent this dithering. +.TP +.B \-usebgcolor +Fill transparent areas with the bitstream's own background color instead of +checkerboard only. Default is white for non-animated images. +.TP +.B \-mt +Use multi-threading for decoding, if possible. +.TP +.B \-info +Display image information on top of the decoded image. +.TP +.BI \-\- " string +Explicitly specify the input file. This option is useful if the input +file starts with an '\-' for instance. This option must appear \fBlast\fP. +Any other options afterward will be ignored. If the input file is "\-", +the data will be read from \fIstdin\fP instead of a file. +.TP + +.SH KEYBOARD SHORTCUTS +.TP +.B 'c' +Toggle use of color profile. +.TP +.B 'b' +Toggle display of background color. +.TP +.B 'i' +Overlay file information. +.TP +.B 'd' +Disable blending and disposal process, for debugging purposes. +.TP +.B 'q' / 'Q' / ESC +Quit. + +.SH BUGS +Please report all bugs to the issue tracker: +https://bugs.chromium.org/p/webp +.br +Patches welcome! See this page to get started: +http://www.webmproject.org/code/contribute/submitting-patches/ + +.SH EXAMPLES +vwebp picture.webp +.br +vwebp picture.webp -mt -dither 0 +.br +vwebp \-\- \-\-\-picture.webp + +.SH AUTHORS +\fBvwebp\fP is a part of libwebp and was written by the WebP team. +.br +The latest source tree is available at +https://chromium.googlesource.com/webm/libwebp +.PP +This manual page was written for the Debian project (and may be used by others). + +.SH SEE ALSO +.BR dwebp (1) +.br +Please refer to http://developers.google.com/speed/webp/ for additional +information. diff --git a/internal/libwebp-1.2.1/man/webpinfo.1 b/internal/libwebp-1.2.1/man/webpinfo.1 new file mode 100644 index 0000000..902ba9e --- /dev/null +++ b/internal/libwebp-1.2.1/man/webpinfo.1 @@ -0,0 +1,80 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH WEBPINFO 1 "November 24, 2017" +.SH NAME +webpinfo \- print out the chunk level structure of WebP files +along with basic integrity checks. +.SH SYNOPSIS +.B webpinfo +.I OPTIONS +.I INPUT +.br +.B webpinfo [\-h|\-help|\-H|\-longhelp] +.br + +.SH DESCRIPTION +This manual page documents the +.B webpinfo +command. +.PP +\fBwebpinfo\fP can be used to print out the chunk level structure and bitstream +header information of WebP files. It can also check if the files are of valid +WebP format. + +.SH OPTIONS +.TP +.B \-version +Print the version number (as major.minor.revision) and exit. +.TP +.B \-quiet +Do not show chunk parsing information. +.TP +.B \-diag +Show parsing error diagnosis. +.TP +.B \-summary +Show chunk stats summary. +.TP +.BI \-bitstream_info +Parse bitstream header. +.TP +.B \-h, \-help +A short usage summary. +.TP +.B \-H, \-longhelp +Detailed usage instructions. + +.SH INPUT +Input files in WebP format. Input files must come last, following +options (if any). There can be multiple input files. + +.SH BUGS +Please report all bugs to the issue tracker: +https://bugs.chromium.org/p/webp +.br +Patches welcome! See this page to get started: +http://www.webmproject.org/code/contribute/submitting\-patches/ + +.SH EXAMPLES +.br +webpinfo \-h +.br +webpinfo \-diag \-summary input_file.webp +.br +webpinfo \-bitstream_info input_file_1.webp input_file_2.webp +.br +webpinfo *.webp + +.SH AUTHORS +\fBwebpinfo\fP is a part of libwebp and was written by the WebP team. +.br +The latest source tree is available at +https://chromium.googlesource.com/webm/libwebp +.PP +This manual page was written by Hui Su , +for the Debian project (and may be used by others). + +.SH SEE ALSO +.BR webpmux (1) +.br +Please refer to http://developers.google.com/speed/webp/ for additional +information. diff --git a/internal/libwebp-1.2.1/man/webpmux.1 b/internal/libwebp-1.2.1/man/webpmux.1 new file mode 100644 index 0000000..dd1eadb --- /dev/null +++ b/internal/libwebp-1.2.1/man/webpmux.1 @@ -0,0 +1,271 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH WEBPMUX 1 "November 3, 2021" +.SH NAME +webpmux \- create animated WebP files from non\-animated WebP images, extract +frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile. +.SH SYNOPSIS +.B webpmux \-get +.I GET_OPTIONS +.I INPUT +.B \-o +.I OUTPUT +.br +.B webpmux \-set +.I SET_OPTIONS +.I INPUT +.B \-o +.I OUTPUT +.br +.B webpmux \-strip +.I STRIP_OPTIONS +.I INPUT +.B \-o +.I OUTPUT +.br +.B webpmux \-frame +.I FRAME_OPTIONS +.B [ \-frame ... ] [ \-loop +.I LOOP_COUNT +.B ] +.br +.RS 8 +.B [ \-bgcolor +.I BACKGROUND_COLOR +.B ] \-o +.I OUTPUT +.RE +.br +.B webpmux \-duration +.I DURATION OPTIONS +.B [ \-duration ... ] +.I INPUT +.B \-o +.I OUTPUT +.br +.B webpmux \-info +.I INPUT +.br +.B webpmux [\-h|\-help] +.br +.B webpmux \-version +.br +.B webpmux argument_file_name +.SH DESCRIPTION +This manual page documents the +.B webpmux +command. +.PP +\fBwebpmux\fP can be used to create/extract from animated WebP files, as well as +to add/extract/strip XMP/EXIF metadata and ICC profile. +If a single file name (not starting with the character '\-') is supplied as +the argument, the command line arguments are actually tokenized from this file. +This allows for easy scripting or using a large number of arguments. +.SH OPTIONS +.SS GET_OPTIONS (\-get): +.TP +.B icc +Get ICC profile. +.TP +.B exif +Get EXIF metadata. +.TP +.B xmp +Get XMP metadata. +.TP +.BI frame " n +Get nth frame from an animated image. (n = 0 has a special meaning: last frame). + +.SS SET_OPTIONS (\-set) +.TP +.BI loop " loop_count +Set loop count on an animated file. +.P +Where: 'loop_count' must be in range [0, 65535]. +.TP +.BI bgcolor " A,R,G,B +Set the background color of the canvas on an animated file. +.P +where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying the +Alpha, Red, Green and Blue component values respectively. +.TP +.BI icc " file.icc +Set ICC profile. +.P +Where: 'file.icc' contains the ICC profile to be set. +.TP +.BI exif " file.exif +Set EXIF metadata. +.P +Where: 'file.exif' contains the EXIF metadata to be set. +.TP +.BI xmp " file.xmp +Set XMP metadata. +.P +Where: 'file.xmp' contains the XMP metadata to be set. + +.SS STRIP_OPTIONS (\-strip) +.TP +.B icc +Strip ICC profile. +.TP +.B exif +Strip EXIF metadata. +.TP +.B xmp +Strip XMP metadata. + +.SS DURATION_OPTIONS (\-duration) +Amend the duration of a specific interval of frames. This option is only +effective on animated WebP and has no effect on a single-frame file. +.TP +.I duration[,start[,end]] +Where: +.br +.B duration +is the duration for the interval in milliseconds (mandatory). +Must be non-negative. +.br +.B start +is the starting frame index of the interval (optional). +.br +.B end +is the ending frame index (inclusive) of the interval (optional). +.TP +The three typical usages of this option are: +.br +.B -duration d + set the duration to 'd' for the whole animation. +.br +.B -duration d,f + set the duration of frame 'f' to 'd'. +.br +.B -duration d,start,end + set the duration to 'd' for the whole [start,end] interval. +.TP +.P +Note that the frames outside of the [start, end] interval will remain untouched. +The 'end' value '0' has the special meaning 'last frame of the animation'. +.TP +.I Reminder: +frame indexing starts at '1'. +.br + +.SS FRAME_OPTIONS (\-frame) +Create an animated WebP file from multiple (non\-animated) WebP images. +.TP +.I file_i +di[+xi+yi[+mi[bi]]] +Where: 'file_i' is the i'th frame (WebP format), 'xi','yi' specify the image +offset for this frame, 'di' is the pause duration before next frame, 'mi' is +the dispose method for this frame (0 for NONE or 1 for BACKGROUND) and 'bi' is +the blending method for this frame (+b for BLEND or \-b for NO_BLEND). +Argument 'bi' can be omitted and will default to +b (BLEND). +Also, 'mi' can be omitted if 'bi' is omitted and will default to 0 (NONE). +Finally, if 'mi' and 'bi' are omitted then 'xi' and 'yi' can be omitted and will +default to +0+0. +.TP +.BI \-loop " n +Loop the frames n number of times. 0 indicates the frames should loop forever. +Valid range is 0 to 65535 [Default: 0 (infinite)]. +.TP +.BI \-bgcolor " A,R,G,B +Background color of the canvas. +.br +where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying the +Alpha, Red, Green and Blue component values respectively +[Default: 255,255,255,255]. + +.SS INPUT +.TP +Input file in WebP format. + +.SS OUTPUT (\-o) +.TP +Output file in WebP format. + +.SS Note: +.TP +The nature of EXIF, XMP and ICC data is not checked and is assumed to be valid. + +.SH BUGS +Please report all bugs to the issue tracker: +https://bugs.chromium.org/p/webp +.br +Patches welcome! See this page to get started: +http://www.webmproject.org/code/contribute/submitting\-patches/ + +.SH EXAMPLES +.P +Add ICC profile: +.br +webpmux \-set icc image_profile.icc in.webp \-o icc_container.webp +.P +Extract ICC profile: +.br +webpmux \-get icc icc_container.webp \-o image_profile.icc +.P +Strip ICC profile: +.br +webpmux \-strip icc icc_container.webp \-o without_icc.webp +.P +Add XMP metadata: +.br +webpmux \-set xmp image_metadata.xmp in.webp \-o xmp_container.webp +.P +Extract XMP metadata: +.br +webpmux \-get xmp xmp_container.webp \-o image_metadata.xmp +.P +Strip XMP metadata: +.br +webpmux \-strip xmp xmp_container.webp \-o without_xmp.webp +.P +Add EXIF metadata: +.br +webpmux \-set exif image_metadata.exif in.webp \-o exif_container.webp +.P +Extract EXIF metadata: +.br +webpmux \-get exif exif_container.webp \-o image_metadata.exif +.P +Strip EXIF metadata: +.br +webpmux \-strip exif exif_container.webp \-o without_exif.webp +.P +Create an animated WebP file from 3 (non\-animated) WebP images: +.br +webpmux \-frame 1.webp +100 \-frame 2.webp +100+50+50 +.br +.RS 8 +\-frame 3.webp +100+50+50+1+b \-loop 10 \-bgcolor 255,255,255,255 +.br +\-o anim_container.webp +.RE +.P +Get the 2nd frame from an animated WebP file: +.br +webpmux \-get frame 2 anim_container.webp \-o frame_2.webp +.P +Using \-get/\-set/\-strip with input file name starting with '\-': +.br +webpmux \-set icc image_profile.icc \-o icc_container.webp \-\- \-\-\-in.webp +.br +webpmux \-get icc \-o image_profile.icc \-\- \-\-\-icc_container.webp +.br +webpmux \-strip icc \-o without_icc.webp \-\- \-\-\-icc_container.webp + +.SH AUTHORS +\fBwebpmux\fP is a part of libwebp and was written by the WebP team. +.br +The latest source tree is available at +https://chromium.googlesource.com/webm/libwebp +.PP +This manual page was written by Vikas Arora , +for the Debian project (and may be used by others). + +.SH SEE ALSO +.BR cwebp (1), +.BR dwebp (1), +.BR gif2webp (1) +.br +Please refer to http://developers.google.com/speed/webp/ for additional +information. diff --git a/internal/libwebp-1.2.1/src/Makefile.am b/internal/libwebp-1.2.1/src/Makefile.am new file mode 100644 index 0000000..b4a6d3a --- /dev/null +++ b/internal/libwebp-1.2.1/src/Makefile.am @@ -0,0 +1,55 @@ +# The mux and demux libraries depend on libwebp, thus the '.' to force +# the build order so it's available to them. +SUBDIRS = dec enc dsp utils . +if BUILD_MUX + SUBDIRS += mux +endif +if BUILD_DEMUX + SUBDIRS += demux +endif + +lib_LTLIBRARIES = libwebp.la + +if BUILD_LIBWEBPDECODER + lib_LTLIBRARIES += libwebpdecoder.la +endif + +common_HEADERS = +common_HEADERS += webp/decode.h +common_HEADERS += webp/types.h +commondir = $(includedir)/webp + +libwebp_la_SOURCES = +libwebpinclude_HEADERS = +libwebpinclude_HEADERS += webp/encode.h + +noinst_HEADERS = +noinst_HEADERS += webp/format_constants.h + +libwebp_la_LIBADD = +libwebp_la_LIBADD += dec/libwebpdecode.la +libwebp_la_LIBADD += dsp/libwebpdsp.la +libwebp_la_LIBADD += enc/libwebpencode.la +libwebp_la_LIBADD += utils/libwebputils.la + +# Use '-no-undefined' to declare that libwebp does not depend on any libraries +# other than the ones listed on the command line, i.e., after linking, it will +# not have unresolved symbols. Some platforms (Windows among them) require all +# symbols in shared libraries to be resolved at library creation. +libwebp_la_LDFLAGS = -no-undefined -version-info 8:2:1 +libwebpincludedir = $(includedir)/webp +pkgconfig_DATA = libwebp.pc + +if BUILD_LIBWEBPDECODER + libwebpdecoder_la_SOURCES = + + libwebpdecoder_la_LIBADD = + libwebpdecoder_la_LIBADD += dec/libwebpdecode.la + libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la + libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la + + libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 4:2:1 + pkgconfig_DATA += libwebpdecoder.pc +endif + +${pkgconfig_DATA}: ${top_builddir}/config.status diff --git a/internal/libwebp-1.2.1/src/dec/Makefile.am b/internal/libwebp-1.2.1/src/dec/Makefile.am new file mode 100644 index 0000000..f8c6398 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/Makefile.am @@ -0,0 +1,29 @@ +AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir) +noinst_LTLIBRARIES = libwebpdecode.la + +libwebpdecode_la_SOURCES = +libwebpdecode_la_SOURCES += alpha_dec.c +libwebpdecode_la_SOURCES += alphai_dec.h +libwebpdecode_la_SOURCES += buffer_dec.c +libwebpdecode_la_SOURCES += common_dec.h +libwebpdecode_la_SOURCES += vp8_dec.h +libwebpdecode_la_SOURCES += frame_dec.c +libwebpdecode_la_SOURCES += idec_dec.c +libwebpdecode_la_SOURCES += io_dec.c +libwebpdecode_la_SOURCES += quant_dec.c +libwebpdecode_la_SOURCES += tree_dec.c +libwebpdecode_la_SOURCES += vp8_dec.c +libwebpdecode_la_SOURCES += vp8i_dec.h +libwebpdecode_la_SOURCES += vp8l_dec.c +libwebpdecode_la_SOURCES += vp8li_dec.h +libwebpdecode_la_SOURCES += webp_dec.c +libwebpdecode_la_SOURCES += webpi_dec.h + +libwebpdecodeinclude_HEADERS = +libwebpdecodeinclude_HEADERS += ../webp/decode.h +libwebpdecodeinclude_HEADERS += ../webp/types.h +noinst_HEADERS = +noinst_HEADERS += ../webp/format_constants.h + +libwebpdecode_la_CPPFLAGS = $(AM_CPPFLAGS) +libwebpdecodeincludedir = $(includedir)/webp diff --git a/internal/libwebp-1.2.1/src/dec/alpha_dec.c b/internal/libwebp-1.2.1/src/dec/alpha_dec.c new file mode 100644 index 0000000..0b93a30 --- /dev/null +++ b/internal/libwebp-1.2.1/src/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 "src/dec/alphai_dec.h" +#include "src/dec/vp8i_dec.h" +#include "src/dec/vp8li_dec.h" +#include "src/dsp/dsp.h" +#include "src/utils/quant_levels_dec_utils.h" +#include "src/utils/utils.h" +#include "src/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; + } + + 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/internal/libwebp-1.2.1/src/dec/alphai_dec.h b/internal/libwebp-1.2.1/src/dec/alphai_dec.h new file mode 100644 index 0000000..a64104a --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_DEC_H_ +#define WEBP_DEC_ALPHAI_DEC_H_ + +#include "src/dec/webpi_dec.h" +#include "src/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_DEC_H_ diff --git a/internal/libwebp-1.2.1/src/dec/buffer_dec.c b/internal/libwebp-1.2.1/src/dec/buffer_dec.c new file mode 100644 index 0000000..4786cf0 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/buffer_dec.c @@ -0,0 +1,310 @@ +// 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 "src/dec/vp8i_dec.h" +#include "src/dec/webpi_dec.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ +// WebPDecBuffer + +// Number of bytes per pixel for the different color-spaces. +static const uint8_t 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 * kModeBpp[mode], 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. + int stride; + uint64_t size; + + if ((uint64_t)w * kModeBpp[mode] >= (1ull << 31)) { + return VP8_STATUS_INVALID_PARAM; + } + stride = w * kModeBpp[mode]; + 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; + + 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 += (int64_t)(buffer->height - 1) * buf->stride; + buf->stride = -buf->stride; + } else { + WebPYUVABuffer* const buf = &buffer->u.YUVA; + const int64_t 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 width, int height, + const WebPDecoderOptions* const options, + WebPDecBuffer* const buffer) { + VP8StatusCode status; + if (buffer == NULL || width <= 0 || height <= 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 (!WebPCheckCropDimensions(width, height, x, y, cw, ch)) { + return VP8_STATUS_INVALID_PARAM; // out of frame boundary. + } + width = cw; + height = ch; + } + + if (options->use_scaling) { +#if !defined(WEBP_REDUCE_SIZE) + int scaled_width = options->scaled_width; + int scaled_height = options->scaled_height; + if (!WebPRescalerGetScaledDimensions( + width, height, &scaled_width, &scaled_height)) { + return VP8_STATUS_INVALID_PARAM; + } + width = scaled_width; + height = scaled_height; +#else + return VP8_STATUS_INVALID_PARAM; // rescaling not supported +#endif + } + } + buffer->width = width; + buffer->height = height; + + // Then, allocate buffer for real. + status = AllocateBuffer(buffer); + if (status != VP8_STATUS_OK) return status; + + // Use the stride trick if vertical flip is needed. + if (options != NULL && options->flip) { + status = WebPFlipBuffer(buffer); + } + 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/internal/libwebp-1.2.1/src/dec/common_dec.h b/internal/libwebp-1.2.1/src/dec/common_dec.h new file mode 100644 index 0000000..b158550 --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_DEC_H_ +#define WEBP_DEC_COMMON_DEC_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_DEC_H_ diff --git a/internal/libwebp-1.2.1/src/dec/frame_dec.c b/internal/libwebp-1.2.1/src/dec/frame_dec.c new file mode 100644 index 0000000..91ca1f8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/frame_dec.c @@ -0,0 +1,803 @@ +// 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 "src/dec/vp8i_dec.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ +// Main reconstruction function. + +static const uint16_t 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 uint8_t 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) { + 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(void* arg1, void* arg2) { + VP8Decoder* const dec = (VP8Decoder*)arg1; + VP8Io* const io = (VP8Io*)arg2; + 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. + } + // If dec->alpha_data_ is not NULL, we have some alpha plane present. + io->a = NULL; + if (dec->alpha_data_ != NULL && y_start < y_end) { + 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; + } + + // 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. + { + 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 = 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 2; +#endif + return 0; +} + +#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 (!CheckSizeOverflow(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_ = 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->filter_type_ > 0 && 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_ = 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_ = 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 ? 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/internal/libwebp-1.2.1/src/dec/idec_dec.c b/internal/libwebp-1.2.1/src/dec/idec_dec.c new file mode 100644 index 0000000..9035df5 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/idec_dec.c @@ -0,0 +1,908 @@ +// 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 "src/dec/alphai_dec.h" +#include "src/dec/webpi_dec.h" +#include "src/dec/vp8i_dec.h" +#include "src/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 && alph_dec->vp8l_dec_ != NULL) { + if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) { + VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_; + 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_ == NULL) ? NULL : mem->buf_ + mem->start_; + const uint8_t* const old_base = + need_compressed_alpha ? dec->alpha_data_ : old_start; + assert(mem->buf_ != NULL || mem->start_ == 0); + 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; + if (old_base != NULL) 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; + } + + assert(mem->buf_ != NULL); + 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 == NULL) ? NULL : old_buf + mem->start_; + assert(old_buf != NULL || mem->start_ == 0); + 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) { + // Synchronize the thread, clean-up and check for errors. + VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->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_; + + // Make sure partition #0 has been read before, to set dec to ready_. + if (!dec->ready_) { + return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); + } + 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); + } + // Synchronize the threads. + if (dec->mt_method_ > 0) { + if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) { + 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)) { + idec->state_ = STATE_ERROR; // prevent re-entry in IDecError + 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) { + const VP8Decoder* const dec = (VP8Decoder*)idec->dec_; + if (dec == NULL) { + return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder. + } + 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 csp, 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 (csp >= 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 = csp; + 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/internal/libwebp-1.2.1/src/dec/io_dec.c b/internal/libwebp-1.2.1/src/dec/io_dec.c new file mode 100644 index 0000000..5ef6298 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/io_dec.c @@ -0,0 +1,662 @@ +// 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 "src/dec/vp8i_dec.h" +#include "src/dec/webpi_dec.h" +#include "src/dsp/dsp.h" +#include "src/dsp/yuv.h" +#include "src/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 + (size_t)io->mb_y * buf->y_stride; + uint8_t* const u_dst = buf->u + (size_t)(io->mb_y >> 1) * buf->u_stride; + uint8_t* const v_dst = buf->v + (size_t)(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; + WebPCopyPlane(io->y, io->y_stride, y_dst, buf->y_stride, mb_w, mb_h); + WebPCopyPlane(io->u, io->uv_stride, u_dst, buf->u_stride, uv_w, uv_h); + WebPCopyPlane(io->v, io->uv_stride, v_dst, buf->v_stride, uv_w, uv_h); + 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 + (size_t)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 + (size_t)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 + (size_t)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 size_t 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 size_t start_y = GetAlphaSourceRow(io, &alpha, &num_rows); + uint8_t* const base_rgba = buf->rgba + start_y * buf->stride; +#if (WEBP_SWAP_16BIT_CSP == 1) + 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) + +#if !defined(WEBP_REDUCE_SIZE) +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 + (size_t)p->last_y * buf->a_stride; + if (io->a != NULL) { + uint8_t* const dst_y = buf->y + (size_t)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; + // scratch memory for luma rescaler + const size_t work_size = 2 * (size_t)out_width; + const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones + uint64_t total_size; + size_t rescaler_size; + rescaler_t* work; + WebPRescaler* scalers; + const int num_rescalers = has_alpha ? 4 : 3; + + total_size = ((uint64_t)work_size + 2 * uv_work_size) * sizeof(*work); + if (has_alpha) { + total_size += (uint64_t)work_size * sizeof(*work); + } + rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST; + total_size += rescaler_size; + if (!CheckSizeOverflow(total_size)) { + return 0; + } + + p->memory = WebPSafeMalloc(1ULL, (size_t)total_size); + if (p->memory == NULL) { + return 0; // memory error + } + work = (rescaler_t*)p->memory; + + scalers = (WebPRescaler*)WEBP_ALIGN( + (const uint8_t*)work + total_size - rescaler_size); + p->scaler_y = &scalers[0]; + p->scaler_u = &scalers[1]; + p->scaler_v = &scalers[2]; + p->scaler_a = has_alpha ? &scalers[3] : NULL; + + if (!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)) { + return 0; + } + p->emit = EmitRescaledYUV; + + if (has_alpha) { + if (!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)) { + return 0; + } + 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 + (size_t)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 + (size_t)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 + (size_t)uv_j * io->uv_stride, + io->uv_stride); + const int v_lines_in = WebPRescalerImport( + p->scaler_v, uv_mb_h - uv_j, io->v + (size_t)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 + (size_t)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 + (size_t)y_pos * buf->stride; +#if (WEBP_SWAP_16BIT_CSP == 1) + 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 int64_t row_offset = (int64_t)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; + // scratch memory for one rescaler + const size_t work_size = 2 * (size_t)out_width; + rescaler_t* work; // rescalers work area + uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion + uint64_t tmp_size1, tmp_size2, total_size; + size_t rescaler_size; + WebPRescaler* scalers; + const int num_rescalers = has_alpha ? 4 : 3; + + tmp_size1 = (uint64_t)num_rescalers * work_size; + tmp_size2 = (uint64_t)num_rescalers * out_width; + total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp); + rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST; + total_size += rescaler_size; + if (!CheckSizeOverflow(total_size)) { + return 0; + } + + p->memory = WebPSafeMalloc(1ULL, (size_t)total_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 - rescaler_size); + p->scaler_y = &scalers[0]; + p->scaler_u = &scalers[1]; + p->scaler_v = &scalers[2]; + p->scaler_a = has_alpha ? &scalers[3] : NULL; + + if (!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)) { + return 0; + } + p->emit = EmitRescaledRGB; + WebPInitYUV444Converters(); + + if (has_alpha) { + if (!WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h, + tmp + 3 * out_width, out_width, out_height, 0, 1, + work + 3 * work_size)) { + return 0; + } + 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; +} + +#endif // WEBP_REDUCE_SIZE + +//------------------------------------------------------------------------------ +// 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) { +#if !defined(WEBP_REDUCE_SIZE) + const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p); + if (!ok) { + return 0; // memory error + } +#else + return 0; // rescaling support not compiled +#endif + } 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(); + } + } + } + + 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/internal/libwebp-1.2.1/src/dec/quant_dec.c b/internal/libwebp-1.2.1/src/dec/quant_dec.c new file mode 100644 index 0000000..a0ac018 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/quant_dec.c @@ -0,0 +1,115 @@ +// 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 "src/dec/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, "global-header"); + const int dqy1_dc = VP8Get(br, "global-header") ? + VP8GetSignedValue(br, 4, "global-header") : 0; + const int dqy2_dc = VP8Get(br, "global-header") ? + VP8GetSignedValue(br, 4, "global-header") : 0; + const int dqy2_ac = VP8Get(br, "global-header") ? + VP8GetSignedValue(br, 4, "global-header") : 0; + const int dquv_dc = VP8Get(br, "global-header") ? + VP8GetSignedValue(br, 4, "global-header") : 0; + const int dquv_ac = VP8Get(br, "global-header") ? + VP8GetSignedValue(br, 4, "global-header") : 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/internal/libwebp-1.2.1/src/dec/tree_dec.c b/internal/libwebp-1.2.1/src/dec/tree_dec.c new file mode 100644 index 0000000..1c6fdea --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/tree_dec.c @@ -0,0 +1,537 @@ +// 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 "src/dec/vp8i_dec.h" +#include "src/utils/bit_reader_inl_utils.h" + +#if !defined(USE_GENERIC_TREE) +#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 1 // ALTERNATE_CODE +#else +#define USE_GENERIC_TREE 0 +#endif +#endif // USE_GENERIC_TREE + +#if (USE_GENERIC_TREE == 1) +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], "segments") + ? VP8GetBit(br, dec->proba_.segments_[1], "segments") + : VP8GetBit(br, dec->proba_.segments_[2], "segments") + 2; + } else { + block->segment_ = 0; // default for intra + } + if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_, "skip"); + + block->is_i4x4_ = !VP8GetBit(br, 145, "block-size"); + if (!block->is_i4x4_) { + // Hardcoded 16x16 intra-mode decision tree. + const int ymode = + VP8GetBit(br, 156, "pred-modes") ? + (VP8GetBit(br, 128, "pred-modes") ? TM_PRED : H_PRED) : + (VP8GetBit(br, 163, "pred-modes") ? 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]; +#if (USE_GENERIC_TREE == 1) + // Generic tree-parsing + int i = kYModesIntra4[VP8GetBit(br, prob[0], "pred-modes")]; + while (i > 0) { + i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i], "pred-modes")]; + } + ymode = -i; +#else + // Hardcoded tree parsing + ymode = !VP8GetBit(br, prob[0], "pred-modes") ? B_DC_PRED : + !VP8GetBit(br, prob[1], "pred-modes") ? B_TM_PRED : + !VP8GetBit(br, prob[2], "pred-modes") ? B_VE_PRED : + !VP8GetBit(br, prob[3], "pred-modes") ? + (!VP8GetBit(br, prob[4], "pred-modes") ? B_HE_PRED : + (!VP8GetBit(br, prob[5], "pred-modes") ? B_RD_PRED + : B_VR_PRED)) : + (!VP8GetBit(br, prob[6], "pred-modes") ? B_LD_PRED : + (!VP8GetBit(br, prob[7], "pred-modes") ? B_VL_PRED : + (!VP8GetBit(br, prob[8], "pred-modes") ? 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, "pred-modes-uv") ? DC_PRED + : !VP8GetBit(br, 114, "pred-modes-uv") ? V_PRED + : VP8GetBit(br, 183, "pred-modes-uv") ? 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 uint8_t 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], "global-header") ? + VP8GetValue(br, 8, "global-header") : + 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, "global-header"); + if (dec->use_skip_proba_) { + dec->skip_p_ = VP8GetValue(br, 8, "global-header"); + } +} diff --git a/internal/libwebp-1.2.1/src/dec/vp8_dec.c b/internal/libwebp-1.2.1/src/dec/vp8_dec.c new file mode 100644 index 0000000..5f405e4 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/vp8_dec.c @@ -0,0 +1,722 @@ +// 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 "src/dec/alphai_dec.h" +#include "src/dec/vp8i_dec.h" +#include "src/dec/vp8li_dec.h" +#include "src/dec/webpi_dec.h" +#include "src/utils/bit_reader_inl_utils.h" +#include "src/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, "global-header"); + if (hdr->use_segment_) { + hdr->update_map_ = VP8Get(br, "global-header"); + if (VP8Get(br, "global-header")) { // update data + int s; + hdr->absolute_delta_ = VP8Get(br, "global-header"); + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + hdr->quantizer_[s] = VP8Get(br, "global-header") ? + VP8GetSignedValue(br, 7, "global-header") : 0; + } + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + hdr->filter_strength_[s] = VP8Get(br, "global-header") ? + VP8GetSignedValue(br, 6, "global-header") : 0; + } + } + if (hdr->update_map_) { + int s; + for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) { + proba->segments_[s] = VP8Get(br, "global-header") ? + VP8GetValue(br, 8, "global-header") : 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, "global-header")) - 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, "global-header"); + hdr->level_ = VP8GetValue(br, 6, "global-header"); + hdr->sharpness_ = VP8GetValue(br, 3, "global-header"); + hdr->use_lf_delta_ = VP8Get(br, "global-header"); + if (hdr->use_lf_delta_) { + if (VP8Get(br, "global-header")) { // update lf-delta? + int i; + for (i = 0; i < NUM_REF_LF_DELTAS; ++i) { + if (VP8Get(br, "global-header")) { + hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header"); + } + } + for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) { + if (VP8Get(br, "global-header")) { + hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header"); + } + } + } + } + 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; // for soundness + 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, "global-header"); + pic_hdr->clamp_type_ = VP8Get(br, "global-header"); + } + 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, "global-header"); // 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], "coeffs")) { + if (!VP8GetBit(br, p[4], "coeffs")) { + v = 2; + } else { + v = 3 + VP8GetBit(br, p[5], "coeffs"); + } + } else { + if (!VP8GetBit(br, p[6], "coeffs")) { + if (!VP8GetBit(br, p[7], "coeffs")) { + v = 5 + VP8GetBit(br, 159, "coeffs"); + } else { + v = 7 + 2 * VP8GetBit(br, 165, "coeffs"); + v += VP8GetBit(br, 145, "coeffs"); + } + } else { + const uint8_t* tab; + const int bit1 = VP8GetBit(br, p[8], "coeffs"); + const int bit0 = VP8GetBit(br, p[9 + bit1], "coeffs"); + const int cat = 2 * bit1 + bit0; + v = 0; + for (tab = kCat3456[cat]; *tab; ++tab) { + v += v + VP8GetBit(br, *tab, "coeffs"); + } + 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], "coeffs")) { + return n; // previous coeff was last non-zero coeff + } + while (!VP8GetBit(br, p[1], "coeffs")) { // 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], "coeffs")) { + v = 1; + p = p_ctx[1]; + } else { + v = GetLargeValue(br, p); + p = p_ctx[2]; + } + out[kZigzag[n]] = VP8GetSigned(br, v, "coeffs") * 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], "coeffs")) { + return n; // previous coeff was last non-zero coeff + } + while (!VP8GetBitAlt(br, p[1], "coeffs")) { // 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], "coeffs")) { + v = 1; + p = p_ctx[1]; + } else { + v = GetLargeValue(br, p); + p = p_ctx[2]; + } + out[kZigzag[n]] = VP8GetSigned(br, v, "coeffs") * dq[n > 0]; + } + } + return 16; +} + +WEBP_DSP_INIT_FUNC(InitGetCoeffs) { + 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/internal/libwebp-1.2.1/src/dec/vp8_dec.h b/internal/libwebp-1.2.1/src/dec/vp8_dec.h new file mode 100644 index 0000000..a05405d --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_DEC_VP8_DEC_H_ +#define WEBP_DEC_VP8_DEC_H_ + +#include "src/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(); +// int ok = VP8Decode(dec, &io); +// 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_DEC_VP8_DEC_H_ diff --git a/internal/libwebp-1.2.1/src/dec/vp8i_dec.h b/internal/libwebp-1.2.1/src/dec/vp8i_dec.h new file mode 100644 index 0000000..20526a8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/vp8i_dec.h @@ -0,0 +1,319 @@ +// 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_DEC_H_ +#define WEBP_DEC_VP8I_DEC_H_ + +#include // for memcpy() +#include "src/dec/common_dec.h" +#include "src/dec/vp8li_dec.h" +#include "src/utils/bit_reader_utils.h" +#include "src/utils/random_utils.h" +#include "src/utils/thread_utils.h" +#include "src/dsp/dsp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Various defines and enums + +// version numbers +#define DEC_MAJ_VERSION 1 +#define DEC_MIN_VERSION 2 +#define DEC_REV_VERSION 1 + +// 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_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_DEC_H_ diff --git a/internal/libwebp-1.2.1/src/dec/vp8l_dec.c b/internal/libwebp-1.2.1/src/dec/vp8l_dec.c new file mode 100644 index 0000000..73c3b54 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/vp8l_dec.c @@ -0,0 +1,1742 @@ +// 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 "src/dec/alphai_dec.h" +#include "src/dec/vp8li_dec.h" +#include "src/dsp/dsp.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" +#include "src/dsp/yuv.h" +#include "src/utils/endian_inl_utils.h" +#include "src/utils/huffman_utils.h" +#include "src/utils/utils.h" + +#define NUM_ARGB_CACHE_ROWS 16 + +static const int kCodeLengthLiterals = 16; +static const int kCodeLengthRepeatCode = 16; +static const uint8_t kCodeLengthExtraBits[3] = { 2, 3, 7 }; +static const uint8_t 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 uint16_t 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* huffman_table = NULL; + int num_htree_groups = 1; + int num_htree_groups_max = 1; + int max_alphabet_size = 0; + int* code_lengths = NULL; + const int table_size = kTableSize[color_cache_bits]; + int* mapping = NULL; + int ok = 0; + + 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_max) { + num_htree_groups_max = group + 1; + } + } + // Check the validity of num_htree_groups_max. If it seems too big, use a + // smaller value for later. This will prevent big memory allocations to end + // up with a bad bitstream anyway. + // The value of 1000 is totally arbitrary. We know that num_htree_groups_max + // is smaller than (1 << 16) and should be smaller than the number of pixels + // (though the format allows it to be bigger). + if (num_htree_groups_max > 1000 || num_htree_groups_max > xsize * ysize) { + // Create a mapping from the used indices to the minimal set of used + // values [0, num_htree_groups) + mapping = (int*)WebPSafeMalloc(num_htree_groups_max, sizeof(*mapping)); + if (mapping == NULL) { + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + goto Error; + } + // -1 means a value is unmapped, and therefore unused in the Huffman + // image. + memset(mapping, 0xff, num_htree_groups_max * sizeof(*mapping)); + for (num_htree_groups = 0, i = 0; i < huffman_pixs; ++i) { + // Get the current mapping for the group and remap the Huffman image. + int* const mapped_group = &mapping[huffman_image[i]]; + if (*mapped_group == -1) *mapped_group = num_htree_groups++; + huffman_image[i] = *mapped_group; + } + } else { + num_htree_groups = num_htree_groups_max; + } + } + + 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; + } + } + + code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size, + sizeof(*code_lengths)); + huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size, + sizeof(*huffman_tables)); + htree_groups = VP8LHtreeGroupsNew(num_htree_groups); + + if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) { + dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + goto Error; + } + + huffman_table = huffman_tables; + for (i = 0; i < num_htree_groups_max; ++i) { + // If the index "i" is unused in the Huffman image, just make sure the + // coefficients are valid but do not store them. + if (mapping != NULL && mapping[i] == -1) { + 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); + } + // Passing in NULL so that nothing gets filled. + if (!ReadHuffmanCode(alphabet_size, dec, code_lengths, NULL)) { + goto Error; + } + } + } else { + HTreeGroup* const htree_group = + &htree_groups[(mapping == NULL) ? i : mapping[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] = huffman_table; + if (j == 0 && color_cache_bits > 0) { + alphabet_size += (1 << color_cache_bits); + } + size = ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_table); + if (size == 0) { + goto Error; + } + if (is_trivial_literal && kLiteralMap[j] == 1) { + is_trivial_literal = (huffman_table->bits == 0); + } + total_size += huffman_table->bits; + huffman_table += 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); + } + } + ok = 1; + + // All OK. Finalize pointers. + hdr->huffman_image_ = huffman_image; + hdr->num_htree_groups_ = num_htree_groups; + hdr->htree_groups_ = htree_groups; + hdr->huffman_tables_ = huffman_tables; + + Error: + WebPSafeFree(code_lengths); + WebPSafeFree(mapping); + if (!ok) { + WebPSafeFree(huffman_image); + WebPSafeFree(huffman_tables); + VP8LHtreeGroupsFree(htree_groups); + } + return ok; +} + +//------------------------------------------------------------------------------ +// Scaling. + +#if !defined(WEBP_REDUCE_SIZE) +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; + + if (!WebPRescalerInit(dec->rescaler, in_width, in_height, + (uint8_t*)scaled_data, out_width, out_height, + 0, num_channels, work)) { + return 0; + } + return 1; +} +#endif // WEBP_REDUCE_SIZE + +//------------------------------------------------------------------------------ +// Export to ARGB + +#if !defined(WEBP_REDUCE_SIZE) + +// 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; + uint8_t* dst = rgba; + const int dst_width = rescaler->dst_width; + int num_lines_out = 0; + while (WebPRescalerHasPendingOutput(rescaler)) { + WebPRescalerExportRow(rescaler); + WebPMultARGBRow(src, dst_width, 1); + VP8LConvertFromBGRA(src, dst_width, colorspace, dst); + dst += rgba_stride; + ++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 + (uint64_t)num_lines_in * in_stride; + uint8_t* const row_out = out + (uint64_t)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; +} + +#endif // WEBP_REDUCE_SIZE + +// 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 start_row, int num_rows, + const uint32_t* const rows) { + int n = dec->next_transform_; + const int cache_pixs = dec->width_ * num_rows; + 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, dec->last_row_, 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 + (int64_t)dec->last_out_row_ * buf->stride; + const int num_rows_out = +#if !defined(WEBP_REDUCE_SIZE) + io->use_scaling ? + EmitRescaledRowsRGBA(dec, rows_data, in_stride, io->mb_h, + rgba, buf->stride) : +#endif // WEBP_REDUCE_SIZE + 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: +#if !defined(WORDS_BIGENDIAN) + memcpy(&pattern, src, sizeof(uint16_t)); +#else + pattern = ((uint32_t)src[0] << 8) | src[1]; +#endif +#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; + } + 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; + } + br->eos_ = VP8LIsEndOfStream(br); + } + // Process the remaining rows corresponding to last row-block. + ExtractPalettedAlphaRows(dec, row > last_row ? last_row : row); + + End: + br->eos_ = VP8LIsEndOfStream(br); + 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 (VP8LIsEndOfStream(br)) break; + if (code == PACKED_NON_LITERAL_CODE) goto AdvanceByOne; + } else { + code = ReadSymbol(htree_group->htrees[GREEN], br); + } + if (VP8LIsEndOfStream(br)) break; + 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 (VP8LIsEndOfStream(br)) 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 (VP8LIsEndOfStream(br)) 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; + } + } + + 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 soundness + 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 soundness + 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, cur_row, 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); + + 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; + + // Only set here, once we are sure it is valid (to avoid thread races). + alph_dec->vp8l_dec_ = dec; + return 1; + + Err: + VP8LDelete(dec); + 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; + + 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 !defined(WEBP_REDUCE_SIZE) + if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err; +#else + if (io->use_scaling) { + dec->status_ = VP8_STATUS_INVALID_PARAM; + goto Err; + } +#endif + 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/internal/libwebp-1.2.1/src/dec/vp8li_dec.h b/internal/libwebp-1.2.1/src/dec/vp8li_dec.h new file mode 100644 index 0000000..72b2e86 --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_DEC_H_ +#define WEBP_DEC_VP8LI_DEC_H_ + +#include // for memcpy() +#include "src/dec/webpi_dec.h" +#include "src/utils/bit_reader_utils.h" +#include "src/utils/color_cache_utils.h" +#include "src/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_DEC_H_ diff --git a/internal/libwebp-1.2.1/src/dec/webp_dec.c b/internal/libwebp-1.2.1/src/dec/webp_dec.c new file mode 100644 index 0000000..77a54c5 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/webp_dec.c @@ -0,0 +1,852 @@ +// 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 "src/dec/vp8i_dec.h" +#include "src/dec/vp8li_dec.h" +#include "src/dec/webpi_dec.h" +#include "src/utils/utils.h" +#include "src/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) { + // The WebPDemux API + libwebp can be used to decode individual + // uncomposited frames or the WebPAnimDecoder can be used to fully + // reconstruct them (see webp/demux.h). + 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 WebPCheckCropDimensions(int image_width, int image_height, + int x, int y, int w, int h) { + return !(x < 0 || y < 0 || w <= 0 || h <= 0 || + x >= image_width || w > image_width || w > image_width - x || + y >= image_height || h > image_height || h > image_height - y); +} + +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; + 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 (!WebPCheckCropDimensions(W, H, x, y, w, 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; + 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/internal/libwebp-1.2.1/src/dec/webpi_dec.h b/internal/libwebp-1.2.1/src/dec/webpi_dec.h new file mode 100644 index 0000000..3b97388 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dec/webpi_dec.h @@ -0,0 +1,137 @@ +// 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_DEC_H_ +#define WEBP_DEC_WEBPI_DEC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "src/utils/rescaler_utils.h" +#include "src/dec/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 + +// Returns true if crop dimensions are within image bounds. +int WebPCheckCropDimensions(int image_width, int image_height, + int x, int y, int w, int h); + +// 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_DEC_H_ diff --git a/internal/libwebp-1.2.1/src/demux/Makefile.am b/internal/libwebp-1.2.1/src/demux/Makefile.am new file mode 100644 index 0000000..89d86c1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/demux/Makefile.am @@ -0,0 +1,18 @@ +AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir) +lib_LTLIBRARIES = libwebpdemux.la + +libwebpdemux_la_SOURCES = +libwebpdemux_la_SOURCES += anim_decode.c demux.c + +libwebpdemuxinclude_HEADERS = +libwebpdemuxinclude_HEADERS += ../webp/decode.h +libwebpdemuxinclude_HEADERS += ../webp/demux.h +libwebpdemuxinclude_HEADERS += ../webp/mux_types.h +libwebpdemuxinclude_HEADERS += ../webp/types.h +noinst_HEADERS = +noinst_HEADERS += ../webp/format_constants.h + +libwebpdemux_la_LIBADD = ../libwebp.la +libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:8:0 +libwebpdemuxincludedir = $(includedir)/webp +pkgconfig_DATA = libwebpdemux.pc diff --git a/internal/libwebp-1.2.1/src/demux/anim_decode.c b/internal/libwebp-1.2.1/src/demux/anim_decode.c new file mode 100644 index 0000000..2bf4dcf --- /dev/null +++ b/internal/libwebp-1.2.1/src/demux/anim_decode.c @@ -0,0 +1,465 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// AnimDecoder implementation. +// + +#ifdef HAVE_CONFIG_H +#include "src/webp/config.h" +#endif + +#include +#include + +#include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/demux.h" + +#define NUM_CHANNELS 4 + +typedef void (*BlendRowFunc)(uint32_t* const, const uint32_t* const, int); +static void BlendPixelRowNonPremult(uint32_t* const src, + const uint32_t* const dst, int num_pixels); +static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst, + int num_pixels); + +struct WebPAnimDecoder { + WebPDemuxer* demux_; // Demuxer created from given WebP bitstream. + WebPDecoderConfig config_; // Decoder config. + // Note: we use a pointer to a function blending multiple pixels at a time to + // allow possible inlining of per-pixel blending function. + BlendRowFunc blend_func_; // Pointer to the chose blend row function. + WebPAnimInfo info_; // Global info about the animation. + uint8_t* curr_frame_; // Current canvas (not disposed). + uint8_t* prev_frame_disposed_; // Previous canvas (properly disposed). + int prev_frame_timestamp_; // Previous frame timestamp (milliseconds). + WebPIterator prev_iter_; // Iterator object for previous frame. + int prev_frame_was_keyframe_; // True if previous frame was a keyframe. + int next_frame_; // Index of the next frame to be decoded + // (starting from 1). +}; + +static void DefaultDecoderOptions(WebPAnimDecoderOptions* const dec_options) { + dec_options->color_mode = MODE_RGBA; + dec_options->use_threads = 0; +} + +int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions* dec_options, + int abi_version) { + if (dec_options == NULL || + WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) { + return 0; + } + DefaultDecoderOptions(dec_options); + return 1; +} + +static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options, + WebPAnimDecoder* const dec) { + WEBP_CSP_MODE mode; + WebPDecoderConfig* config = &dec->config_; + assert(dec_options != NULL); + + mode = dec_options->color_mode; + if (mode != MODE_RGBA && mode != MODE_BGRA && + mode != MODE_rgbA && mode != MODE_bgrA) { + return 0; + } + dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA) + ? &BlendPixelRowNonPremult + : &BlendPixelRowPremult; + WebPInitDecoderConfig(config); + config->output.colorspace = mode; + config->output.is_external_memory = 1; + config->options.use_threads = dec_options->use_threads; + // Note: config->output.u.RGBA is set at the time of decoding each frame. + return 1; +} + +WebPAnimDecoder* WebPAnimDecoderNewInternal( + const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options, + int abi_version) { + WebPAnimDecoderOptions options; + WebPAnimDecoder* dec = NULL; + WebPBitstreamFeatures features; + if (webp_data == NULL || + WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) { + return NULL; + } + + // Validate the bitstream before doing expensive allocations. The demuxer may + // be more tolerant than the decoder. + if (WebPGetFeatures(webp_data->bytes, webp_data->size, &features) != + VP8_STATUS_OK) { + return NULL; + } + + // Note: calloc() so that the pointer members are initialized to NULL. + dec = (WebPAnimDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); + if (dec == NULL) goto Error; + + if (dec_options != NULL) { + options = *dec_options; + } else { + DefaultDecoderOptions(&options); + } + if (!ApplyDecoderOptions(&options, dec)) goto Error; + + dec->demux_ = WebPDemux(webp_data); + if (dec->demux_ == NULL) goto Error; + + dec->info_.canvas_width = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_WIDTH); + dec->info_.canvas_height = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_HEIGHT); + dec->info_.loop_count = WebPDemuxGetI(dec->demux_, WEBP_FF_LOOP_COUNT); + dec->info_.bgcolor = WebPDemuxGetI(dec->demux_, WEBP_FF_BACKGROUND_COLOR); + dec->info_.frame_count = WebPDemuxGetI(dec->demux_, WEBP_FF_FRAME_COUNT); + + // Note: calloc() because we fill frame with zeroes as well. + dec->curr_frame_ = (uint8_t*)WebPSafeCalloc( + dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height); + if (dec->curr_frame_ == NULL) goto Error; + dec->prev_frame_disposed_ = (uint8_t*)WebPSafeCalloc( + dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height); + if (dec->prev_frame_disposed_ == NULL) goto Error; + + WebPAnimDecoderReset(dec); + return dec; + + Error: + WebPAnimDecoderDelete(dec); + return NULL; +} + +int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, WebPAnimInfo* info) { + if (dec == NULL || info == NULL) return 0; + *info = dec->info_; + return 1; +} + +// Returns true if the frame covers the full canvas. +static int IsFullFrame(int width, int height, int canvas_width, + int canvas_height) { + return (width == canvas_width && height == canvas_height); +} + +// Clear the canvas to transparent. +static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width, + uint32_t canvas_height) { + const uint64_t size = + (uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf); + if (!CheckSizeOverflow(size)) return 0; + memset(buf, 0, (size_t)size); + return 1; +} + +// Clear given frame rectangle to transparent. +static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset, + int y_offset, int width, int height) { + int j; + assert(width * NUM_CHANNELS <= buf_stride); + buf += y_offset * buf_stride + x_offset * NUM_CHANNELS; + for (j = 0; j < height; ++j) { + memset(buf, 0, width * NUM_CHANNELS); + buf += buf_stride; + } +} + +// Copy width * height pixels from 'src' to 'dst'. +static int CopyCanvas(const uint8_t* src, uint8_t* dst, + uint32_t width, uint32_t height) { + const uint64_t size = (uint64_t)width * height * NUM_CHANNELS; + if (!CheckSizeOverflow(size)) return 0; + assert(src != NULL && dst != NULL); + memcpy(dst, src, (size_t)size); + return 1; +} + +// Returns true if the current frame is a key-frame. +static int IsKeyFrame(const WebPIterator* const curr, + const WebPIterator* const prev, + int prev_frame_was_key_frame, + int canvas_width, int canvas_height) { + if (curr->frame_num == 1) { + return 1; + } else if ((!curr->has_alpha || curr->blend_method == WEBP_MUX_NO_BLEND) && + IsFullFrame(curr->width, curr->height, + canvas_width, canvas_height)) { + return 1; + } else { + return (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) && + (IsFullFrame(prev->width, prev->height, canvas_width, + canvas_height) || + prev_frame_was_key_frame); + } +} + + +// Blend a single channel of 'src' over 'dst', given their alpha channel values. +// 'src' and 'dst' are assumed to be NOT pre-multiplied by alpha. +static uint8_t BlendChannelNonPremult(uint32_t src, uint8_t src_a, + uint32_t dst, uint8_t dst_a, + uint32_t scale, int shift) { + const uint8_t src_channel = (src >> shift) & 0xff; + const uint8_t dst_channel = (dst >> shift) & 0xff; + const uint32_t blend_unscaled = src_channel * src_a + dst_channel * dst_a; + assert(blend_unscaled < (1ULL << 32) / scale); + return (blend_unscaled * scale) >> 24; +} + +// Blend 'src' over 'dst' assuming they are NOT pre-multiplied by alpha. +static uint32_t BlendPixelNonPremult(uint32_t src, uint32_t dst) { + const uint8_t src_a = (src >> 24) & 0xff; + + if (src_a == 0) { + return dst; + } else { + const uint8_t dst_a = (dst >> 24) & 0xff; + // This is the approximate integer arithmetic for the actual formula: + // dst_factor_a = (dst_a * (255 - src_a)) / 255. + const uint8_t dst_factor_a = (dst_a * (256 - src_a)) >> 8; + const uint8_t blend_a = src_a + dst_factor_a; + const uint32_t scale = (1UL << 24) / blend_a; + + const uint8_t blend_r = + BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 0); + const uint8_t blend_g = + BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 8); + const uint8_t blend_b = + BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 16); + assert(src_a + dst_factor_a < 256); + + return (blend_r << 0) | + (blend_g << 8) | + (blend_b << 16) | + ((uint32_t)blend_a << 24); + } +} + +// Blend 'num_pixels' in 'src' over 'dst' assuming they are NOT pre-multiplied +// by alpha. +static void BlendPixelRowNonPremult(uint32_t* const src, + const uint32_t* const dst, int num_pixels) { + int i; + for (i = 0; i < num_pixels; ++i) { + const uint8_t src_alpha = (src[i] >> 24) & 0xff; + if (src_alpha != 0xff) { + src[i] = BlendPixelNonPremult(src[i], dst[i]); + } + } +} + +// Individually multiply each channel in 'pix' by 'scale'. +static WEBP_INLINE uint32_t ChannelwiseMultiply(uint32_t pix, uint32_t scale) { + uint32_t mask = 0x00FF00FF; + uint32_t rb = ((pix & mask) * scale) >> 8; + uint32_t ag = ((pix >> 8) & mask) * scale; + return (rb & mask) | (ag & ~mask); +} + +// Blend 'src' over 'dst' assuming they are pre-multiplied by alpha. +static uint32_t BlendPixelPremult(uint32_t src, uint32_t dst) { + const uint8_t src_a = (src >> 24) & 0xff; + return src + ChannelwiseMultiply(dst, 256 - src_a); +} + +// Blend 'num_pixels' in 'src' over 'dst' assuming they are pre-multiplied by +// alpha. +static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst, + int num_pixels) { + int i; + for (i = 0; i < num_pixels; ++i) { + const uint8_t src_alpha = (src[i] >> 24) & 0xff; + if (src_alpha != 0xff) { + src[i] = BlendPixelPremult(src[i], dst[i]); + } + } +} + +// Returns two ranges ( pairs) at row 'canvas_y', that belong to +// 'src' but not 'dst'. A point range is empty if the corresponding width is 0. +static void FindBlendRangeAtRow(const WebPIterator* const src, + const WebPIterator* const dst, int canvas_y, + int* const left1, int* const width1, + int* const left2, int* const width2) { + const int src_max_x = src->x_offset + src->width; + const int dst_max_x = dst->x_offset + dst->width; + const int dst_max_y = dst->y_offset + dst->height; + assert(canvas_y >= src->y_offset && canvas_y < (src->y_offset + src->height)); + *left1 = -1; + *width1 = 0; + *left2 = -1; + *width2 = 0; + + if (canvas_y < dst->y_offset || canvas_y >= dst_max_y || + src->x_offset >= dst_max_x || src_max_x <= dst->x_offset) { + *left1 = src->x_offset; + *width1 = src->width; + return; + } + + if (src->x_offset < dst->x_offset) { + *left1 = src->x_offset; + *width1 = dst->x_offset - src->x_offset; + } + + if (src_max_x > dst_max_x) { + *left2 = dst_max_x; + *width2 = src_max_x - dst_max_x; + } +} + +int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, + uint8_t** buf_ptr, int* timestamp_ptr) { + WebPIterator iter; + uint32_t width; + uint32_t height; + int is_key_frame; + int timestamp; + BlendRowFunc blend_row; + + if (dec == NULL || buf_ptr == NULL || timestamp_ptr == NULL) return 0; + if (!WebPAnimDecoderHasMoreFrames(dec)) return 0; + + width = dec->info_.canvas_width; + height = dec->info_.canvas_height; + blend_row = dec->blend_func_; + + // Get compressed frame. + if (!WebPDemuxGetFrame(dec->demux_, dec->next_frame_, &iter)) { + return 0; + } + timestamp = dec->prev_frame_timestamp_ + iter.duration; + + // Initialize. + is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_, + dec->prev_frame_was_keyframe_, width, height); + if (is_key_frame) { + if (!ZeroFillCanvas(dec->curr_frame_, width, height)) { + goto Error; + } + } else { + if (!CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_, + width, height)) { + goto Error; + } + } + + // Decode. + { + const uint8_t* in = iter.fragment.bytes; + const size_t in_size = iter.fragment.size; + const uint32_t stride = width * NUM_CHANNELS; // at most 25 + 2 bits + const uint64_t out_offset = (uint64_t)iter.y_offset * stride + + (uint64_t)iter.x_offset * NUM_CHANNELS; // 53b + const uint64_t size = (uint64_t)iter.height * stride; // at most 25 + 27b + WebPDecoderConfig* const config = &dec->config_; + WebPRGBABuffer* const buf = &config->output.u.RGBA; + if ((size_t)size != size) goto Error; + buf->stride = (int)stride; + buf->size = (size_t)size; + buf->rgba = dec->curr_frame_ + out_offset; + + if (WebPDecode(in, in_size, config) != VP8_STATUS_OK) { + goto Error; + } + } + + // During the decoding of current frame, we may have set some pixels to be + // transparent (i.e. alpha < 255). However, the value of each of these + // pixels should have been determined by blending it against the value of + // that pixel in the previous frame if blending method of is WEBP_MUX_BLEND. + if (iter.frame_num > 1 && iter.blend_method == WEBP_MUX_BLEND && + !is_key_frame) { + if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_NONE) { + int y; + // Blend transparent pixels with pixels in previous canvas. + for (y = 0; y < iter.height; ++y) { + const size_t offset = + (iter.y_offset + y) * width + iter.x_offset; + blend_row((uint32_t*)dec->curr_frame_ + offset, + (uint32_t*)dec->prev_frame_disposed_ + offset, iter.width); + } + } else { + int y; + assert(dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND); + // We need to blend a transparent pixel with its value just after + // initialization. That is, blend it with: + // * Fully transparent pixel if it belongs to prevRect <-- No-op. + // * The pixel in the previous canvas otherwise <-- Need alpha-blending. + for (y = 0; y < iter.height; ++y) { + const int canvas_y = iter.y_offset + y; + int left1, width1, left2, width2; + FindBlendRangeAtRow(&iter, &dec->prev_iter_, canvas_y, &left1, &width1, + &left2, &width2); + if (width1 > 0) { + const size_t offset1 = canvas_y * width + left1; + blend_row((uint32_t*)dec->curr_frame_ + offset1, + (uint32_t*)dec->prev_frame_disposed_ + offset1, width1); + } + if (width2 > 0) { + const size_t offset2 = canvas_y * width + left2; + blend_row((uint32_t*)dec->curr_frame_ + offset2, + (uint32_t*)dec->prev_frame_disposed_ + offset2, width2); + } + } + } + } + + // Update info of the previous frame and dispose it for the next iteration. + dec->prev_frame_timestamp_ = timestamp; + WebPDemuxReleaseIterator(&dec->prev_iter_); + dec->prev_iter_ = iter; + dec->prev_frame_was_keyframe_ = is_key_frame; + CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height); + if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { + ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS, + dec->prev_iter_.x_offset, dec->prev_iter_.y_offset, + dec->prev_iter_.width, dec->prev_iter_.height); + } + ++dec->next_frame_; + + // All OK, fill in the values. + *buf_ptr = dec->curr_frame_; + *timestamp_ptr = timestamp; + return 1; + + Error: + WebPDemuxReleaseIterator(&iter); + return 0; +} + +int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) { + if (dec == NULL) return 0; + return (dec->next_frame_ <= (int)dec->info_.frame_count); +} + +void WebPAnimDecoderReset(WebPAnimDecoder* dec) { + if (dec != NULL) { + dec->prev_frame_timestamp_ = 0; + WebPDemuxReleaseIterator(&dec->prev_iter_); + memset(&dec->prev_iter_, 0, sizeof(dec->prev_iter_)); + dec->prev_frame_was_keyframe_ = 0; + dec->next_frame_ = 1; + } +} + +const WebPDemuxer* WebPAnimDecoderGetDemuxer(const WebPAnimDecoder* dec) { + if (dec == NULL) return NULL; + return dec->demux_; +} + +void WebPAnimDecoderDelete(WebPAnimDecoder* dec) { + if (dec != NULL) { + WebPDemuxReleaseIterator(&dec->prev_iter_); + WebPDemuxDelete(dec->demux_); + WebPSafeFree(dec->curr_frame_); + WebPSafeFree(dec->prev_frame_disposed_); + WebPSafeFree(dec); + } +} diff --git a/internal/libwebp-1.2.1/src/demux/demux.c b/internal/libwebp-1.2.1/src/demux/demux.c new file mode 100644 index 0000000..547a772 --- /dev/null +++ b/internal/libwebp-1.2.1/src/demux/demux.c @@ -0,0 +1,978 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// WebP container demux. +// + +#ifdef HAVE_CONFIG_H +#include "src/webp/config.h" +#endif + +#include +#include +#include + +#include "src/utils/utils.h" +#include "src/webp/decode.h" // WebPGetFeatures +#include "src/webp/demux.h" +#include "src/webp/format_constants.h" + +#define DMUX_MAJ_VERSION 1 +#define DMUX_MIN_VERSION 2 +#define DMUX_REV_VERSION 1 + +typedef struct { + size_t start_; // start location of the data + size_t end_; // end location + size_t riff_end_; // riff chunk end location, can be > end_. + size_t buf_size_; // size of the buffer + const uint8_t* buf_; +} MemBuffer; + +typedef struct { + size_t offset_; + size_t size_; +} ChunkData; + +typedef struct Frame { + int x_offset_, y_offset_; + int width_, height_; + int has_alpha_; + int duration_; + WebPMuxAnimDispose dispose_method_; + WebPMuxAnimBlend blend_method_; + int frame_num_; + int complete_; // img_components_ contains a full image. + ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH + struct Frame* next_; +} Frame; + +typedef struct Chunk { + ChunkData data_; + struct Chunk* next_; +} Chunk; + +struct WebPDemuxer { + MemBuffer mem_; + WebPDemuxState state_; + int is_ext_format_; + uint32_t feature_flags_; + int canvas_width_, canvas_height_; + int loop_count_; + uint32_t bgcolor_; + int num_frames_; + Frame* frames_; + Frame** frames_tail_; + Chunk* chunks_; // non-image chunks + Chunk** chunks_tail_; +}; + +typedef enum { + PARSE_OK, + PARSE_NEED_MORE_DATA, + PARSE_ERROR +} ParseStatus; + +typedef struct ChunkParser { + uint8_t id[4]; + ParseStatus (*parse)(WebPDemuxer* const dmux); + int (*valid)(const WebPDemuxer* const dmux); +} ChunkParser; + +static ParseStatus ParseSingleImage(WebPDemuxer* const dmux); +static ParseStatus ParseVP8X(WebPDemuxer* const dmux); +static int IsValidSimpleFormat(const WebPDemuxer* const dmux); +static int IsValidExtendedFormat(const WebPDemuxer* const dmux); + +static const ChunkParser kMasterChunks[] = { + { { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat }, + { { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat }, + { { 'V', 'P', '8', 'X' }, ParseVP8X, IsValidExtendedFormat }, + { { '0', '0', '0', '0' }, NULL, NULL }, +}; + +//------------------------------------------------------------------------------ + +int WebPGetDemuxVersion(void) { + return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION; +} + +// ----------------------------------------------------------------------------- +// MemBuffer + +static int RemapMemBuffer(MemBuffer* const mem, + const uint8_t* data, size_t size) { + if (size < mem->buf_size_) return 0; // can't remap to a shorter buffer! + + mem->buf_ = data; + mem->end_ = mem->buf_size_ = size; + return 1; +} + +static int InitMemBuffer(MemBuffer* const mem, + const uint8_t* data, size_t size) { + memset(mem, 0, sizeof(*mem)); + return RemapMemBuffer(mem, data, size); +} + +// Return the remaining data size available in 'mem'. +static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) { + return (mem->end_ - mem->start_); +} + +// Return true if 'size' exceeds the end of the RIFF chunk. +static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) { + return (size > mem->riff_end_ - mem->start_); +} + +static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) { + mem->start_ += size; +} + +static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) { + mem->start_ -= size; +} + +static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) { + return mem->buf_ + mem->start_; +} + +// Read from 'mem' and skip the read bytes. +static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) { + const uint8_t byte = mem->buf_[mem->start_]; + Skip(mem, 1); + return byte; +} + +static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) { + const uint8_t* const data = mem->buf_ + mem->start_; + const int val = GetLE16(data); + Skip(mem, 2); + return val; +} + +static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) { + const uint8_t* const data = mem->buf_ + mem->start_; + const int val = GetLE24(data); + Skip(mem, 3); + return val; +} + +static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) { + const uint8_t* const data = mem->buf_ + mem->start_; + const uint32_t val = GetLE32(data); + Skip(mem, 4); + return val; +} + +// ----------------------------------------------------------------------------- +// Secondary chunk parsing + +static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) { + *dmux->chunks_tail_ = chunk; + chunk->next_ = NULL; + dmux->chunks_tail_ = &chunk->next_; +} + +// Add a frame to the end of the list, ensuring the last frame is complete. +// Returns true on success, false otherwise. +static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) { + const Frame* const last_frame = *dmux->frames_tail_; + if (last_frame != NULL && !last_frame->complete_) return 0; + + *dmux->frames_tail_ = frame; + frame->next_ = NULL; + dmux->frames_tail_ = &frame->next_; + return 1; +} + +static void SetFrameInfo(size_t start_offset, size_t size, + int frame_num, int complete, + const WebPBitstreamFeatures* const features, + Frame* const frame) { + frame->img_components_[0].offset_ = start_offset; + frame->img_components_[0].size_ = size; + frame->width_ = features->width; + frame->height_ = features->height; + frame->has_alpha_ |= features->has_alpha; + frame->frame_num_ = frame_num; + frame->complete_ = complete; +} + +// Store image bearing chunks to 'frame'. 'min_size' is an optional size +// requirement, it may be zero. +static ParseStatus StoreFrame(int frame_num, uint32_t min_size, + MemBuffer* const mem, Frame* const frame) { + int alpha_chunks = 0; + int image_chunks = 0; + int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE || + MemDataSize(mem) < min_size); + ParseStatus status = PARSE_OK; + + if (done) return PARSE_NEED_MORE_DATA; + + do { + const size_t chunk_start_offset = mem->start_; + const uint32_t fourcc = ReadLE32(mem); + const uint32_t payload_size = ReadLE32(mem); + uint32_t payload_size_padded; + size_t payload_available; + size_t chunk_size; + + if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; + + payload_size_padded = payload_size + (payload_size & 1); + payload_available = (payload_size_padded > MemDataSize(mem)) + ? MemDataSize(mem) : payload_size_padded; + chunk_size = CHUNK_HEADER_SIZE + payload_available; + if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR; + if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA; + + switch (fourcc) { + case MKFOURCC('A', 'L', 'P', 'H'): + if (alpha_chunks == 0) { + ++alpha_chunks; + frame->img_components_[1].offset_ = chunk_start_offset; + frame->img_components_[1].size_ = chunk_size; + frame->has_alpha_ = 1; + frame->frame_num_ = frame_num; + Skip(mem, payload_available); + } else { + goto Done; + } + break; + case MKFOURCC('V', 'P', '8', 'L'): + if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha + // fall through + case MKFOURCC('V', 'P', '8', ' '): + if (image_chunks == 0) { + // Extract the bitstream features, tolerating failures when the data + // is incomplete. + WebPBitstreamFeatures features; + const VP8StatusCode vp8_status = + WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size, + &features); + if (status == PARSE_NEED_MORE_DATA && + vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) { + return PARSE_NEED_MORE_DATA; + } else if (vp8_status != VP8_STATUS_OK) { + // We have enough data, and yet WebPGetFeatures() failed. + return PARSE_ERROR; + } + ++image_chunks; + SetFrameInfo(chunk_start_offset, chunk_size, frame_num, + status == PARSE_OK, &features, frame); + Skip(mem, payload_available); + } else { + goto Done; + } + break; + Done: + default: + // Restore fourcc/size when moving up one level in parsing. + Rewind(mem, CHUNK_HEADER_SIZE); + done = 1; + break; + } + + if (mem->start_ == mem->riff_end_) { + done = 1; + } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { + status = PARSE_NEED_MORE_DATA; + } + } while (!done && status == PARSE_OK); + + return status; +} + +// Creates a new Frame if 'actual_size' is within bounds and 'mem' contains +// enough data ('min_size') to parse the payload. +// Returns PARSE_OK on success with *frame pointing to the new Frame. +// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise. +static ParseStatus NewFrame(const MemBuffer* const mem, + uint32_t min_size, uint32_t actual_size, + Frame** frame) { + if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; + if (actual_size < min_size) return PARSE_ERROR; + if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; + + *frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(**frame)); + return (*frame == NULL) ? PARSE_ERROR : PARSE_OK; +} + +// Parse a 'ANMF' chunk and any image bearing chunks that immediately follow. +// 'frame_chunk_size' is the previously validated, padded chunk size. +static ParseStatus ParseAnimationFrame( + WebPDemuxer* const dmux, uint32_t frame_chunk_size) { + const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); + const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE; + int added_frame = 0; + int bits; + MemBuffer* const mem = &dmux->mem_; + Frame* frame; + size_t start_offset; + ParseStatus status = + NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame); + if (status != PARSE_OK) return status; + + frame->x_offset_ = 2 * ReadLE24s(mem); + frame->y_offset_ = 2 * ReadLE24s(mem); + frame->width_ = 1 + ReadLE24s(mem); + frame->height_ = 1 + ReadLE24s(mem); + frame->duration_ = ReadLE24s(mem); + bits = ReadByte(mem); + 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; + if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) { + WebPSafeFree(frame); + return PARSE_ERROR; + } + + // Store a frame only if the animation flag is set there is some data for + // this frame is available. + start_offset = mem->start_; + status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame); + if (status != PARSE_ERROR && mem->start_ - start_offset > anmf_payload_size) { + status = PARSE_ERROR; + } + if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) { + added_frame = AddFrame(dmux, frame); + if (added_frame) { + ++dmux->num_frames_; + } else { + status = PARSE_ERROR; + } + } + + if (!added_frame) WebPSafeFree(frame); + return status; +} + +// General chunk storage, starting with the header at 'start_offset', allowing +// the user to request the payload via a fourcc string. 'size' includes the +// header and the unpadded payload size. +// Returns true on success, false otherwise. +static int StoreChunk(WebPDemuxer* const dmux, + size_t start_offset, uint32_t size) { + Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk)); + if (chunk == NULL) return 0; + + chunk->data_.offset_ = start_offset; + chunk->data_.size_ = size; + AddChunk(dmux, chunk); + return 1; +} + +// ----------------------------------------------------------------------------- +// Primary chunk parsing + +static ParseStatus ReadHeader(MemBuffer* const mem) { + const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE; + uint32_t riff_size; + + // Basic file level validation. + if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; + if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) || + memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) { + return PARSE_ERROR; + } + + riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE); + if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR; + if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; + + // There's no point in reading past the end of the RIFF chunk + mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE; + if (mem->buf_size_ > mem->riff_end_) { + mem->buf_size_ = mem->end_ = mem->riff_end_; + } + + Skip(mem, RIFF_HEADER_SIZE); + return PARSE_OK; +} + +static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { + const size_t min_size = CHUNK_HEADER_SIZE; + MemBuffer* const mem = &dmux->mem_; + Frame* frame; + ParseStatus status; + int image_added = 0; + + if (dmux->frames_ != NULL) return PARSE_ERROR; + if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; + if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; + + frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame)); + if (frame == NULL) return PARSE_ERROR; + + // For the single image case we allow parsing of a partial frame, so no + // minimum size is imposed here. + status = StoreFrame(1, 0, &dmux->mem_, frame); + if (status != PARSE_ERROR) { + const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG); + // Clear any alpha when the alpha flag is missing. + if (!has_alpha && frame->img_components_[1].size_ > 0) { + frame->img_components_[1].offset_ = 0; + frame->img_components_[1].size_ = 0; + frame->has_alpha_ = 0; + } + + // Use the frame width/height as the canvas values for non-vp8x files. + // Also, set ALPHA_FLAG if this is a lossless image with alpha. + if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) { + dmux->state_ = WEBP_DEMUX_PARSED_HEADER; + dmux->canvas_width_ = frame->width_; + dmux->canvas_height_ = frame->height_; + dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; + } + if (!AddFrame(dmux, frame)) { + status = PARSE_ERROR; // last frame was left incomplete + } else { + image_added = 1; + dmux->num_frames_ = 1; + } + } + + if (!image_added) WebPSafeFree(frame); + return status; +} + +static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { + const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); + MemBuffer* const mem = &dmux->mem_; + int anim_chunks = 0; + ParseStatus status = PARSE_OK; + + do { + int store_chunk = 1; + const size_t chunk_start_offset = mem->start_; + const uint32_t fourcc = ReadLE32(mem); + const uint32_t chunk_size = ReadLE32(mem); + uint32_t chunk_size_padded; + + if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; + + chunk_size_padded = chunk_size + (chunk_size & 1); + if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR; + + switch (fourcc) { + case MKFOURCC('V', 'P', '8', 'X'): { + return PARSE_ERROR; + } + case MKFOURCC('A', 'L', 'P', 'H'): + case MKFOURCC('V', 'P', '8', ' '): + case MKFOURCC('V', 'P', '8', 'L'): { + // check that this isn't an animation (all frames should be in an ANMF). + if (anim_chunks > 0 || is_animation) return PARSE_ERROR; + + Rewind(mem, CHUNK_HEADER_SIZE); + status = ParseSingleImage(dmux); + break; + } + case MKFOURCC('A', 'N', 'I', 'M'): { + if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR; + + if (MemDataSize(mem) < chunk_size_padded) { + status = PARSE_NEED_MORE_DATA; + } else if (anim_chunks == 0) { + ++anim_chunks; + dmux->bgcolor_ = ReadLE32(mem); + dmux->loop_count_ = ReadLE16s(mem); + Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE); + } else { + store_chunk = 0; + goto Skip; + } + break; + } + case MKFOURCC('A', 'N', 'M', 'F'): { + if (anim_chunks == 0) return PARSE_ERROR; // 'ANIM' precedes frames. + status = ParseAnimationFrame(dmux, chunk_size_padded); + break; + } + case MKFOURCC('I', 'C', 'C', 'P'): { + store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG); + goto Skip; + } + case MKFOURCC('E', 'X', 'I', 'F'): { + store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG); + goto Skip; + } + case MKFOURCC('X', 'M', 'P', ' '): { + store_chunk = !!(dmux->feature_flags_ & XMP_FLAG); + goto Skip; + } + Skip: + default: { + if (chunk_size_padded <= MemDataSize(mem)) { + if (store_chunk) { + // Store only the chunk header and unpadded size as only the payload + // will be returned to the user. + if (!StoreChunk(dmux, chunk_start_offset, + CHUNK_HEADER_SIZE + chunk_size)) { + return PARSE_ERROR; + } + } + Skip(mem, chunk_size_padded); + } else { + status = PARSE_NEED_MORE_DATA; + } + } + } + + if (mem->start_ == mem->riff_end_) { + break; + } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { + status = PARSE_NEED_MORE_DATA; + } + } while (status == PARSE_OK); + + return status; +} + +static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { + MemBuffer* const mem = &dmux->mem_; + uint32_t vp8x_size; + + if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; + + dmux->is_ext_format_ = 1; + Skip(mem, TAG_SIZE); // VP8X + vp8x_size = ReadLE32(mem); + if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; + if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR; + vp8x_size += vp8x_size & 1; + if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR; + if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA; + + dmux->feature_flags_ = ReadByte(mem); + Skip(mem, 3); // Reserved. + dmux->canvas_width_ = 1 + ReadLE24s(mem); + dmux->canvas_height_ = 1 + ReadLE24s(mem); + if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) { + return PARSE_ERROR; // image final dimension is too large + } + Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data. + dmux->state_ = WEBP_DEMUX_PARSED_HEADER; + + if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR; + if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; + + return ParseVP8XChunks(dmux); +} + +// ----------------------------------------------------------------------------- +// Format validation + +static int IsValidSimpleFormat(const WebPDemuxer* const dmux) { + const Frame* const frame = dmux->frames_; + if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; + + if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; + if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0; + + if (frame->width_ <= 0 || frame->height_ <= 0) return 0; + return 1; +} + +// If 'exact' is true, check that the image resolution matches the canvas. +// If 'exact' is false, check that the x/y offsets do not exceed the canvas. +static int CheckFrameBounds(const Frame* const frame, int exact, + int canvas_width, int canvas_height) { + if (exact) { + if (frame->x_offset_ != 0 || frame->y_offset_ != 0) { + return 0; + } + if (frame->width_ != canvas_width || frame->height_ != canvas_height) { + return 0; + } + } else { + if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0; + if (frame->width_ + frame->x_offset_ > canvas_width) return 0; + if (frame->height_ + frame->y_offset_ > canvas_height) return 0; + } + return 1; +} + +static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { + const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); + const Frame* f = dmux->frames_; + + if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; + + 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 (dmux->feature_flags_ & ~ALL_VALID_FLAGS) return 0; // invalid bitstream + + while (f != NULL) { + const int cur_frame_set = f->frame_num_; + int frame_count = 0; + + // Check frame properties. + for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) { + const ChunkData* const image = f->img_components_; + const ChunkData* const alpha = f->img_components_ + 1; + + if (!is_animation && f->frame_num_ > 1) return 0; + + if (f->complete_) { + if (alpha->size_ == 0 && image->size_ == 0) return 0; + // Ensure alpha precedes image bitstream. + if (alpha->size_ > 0 && alpha->offset_ > image->offset_) { + return 0; + } + + if (f->width_ <= 0 || f->height_ <= 0) return 0; + } else { + // There shouldn't be a partial frame in a complete file. + if (dmux->state_ == WEBP_DEMUX_DONE) return 0; + + // Ensure alpha precedes image bitstream. + if (alpha->size_ > 0 && image->size_ > 0 && + alpha->offset_ > image->offset_) { + return 0; + } + // There shouldn't be any frames after an incomplete one. + if (f->next_ != NULL) return 0; + } + + if (f->width_ > 0 && f->height_ > 0 && + !CheckFrameBounds(f, !is_animation, + dmux->canvas_width_, dmux->canvas_height_)) { + return 0; + } + + ++frame_count; + } + } + return 1; +} + +// ----------------------------------------------------------------------------- +// WebPDemuxer object + +static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) { + dmux->state_ = WEBP_DEMUX_PARSING_HEADER; + dmux->loop_count_ = 1; + dmux->bgcolor_ = 0xFFFFFFFF; // White background by default. + dmux->canvas_width_ = -1; + dmux->canvas_height_ = -1; + dmux->frames_tail_ = &dmux->frames_; + dmux->chunks_tail_ = &dmux->chunks_; + dmux->mem_ = *mem; +} + +static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem, + WebPDemuxer** demuxer) { + WebPBitstreamFeatures features; + const VP8StatusCode status = + WebPGetFeatures(mem->buf_, mem->buf_size_, &features); + *demuxer = NULL; + if (status != VP8_STATUS_OK) { + return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA + : PARSE_ERROR; + } + + { + WebPDemuxer* const dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux)); + Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame)); + if (dmux == NULL || frame == NULL) goto Error; + InitDemux(dmux, mem); + SetFrameInfo(0, mem->buf_size_, 1 /*frame_num*/, 1 /*complete*/, &features, + frame); + if (!AddFrame(dmux, frame)) goto Error; + dmux->state_ = WEBP_DEMUX_DONE; + dmux->canvas_width_ = frame->width_; + dmux->canvas_height_ = frame->height_; + dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; + dmux->num_frames_ = 1; + assert(IsValidSimpleFormat(dmux)); + *demuxer = dmux; + return PARSE_OK; + + Error: + WebPSafeFree(dmux); + WebPSafeFree(frame); + return PARSE_ERROR; + } +} + +WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, + WebPDemuxState* state, int version) { + const ChunkParser* parser; + int partial; + ParseStatus status = PARSE_ERROR; + MemBuffer mem; + WebPDemuxer* dmux; + + if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR; + + if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL; + if (data == NULL || data->bytes == NULL || data->size == 0) return NULL; + + if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL; + status = ReadHeader(&mem); + if (status != PARSE_OK) { + // If parsing of the webp file header fails attempt to handle a raw + // VP8/VP8L frame. Note 'allow_partial' is ignored in this case. + if (status == PARSE_ERROR) { + status = CreateRawImageDemuxer(&mem, &dmux); + if (status == PARSE_OK) { + if (state != NULL) *state = WEBP_DEMUX_DONE; + return dmux; + } + } + if (state != NULL) { + *state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER + : WEBP_DEMUX_PARSE_ERROR; + } + return NULL; + } + + partial = (mem.buf_size_ < mem.riff_end_); + if (!allow_partial && partial) return NULL; + + dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux)); + if (dmux == NULL) return NULL; + InitDemux(dmux, &mem); + + status = PARSE_ERROR; + for (parser = kMasterChunks; parser->parse != NULL; ++parser) { + if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) { + status = parser->parse(dmux); + if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE; + if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR; + if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR; + if (status == PARSE_ERROR) dmux->state_ = WEBP_DEMUX_PARSE_ERROR; + break; + } + } + if (state != NULL) *state = dmux->state_; + + if (status == PARSE_ERROR) { + WebPDemuxDelete(dmux); + return NULL; + } + return dmux; +} + +void WebPDemuxDelete(WebPDemuxer* dmux) { + Chunk* c; + Frame* f; + if (dmux == NULL) return; + + for (f = dmux->frames_; f != NULL;) { + Frame* const cur_frame = f; + f = f->next_; + WebPSafeFree(cur_frame); + } + for (c = dmux->chunks_; c != NULL;) { + Chunk* const cur_chunk = c; + c = c->next_; + WebPSafeFree(cur_chunk); + } + WebPSafeFree(dmux); +} + +// ----------------------------------------------------------------------------- + +uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) { + if (dmux == NULL) return 0; + + switch (feature) { + case WEBP_FF_FORMAT_FLAGS: return dmux->feature_flags_; + case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width_; + case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height_; + case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count_; + case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_; + case WEBP_FF_FRAME_COUNT: return (uint32_t)dmux->num_frames_; + } + return 0; +} + +// ----------------------------------------------------------------------------- +// Frame iteration + +static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) { + const Frame* f; + for (f = dmux->frames_; f != NULL; f = f->next_) { + if (frame_num == f->frame_num_) break; + } + return f; +} + +static const uint8_t* GetFramePayload(const uint8_t* const mem_buf, + const Frame* const frame, + size_t* const data_size) { + *data_size = 0; + if (frame != NULL) { + const ChunkData* const image = frame->img_components_; + const ChunkData* const alpha = frame->img_components_ + 1; + size_t start_offset = image->offset_; + *data_size = image->size_; + + // if alpha exists it precedes image, update the size allowing for + // intervening chunks. + if (alpha->size_ > 0) { + const size_t inter_size = (image->offset_ > 0) + ? image->offset_ - (alpha->offset_ + alpha->size_) + : 0; + start_offset = alpha->offset_; + *data_size += alpha->size_ + inter_size; + } + return mem_buf + start_offset; + } + return NULL; +} + +// Create a whole 'frame' from VP8 (+ alpha) or lossless. +static int SynthesizeFrame(const WebPDemuxer* const dmux, + const Frame* const frame, + WebPIterator* const iter) { + const uint8_t* const mem_buf = dmux->mem_.buf_; + size_t payload_size = 0; + const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size); + if (payload == NULL) return 0; + assert(frame != NULL); + + iter->frame_num = frame->frame_num_; + iter->num_frames = dmux->num_frames_; + iter->x_offset = frame->x_offset_; + iter->y_offset = frame->y_offset_; + iter->width = frame->width_; + iter->height = frame->height_; + iter->has_alpha = frame->has_alpha_; + iter->duration = frame->duration_; + iter->dispose_method = frame->dispose_method_; + iter->blend_method = frame->blend_method_; + iter->complete = frame->complete_; + iter->fragment.bytes = payload; + iter->fragment.size = payload_size; + return 1; +} + +static int SetFrame(int frame_num, WebPIterator* const iter) { + const Frame* frame; + const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; + if (dmux == NULL || frame_num < 0) return 0; + if (frame_num > dmux->num_frames_) return 0; + if (frame_num == 0) frame_num = dmux->num_frames_; + + frame = GetFrame(dmux, frame_num); + if (frame == NULL) return 0; + + return SynthesizeFrame(dmux, frame, iter); +} + +int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) { + if (iter == NULL) return 0; + + memset(iter, 0, sizeof(*iter)); + iter->private_ = (void*)dmux; + return SetFrame(frame, iter); +} + +int WebPDemuxNextFrame(WebPIterator* iter) { + if (iter == NULL) return 0; + return SetFrame(iter->frame_num + 1, iter); +} + +int WebPDemuxPrevFrame(WebPIterator* iter) { + if (iter == NULL) return 0; + if (iter->frame_num <= 1) return 0; + return SetFrame(iter->frame_num - 1, iter); +} + +void WebPDemuxReleaseIterator(WebPIterator* iter) { + (void)iter; +} + +// ----------------------------------------------------------------------------- +// Chunk iteration + +static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) { + const uint8_t* const mem_buf = dmux->mem_.buf_; + const Chunk* c; + int count = 0; + for (c = dmux->chunks_; c != NULL; c = c->next_) { + const uint8_t* const header = mem_buf + c->data_.offset_; + if (!memcmp(header, fourcc, TAG_SIZE)) ++count; + } + return count; +} + +static const Chunk* GetChunk(const WebPDemuxer* const dmux, + const char fourcc[4], int chunk_num) { + const uint8_t* const mem_buf = dmux->mem_.buf_; + const Chunk* c; + int count = 0; + for (c = dmux->chunks_; c != NULL; c = c->next_) { + const uint8_t* const header = mem_buf + c->data_.offset_; + if (!memcmp(header, fourcc, TAG_SIZE)) ++count; + if (count == chunk_num) break; + } + return c; +} + +static int SetChunk(const char fourcc[4], int chunk_num, + WebPChunkIterator* const iter) { + const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; + int count; + + if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0; + count = ChunkCount(dmux, fourcc); + if (count == 0) return 0; + if (chunk_num == 0) chunk_num = count; + + if (chunk_num <= count) { + const uint8_t* const mem_buf = dmux->mem_.buf_; + const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num); + iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE; + iter->chunk.size = chunk->data_.size_ - CHUNK_HEADER_SIZE; + iter->num_chunks = count; + iter->chunk_num = chunk_num; + return 1; + } + return 0; +} + +int WebPDemuxGetChunk(const WebPDemuxer* dmux, + const char fourcc[4], int chunk_num, + WebPChunkIterator* iter) { + if (iter == NULL) return 0; + + memset(iter, 0, sizeof(*iter)); + iter->private_ = (void*)dmux; + return SetChunk(fourcc, chunk_num, iter); +} + +int WebPDemuxNextChunk(WebPChunkIterator* iter) { + if (iter != NULL) { + const char* const fourcc = + (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE; + return SetChunk(fourcc, iter->chunk_num + 1, iter); + } + return 0; +} + +int WebPDemuxPrevChunk(WebPChunkIterator* iter) { + if (iter != NULL && iter->chunk_num > 1) { + const char* const fourcc = + (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE; + return SetChunk(fourcc, iter->chunk_num - 1, iter); + } + return 0; +} + +void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) { + (void)iter; +} + diff --git a/internal/libwebp-1.2.1/src/demux/libwebpdemux.pc.in b/internal/libwebp-1.2.1/src/demux/libwebpdemux.pc.in new file mode 100644 index 0000000..6dfbbbd --- /dev/null +++ b/internal/libwebp-1.2.1/src/demux/libwebpdemux.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libwebpdemux +Description: Library for parsing the WebP graphics format container +Version: @PACKAGE_VERSION@ +Requires: libwebp >= 0.2.0 +Cflags: -I${includedir} +Libs: -L${libdir} -lwebpdemux diff --git a/internal/libwebp-1.2.1/src/demux/libwebpdemux.rc b/internal/libwebp-1.2.1/src/demux/libwebpdemux.rc new file mode 100644 index 0000000..8dce349 --- /dev/null +++ b/internal/libwebp-1.2.1/src/demux/libwebpdemux.rc @@ -0,0 +1,41 @@ +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,2,1 + PRODUCTVERSION 1,0,2,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Google, Inc." + VALUE "FileDescription", "libwebpdemux DLL" + VALUE "FileVersion", "1.2.1" + VALUE "InternalName", "libwebpdemux.dll" + VALUE "LegalCopyright", "Copyright (C) 2021" + VALUE "OriginalFilename", "libwebpdemux.dll" + VALUE "ProductName", "WebP Image Demuxer" + VALUE "ProductVersion", "1.2.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources diff --git a/internal/libwebp-1.2.1/src/dsp/Makefile.am b/internal/libwebp-1.2.1/src/dsp/Makefile.am new file mode 100644 index 0000000..766bb0a --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/Makefile.am @@ -0,0 +1,186 @@ +AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir) +noinst_LTLIBRARIES = +noinst_LTLIBRARIES += libwebpdsp.la +noinst_LTLIBRARIES += libwebpdsp_sse2.la +noinst_LTLIBRARIES += libwebpdspdecode_sse2.la +noinst_LTLIBRARIES += libwebpdsp_sse41.la +noinst_LTLIBRARIES += libwebpdspdecode_sse41.la +noinst_LTLIBRARIES += libwebpdsp_neon.la +noinst_LTLIBRARIES += libwebpdspdecode_neon.la +noinst_LTLIBRARIES += libwebpdsp_msa.la +noinst_LTLIBRARIES += libwebpdspdecode_msa.la +noinst_LTLIBRARIES += libwebpdsp_mips32.la +noinst_LTLIBRARIES += libwebpdspdecode_mips32.la +noinst_LTLIBRARIES += libwebpdsp_mips_dsp_r2.la +noinst_LTLIBRARIES += libwebpdspdecode_mips_dsp_r2.la + +if BUILD_LIBWEBPDECODER + noinst_LTLIBRARIES += libwebpdspdecode.la +endif + +common_HEADERS = ../webp/types.h +commondir = $(includedir)/webp + +COMMON_SOURCES = +COMMON_SOURCES += alpha_processing.c +COMMON_SOURCES += cpu.c +COMMON_SOURCES += dec.c +COMMON_SOURCES += dec_clip_tables.c +COMMON_SOURCES += dsp.h +COMMON_SOURCES += filters.c +COMMON_SOURCES += lossless.c +COMMON_SOURCES += lossless.h +COMMON_SOURCES += lossless_common.h +COMMON_SOURCES += rescaler.c +COMMON_SOURCES += upsampling.c +COMMON_SOURCES += yuv.c +COMMON_SOURCES += yuv.h + +ENC_SOURCES = +ENC_SOURCES += cost.c +ENC_SOURCES += enc.c +ENC_SOURCES += lossless_enc.c +ENC_SOURCES += quant.h +ENC_SOURCES += ssim.c + +libwebpdspdecode_sse41_la_SOURCES = +libwebpdspdecode_sse41_la_SOURCES += alpha_processing_sse41.c +libwebpdspdecode_sse41_la_SOURCES += dec_sse41.c +libwebpdspdecode_sse41_la_SOURCES += lossless_sse41.c +libwebpdspdecode_sse41_la_SOURCES += upsampling_sse41.c +libwebpdspdecode_sse41_la_SOURCES += yuv_sse41.c +libwebpdspdecode_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS) +libwebpdspdecode_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS) + +libwebpdspdecode_sse2_la_SOURCES = +libwebpdspdecode_sse2_la_SOURCES += alpha_processing_sse2.c +libwebpdspdecode_sse2_la_SOURCES += common_sse2.h +libwebpdspdecode_sse2_la_SOURCES += dec_sse2.c +libwebpdspdecode_sse2_la_SOURCES += filters_sse2.c +libwebpdspdecode_sse2_la_SOURCES += lossless_sse2.c +libwebpdspdecode_sse2_la_SOURCES += rescaler_sse2.c +libwebpdspdecode_sse2_la_SOURCES += upsampling_sse2.c +libwebpdspdecode_sse2_la_SOURCES += yuv_sse2.c +libwebpdspdecode_sse2_la_CPPFLAGS = $(libwebpdsp_sse2_la_CPPFLAGS) +libwebpdspdecode_sse2_la_CFLAGS = $(libwebpdsp_sse2_la_CFLAGS) + +libwebpdspdecode_neon_la_SOURCES = +libwebpdspdecode_neon_la_SOURCES += alpha_processing_neon.c +libwebpdspdecode_neon_la_SOURCES += dec_neon.c +libwebpdspdecode_neon_la_SOURCES += filters_neon.c +libwebpdspdecode_neon_la_SOURCES += lossless_neon.c +libwebpdspdecode_neon_la_SOURCES += neon.h +libwebpdspdecode_neon_la_SOURCES += rescaler_neon.c +libwebpdspdecode_neon_la_SOURCES += upsampling_neon.c +libwebpdspdecode_neon_la_SOURCES += yuv_neon.c +libwebpdspdecode_neon_la_CPPFLAGS = $(libwebpdsp_neon_la_CPPFLAGS) +libwebpdspdecode_neon_la_CFLAGS = $(libwebpdsp_neon_la_CFLAGS) + +libwebpdspdecode_msa_la_SOURCES = +libwebpdspdecode_msa_la_SOURCES += dec_msa.c +libwebpdspdecode_msa_la_SOURCES += filters_msa.c +libwebpdspdecode_msa_la_SOURCES += lossless_msa.c +libwebpdspdecode_msa_la_SOURCES += msa_macro.h +libwebpdspdecode_msa_la_SOURCES += rescaler_msa.c +libwebpdspdecode_msa_la_SOURCES += upsampling_msa.c +libwebpdspdecode_msa_la_CPPFLAGS = $(libwebpdsp_msa_la_CPPFLAGS) +libwebpdspdecode_msa_la_CFLAGS = $(libwebpdsp_msa_la_CFLAGS) + +libwebpdspdecode_mips32_la_SOURCES = +libwebpdspdecode_mips32_la_SOURCES += dec_mips32.c +libwebpdspdecode_mips32_la_SOURCES += mips_macro.h +libwebpdspdecode_mips32_la_SOURCES += rescaler_mips32.c +libwebpdspdecode_mips32_la_SOURCES += yuv_mips32.c +libwebpdspdecode_mips32_la_CPPFLAGS = $(libwebpdsp_mips32_la_CPPFLAGS) +libwebpdspdecode_mips32_la_CFLAGS = $(libwebpdsp_mips32_la_CFLAGS) + +libwebpdspdecode_mips_dsp_r2_la_SOURCES = +libwebpdspdecode_mips_dsp_r2_la_SOURCES += alpha_processing_mips_dsp_r2.c +libwebpdspdecode_mips_dsp_r2_la_SOURCES += dec_mips_dsp_r2.c +libwebpdspdecode_mips_dsp_r2_la_SOURCES += filters_mips_dsp_r2.c +libwebpdspdecode_mips_dsp_r2_la_SOURCES += lossless_mips_dsp_r2.c +libwebpdspdecode_mips_dsp_r2_la_SOURCES += mips_macro.h +libwebpdspdecode_mips_dsp_r2_la_SOURCES += rescaler_mips_dsp_r2.c +libwebpdspdecode_mips_dsp_r2_la_SOURCES += upsampling_mips_dsp_r2.c +libwebpdspdecode_mips_dsp_r2_la_SOURCES += yuv_mips_dsp_r2.c +libwebpdspdecode_mips_dsp_r2_la_CPPFLAGS = $(libwebpdsp_mips_dsp_r2_la_CPPFLAGS) +libwebpdspdecode_mips_dsp_r2_la_CFLAGS = $(libwebpdsp_mips_dsp_r2_la_CFLAGS) + +libwebpdsp_sse2_la_SOURCES = +libwebpdsp_sse2_la_SOURCES += cost_sse2.c +libwebpdsp_sse2_la_SOURCES += enc_sse2.c +libwebpdsp_sse2_la_SOURCES += lossless_enc_sse2.c +libwebpdsp_sse2_la_SOURCES += ssim_sse2.c +libwebpdsp_sse2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS) +libwebpdsp_sse2_la_CFLAGS = $(AM_CFLAGS) $(SSE2_FLAGS) +libwebpdsp_sse2_la_LIBADD = libwebpdspdecode_sse2.la + +libwebpdsp_sse41_la_SOURCES = +libwebpdsp_sse41_la_SOURCES += enc_sse41.c +libwebpdsp_sse41_la_SOURCES += lossless_enc_sse41.c +libwebpdsp_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS) +libwebpdsp_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS) +libwebpdsp_sse41_la_LIBADD = libwebpdspdecode_sse41.la + +libwebpdsp_neon_la_SOURCES = +libwebpdsp_neon_la_SOURCES += cost_neon.c +libwebpdsp_neon_la_SOURCES += enc_neon.c +libwebpdsp_neon_la_SOURCES += lossless_enc_neon.c +libwebpdsp_neon_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS) +libwebpdsp_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_FLAGS) +libwebpdsp_neon_la_LIBADD = libwebpdspdecode_neon.la + +libwebpdsp_msa_la_SOURCES = +libwebpdsp_msa_la_SOURCES += enc_msa.c +libwebpdsp_msa_la_SOURCES += lossless_enc_msa.c +libwebpdsp_msa_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS) +libwebpdsp_msa_la_CFLAGS = $(AM_CFLAGS) +libwebpdsp_msa_la_LIBADD = libwebpdspdecode_msa.la + +libwebpdsp_mips32_la_SOURCES = +libwebpdsp_mips32_la_SOURCES += cost_mips32.c +libwebpdsp_mips32_la_SOURCES += enc_mips32.c +libwebpdsp_mips32_la_SOURCES += lossless_enc_mips32.c +libwebpdsp_mips32_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS) +libwebpdsp_mips32_la_CFLAGS = $(AM_CFLAGS) +libwebpdsp_mips32_la_LIBADD = libwebpdspdecode_mips32.la + +libwebpdsp_mips_dsp_r2_la_SOURCES = +libwebpdsp_mips_dsp_r2_la_SOURCES += cost_mips_dsp_r2.c +libwebpdsp_mips_dsp_r2_la_SOURCES += enc_mips_dsp_r2.c +libwebpdsp_mips_dsp_r2_la_SOURCES += lossless_enc_mips_dsp_r2.c +libwebpdsp_mips_dsp_r2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS) +libwebpdsp_mips_dsp_r2_la_CFLAGS = $(AM_CFLAGS) +libwebpdsp_mips_dsp_r2_la_LIBADD = libwebpdspdecode_mips_dsp_r2.la + +libwebpdsp_la_SOURCES = $(COMMON_SOURCES) $(ENC_SOURCES) + +noinst_HEADERS = +noinst_HEADERS += ../dec/vp8_dec.h +noinst_HEADERS += ../webp/decode.h + +libwebpdsp_la_CPPFLAGS = +libwebpdsp_la_CPPFLAGS += $(AM_CPPFLAGS) +libwebpdsp_la_CPPFLAGS += $(USE_SWAP_16BIT_CSP) +libwebpdsp_la_LDFLAGS = -lm +libwebpdsp_la_LIBADD = +libwebpdsp_la_LIBADD += libwebpdsp_sse2.la +libwebpdsp_la_LIBADD += libwebpdsp_sse41.la +libwebpdsp_la_LIBADD += libwebpdsp_neon.la +libwebpdsp_la_LIBADD += libwebpdsp_msa.la +libwebpdsp_la_LIBADD += libwebpdsp_mips32.la +libwebpdsp_la_LIBADD += libwebpdsp_mips_dsp_r2.la + +if BUILD_LIBWEBPDECODER + libwebpdspdecode_la_SOURCES = $(COMMON_SOURCES) + + libwebpdspdecode_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS) + libwebpdspdecode_la_LDFLAGS = $(libwebpdsp_la_LDFLAGS) + libwebpdspdecode_la_LIBADD = + libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse2.la + libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse41.la + libwebpdspdecode_la_LIBADD += libwebpdspdecode_neon.la + libwebpdspdecode_la_LIBADD += libwebpdspdecode_msa.la + libwebpdspdecode_la_LIBADD += libwebpdspdecode_mips32.la + libwebpdspdecode_la_LIBADD += libwebpdspdecode_mips_dsp_r2.la +endif diff --git a/internal/libwebp-1.2.1/src/dsp/alpha_processing.c b/internal/libwebp-1.2.1/src/dsp/alpha_processing.c new file mode 100644 index 0000000..1892929 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/alpha_processing.c @@ -0,0 +1,495 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Utilities for processing transparent channel. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include "src/dsp/dsp.h" + +// Tables can be faster on some platform but incur some extra binary size (~2k). +#if !defined(USE_TABLES_FOR_ALPHA_MULT) +#define USE_TABLES_FOR_ALPHA_MULT 0 // ALTERNATE_CODE +#endif + + +// ----------------------------------------------------------------------------- + +#define MFIX 24 // 24bit fixed-point arithmetic +#define HALF ((1u << MFIX) >> 1) +#define KINV_255 ((1u << MFIX) / 255u) + +static uint32_t Mult(uint8_t x, uint32_t mult) { + const uint32_t v = (x * mult + HALF) >> MFIX; + assert(v <= 255); // <- 24bit precision is enough to ensure that. + return v; +} + +#if (USE_TABLES_FOR_ALPHA_MULT == 1) + +static const uint32_t kMultTables[2][256] = { + { // (255u << MFIX) / alpha + 0x00000000, 0xff000000, 0x7f800000, 0x55000000, 0x3fc00000, 0x33000000, + 0x2a800000, 0x246db6db, 0x1fe00000, 0x1c555555, 0x19800000, 0x172e8ba2, + 0x15400000, 0x139d89d8, 0x1236db6d, 0x11000000, 0x0ff00000, 0x0f000000, + 0x0e2aaaaa, 0x0d6bca1a, 0x0cc00000, 0x0c249249, 0x0b9745d1, 0x0b1642c8, + 0x0aa00000, 0x0a333333, 0x09cec4ec, 0x0971c71c, 0x091b6db6, 0x08cb08d3, + 0x08800000, 0x0839ce73, 0x07f80000, 0x07ba2e8b, 0x07800000, 0x07492492, + 0x07155555, 0x06e45306, 0x06b5e50d, 0x0689d89d, 0x06600000, 0x063831f3, + 0x06124924, 0x05ee23b8, 0x05cba2e8, 0x05aaaaaa, 0x058b2164, 0x056cefa8, + 0x05500000, 0x05343eb1, 0x05199999, 0x05000000, 0x04e76276, 0x04cfb2b7, + 0x04b8e38e, 0x04a2e8ba, 0x048db6db, 0x0479435e, 0x04658469, 0x045270d0, + 0x04400000, 0x042e29f7, 0x041ce739, 0x040c30c3, 0x03fc0000, 0x03ec4ec4, + 0x03dd1745, 0x03ce540f, 0x03c00000, 0x03b21642, 0x03a49249, 0x03976fc6, + 0x038aaaaa, 0x037e3f1f, 0x03722983, 0x03666666, 0x035af286, 0x034fcace, + 0x0344ec4e, 0x033a5440, 0x03300000, 0x0325ed09, 0x031c18f9, 0x0312818a, + 0x03092492, 0x03000000, 0x02f711dc, 0x02ee5846, 0x02e5d174, 0x02dd7baf, + 0x02d55555, 0x02cd5cd5, 0x02c590b2, 0x02bdef7b, 0x02b677d4, 0x02af286b, + 0x02a80000, 0x02a0fd5c, 0x029a1f58, 0x029364d9, 0x028ccccc, 0x0286562d, + 0x02800000, 0x0279c952, 0x0273b13b, 0x026db6db, 0x0267d95b, 0x026217ec, + 0x025c71c7, 0x0256e62a, 0x0251745d, 0x024c1bac, 0x0246db6d, 0x0241b2f9, + 0x023ca1af, 0x0237a6f4, 0x0232c234, 0x022df2df, 0x02293868, 0x02249249, + 0x02200000, 0x021b810e, 0x021714fb, 0x0212bb51, 0x020e739c, 0x020a3d70, + 0x02061861, 0x02020408, 0x01fe0000, 0x01fa0be8, 0x01f62762, 0x01f25213, + 0x01ee8ba2, 0x01ead3ba, 0x01e72a07, 0x01e38e38, 0x01e00000, 0x01dc7f10, + 0x01d90b21, 0x01d5a3e9, 0x01d24924, 0x01cefa8d, 0x01cbb7e3, 0x01c880e5, + 0x01c55555, 0x01c234f7, 0x01bf1f8f, 0x01bc14e5, 0x01b914c1, 0x01b61eed, + 0x01b33333, 0x01b05160, 0x01ad7943, 0x01aaaaaa, 0x01a7e567, 0x01a5294a, + 0x01a27627, 0x019fcbd2, 0x019d2a20, 0x019a90e7, 0x01980000, 0x01957741, + 0x0192f684, 0x01907da4, 0x018e0c7c, 0x018ba2e8, 0x018940c5, 0x0186e5f0, + 0x01849249, 0x018245ae, 0x01800000, 0x017dc11f, 0x017b88ee, 0x0179574e, + 0x01772c23, 0x01750750, 0x0172e8ba, 0x0170d045, 0x016ebdd7, 0x016cb157, + 0x016aaaaa, 0x0168a9b9, 0x0166ae6a, 0x0164b8a7, 0x0162c859, 0x0160dd67, + 0x015ef7bd, 0x015d1745, 0x015b3bea, 0x01596596, 0x01579435, 0x0155c7b4, + 0x01540000, 0x01523d03, 0x01507eae, 0x014ec4ec, 0x014d0fac, 0x014b5edc, + 0x0149b26c, 0x01480a4a, 0x01466666, 0x0144c6af, 0x01432b16, 0x0141938b, + 0x01400000, 0x013e7063, 0x013ce4a9, 0x013b5cc0, 0x0139d89d, 0x01385830, + 0x0136db6d, 0x01356246, 0x0133ecad, 0x01327a97, 0x01310bf6, 0x012fa0be, + 0x012e38e3, 0x012cd459, 0x012b7315, 0x012a150a, 0x0128ba2e, 0x01276276, + 0x01260dd6, 0x0124bc44, 0x01236db6, 0x01222222, 0x0120d97c, 0x011f93bc, + 0x011e50d7, 0x011d10c4, 0x011bd37a, 0x011a98ef, 0x0119611a, 0x01182bf2, + 0x0116f96f, 0x0115c988, 0x01149c34, 0x0113716a, 0x01124924, 0x01112358, + 0x01100000, 0x010edf12, 0x010dc087, 0x010ca458, 0x010b8a7d, 0x010a72f0, + 0x01095da8, 0x01084a9f, 0x010739ce, 0x01062b2e, 0x01051eb8, 0x01041465, + 0x01030c30, 0x01020612, 0x01010204, 0x01000000 }, + { // alpha * KINV_255 + 0x00000000, 0x00010101, 0x00020202, 0x00030303, 0x00040404, 0x00050505, + 0x00060606, 0x00070707, 0x00080808, 0x00090909, 0x000a0a0a, 0x000b0b0b, + 0x000c0c0c, 0x000d0d0d, 0x000e0e0e, 0x000f0f0f, 0x00101010, 0x00111111, + 0x00121212, 0x00131313, 0x00141414, 0x00151515, 0x00161616, 0x00171717, + 0x00181818, 0x00191919, 0x001a1a1a, 0x001b1b1b, 0x001c1c1c, 0x001d1d1d, + 0x001e1e1e, 0x001f1f1f, 0x00202020, 0x00212121, 0x00222222, 0x00232323, + 0x00242424, 0x00252525, 0x00262626, 0x00272727, 0x00282828, 0x00292929, + 0x002a2a2a, 0x002b2b2b, 0x002c2c2c, 0x002d2d2d, 0x002e2e2e, 0x002f2f2f, + 0x00303030, 0x00313131, 0x00323232, 0x00333333, 0x00343434, 0x00353535, + 0x00363636, 0x00373737, 0x00383838, 0x00393939, 0x003a3a3a, 0x003b3b3b, + 0x003c3c3c, 0x003d3d3d, 0x003e3e3e, 0x003f3f3f, 0x00404040, 0x00414141, + 0x00424242, 0x00434343, 0x00444444, 0x00454545, 0x00464646, 0x00474747, + 0x00484848, 0x00494949, 0x004a4a4a, 0x004b4b4b, 0x004c4c4c, 0x004d4d4d, + 0x004e4e4e, 0x004f4f4f, 0x00505050, 0x00515151, 0x00525252, 0x00535353, + 0x00545454, 0x00555555, 0x00565656, 0x00575757, 0x00585858, 0x00595959, + 0x005a5a5a, 0x005b5b5b, 0x005c5c5c, 0x005d5d5d, 0x005e5e5e, 0x005f5f5f, + 0x00606060, 0x00616161, 0x00626262, 0x00636363, 0x00646464, 0x00656565, + 0x00666666, 0x00676767, 0x00686868, 0x00696969, 0x006a6a6a, 0x006b6b6b, + 0x006c6c6c, 0x006d6d6d, 0x006e6e6e, 0x006f6f6f, 0x00707070, 0x00717171, + 0x00727272, 0x00737373, 0x00747474, 0x00757575, 0x00767676, 0x00777777, + 0x00787878, 0x00797979, 0x007a7a7a, 0x007b7b7b, 0x007c7c7c, 0x007d7d7d, + 0x007e7e7e, 0x007f7f7f, 0x00808080, 0x00818181, 0x00828282, 0x00838383, + 0x00848484, 0x00858585, 0x00868686, 0x00878787, 0x00888888, 0x00898989, + 0x008a8a8a, 0x008b8b8b, 0x008c8c8c, 0x008d8d8d, 0x008e8e8e, 0x008f8f8f, + 0x00909090, 0x00919191, 0x00929292, 0x00939393, 0x00949494, 0x00959595, + 0x00969696, 0x00979797, 0x00989898, 0x00999999, 0x009a9a9a, 0x009b9b9b, + 0x009c9c9c, 0x009d9d9d, 0x009e9e9e, 0x009f9f9f, 0x00a0a0a0, 0x00a1a1a1, + 0x00a2a2a2, 0x00a3a3a3, 0x00a4a4a4, 0x00a5a5a5, 0x00a6a6a6, 0x00a7a7a7, + 0x00a8a8a8, 0x00a9a9a9, 0x00aaaaaa, 0x00ababab, 0x00acacac, 0x00adadad, + 0x00aeaeae, 0x00afafaf, 0x00b0b0b0, 0x00b1b1b1, 0x00b2b2b2, 0x00b3b3b3, + 0x00b4b4b4, 0x00b5b5b5, 0x00b6b6b6, 0x00b7b7b7, 0x00b8b8b8, 0x00b9b9b9, + 0x00bababa, 0x00bbbbbb, 0x00bcbcbc, 0x00bdbdbd, 0x00bebebe, 0x00bfbfbf, + 0x00c0c0c0, 0x00c1c1c1, 0x00c2c2c2, 0x00c3c3c3, 0x00c4c4c4, 0x00c5c5c5, + 0x00c6c6c6, 0x00c7c7c7, 0x00c8c8c8, 0x00c9c9c9, 0x00cacaca, 0x00cbcbcb, + 0x00cccccc, 0x00cdcdcd, 0x00cecece, 0x00cfcfcf, 0x00d0d0d0, 0x00d1d1d1, + 0x00d2d2d2, 0x00d3d3d3, 0x00d4d4d4, 0x00d5d5d5, 0x00d6d6d6, 0x00d7d7d7, + 0x00d8d8d8, 0x00d9d9d9, 0x00dadada, 0x00dbdbdb, 0x00dcdcdc, 0x00dddddd, + 0x00dedede, 0x00dfdfdf, 0x00e0e0e0, 0x00e1e1e1, 0x00e2e2e2, 0x00e3e3e3, + 0x00e4e4e4, 0x00e5e5e5, 0x00e6e6e6, 0x00e7e7e7, 0x00e8e8e8, 0x00e9e9e9, + 0x00eaeaea, 0x00ebebeb, 0x00ececec, 0x00ededed, 0x00eeeeee, 0x00efefef, + 0x00f0f0f0, 0x00f1f1f1, 0x00f2f2f2, 0x00f3f3f3, 0x00f4f4f4, 0x00f5f5f5, + 0x00f6f6f6, 0x00f7f7f7, 0x00f8f8f8, 0x00f9f9f9, 0x00fafafa, 0x00fbfbfb, + 0x00fcfcfc, 0x00fdfdfd, 0x00fefefe, 0x00ffffff } +}; + +static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) { + return kMultTables[!inverse][a]; +} + +#else + +static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) { + return inverse ? (255u << MFIX) / a : a * KINV_255; +} + +#endif // USE_TABLES_FOR_ALPHA_MULT + +void WebPMultARGBRow_C(uint32_t* const ptr, int width, int inverse) { + int x; + for (x = 0; x < width; ++x) { + const uint32_t argb = ptr[x]; + if (argb < 0xff000000u) { // alpha < 255 + if (argb <= 0x00ffffffu) { // alpha == 0 + ptr[x] = 0; + } else { + const uint32_t alpha = (argb >> 24) & 0xff; + const uint32_t scale = GetScale(alpha, inverse); + uint32_t out = argb & 0xff000000u; + out |= Mult(argb >> 0, scale) << 0; + out |= Mult(argb >> 8, scale) << 8; + out |= Mult(argb >> 16, scale) << 16; + ptr[x] = out; + } + } + } +} + +void WebPMultRow_C(uint8_t* WEBP_RESTRICT const ptr, + const uint8_t* WEBP_RESTRICT const alpha, + int width, int inverse) { + int x; + for (x = 0; x < width; ++x) { + const uint32_t a = alpha[x]; + if (a != 255) { + if (a == 0) { + ptr[x] = 0; + } else { + const uint32_t scale = GetScale(a, inverse); + ptr[x] = Mult(ptr[x], scale); + } + } + } +} + +#undef KINV_255 +#undef HALF +#undef MFIX + +void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse); +void (*WebPMultRow)(uint8_t* WEBP_RESTRICT const ptr, + const uint8_t* WEBP_RESTRICT const alpha, + int width, int inverse); + +//------------------------------------------------------------------------------ +// Generic per-plane calls + +void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows, + int inverse) { + int n; + for (n = 0; n < num_rows; ++n) { + WebPMultARGBRow((uint32_t*)ptr, width, inverse); + ptr += stride; + } +} + +void WebPMultRows(uint8_t* WEBP_RESTRICT ptr, int stride, + const uint8_t* WEBP_RESTRICT alpha, int alpha_stride, + int width, int num_rows, int inverse) { + int n; + for (n = 0; n < num_rows; ++n) { + WebPMultRow(ptr, alpha, width, inverse); + ptr += stride; + alpha += alpha_stride; + } +} + +//------------------------------------------------------------------------------ +// Premultiplied modes + +// non dithered-modes + +// (x * a * 32897) >> 23 is bit-wise equivalent to (int)(x * a / 255.) +// for all 8bit x or a. For bit-wise equivalence to (int)(x * a / 255. + .5), +// one can use instead: (x * a * 65793 + (1 << 23)) >> 24 +#if 1 // (int)(x * a / 255.) +#define MULTIPLIER(a) ((a) * 32897U) +#define PREMULTIPLY(x, m) (((x) * (m)) >> 23) +#else // (int)(x * a / 255. + .5) +#define MULTIPLIER(a) ((a) * 65793U) +#define PREMULTIPLY(x, m) (((x) * (m) + (1U << 23)) >> 24) +#endif + +#if !WEBP_NEON_OMIT_C_CODE +static void ApplyAlphaMultiply_C(uint8_t* rgba, int alpha_first, + int w, int h, int stride) { + while (h-- > 0) { + uint8_t* const rgb = rgba + (alpha_first ? 1 : 0); + const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3); + int i; + for (i = 0; i < w; ++i) { + 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; + } +} +#endif // !WEBP_NEON_OMIT_C_CODE +#undef MULTIPLIER +#undef PREMULTIPLY + +// rgbA4444 + +#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15 + +static WEBP_INLINE uint8_t dither_hi(uint8_t x) { + return (x & 0xf0) | (x >> 4); +} + +static WEBP_INLINE uint8_t dither_lo(uint8_t x) { + return (x & 0x0f) | (x << 4); +} + +static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) { + return (x * m) >> 16; +} + +static WEBP_INLINE void ApplyAlphaMultiply4444_C(uint8_t* rgba4444, + int w, int h, int stride, + int rg_byte_pos /* 0 or 1 */) { + while (h-- > 0) { + int i; + for (i = 0; i < w; ++i) { + const uint32_t rg = rgba4444[2 * i + rg_byte_pos]; + const uint32_t ba = rgba4444[2 * i + (rg_byte_pos ^ 1)]; + const uint8_t a = ba & 0x0f; + const uint32_t mult = MULTIPLIER(a); + const uint8_t r = multiply(dither_hi(rg), mult); + const uint8_t g = multiply(dither_lo(rg), mult); + const uint8_t b = multiply(dither_hi(ba), mult); + rgba4444[2 * i + rg_byte_pos] = (r & 0xf0) | ((g >> 4) & 0x0f); + rgba4444[2 * i + (rg_byte_pos ^ 1)] = (b & 0xf0) | a; + } + rgba4444 += stride; + } +} +#undef MULTIPLIER + +static void ApplyAlphaMultiply_16b_C(uint8_t* rgba4444, + int w, int h, int stride) { +#if (WEBP_SWAP_16BIT_CSP == 1) + ApplyAlphaMultiply4444_C(rgba4444, w, h, stride, 1); +#else + ApplyAlphaMultiply4444_C(rgba4444, w, h, stride, 0); +#endif +} + +#if !WEBP_NEON_OMIT_C_CODE +static int DispatchAlpha_C(const uint8_t* WEBP_RESTRICT alpha, int alpha_stride, + int width, int height, + uint8_t* WEBP_RESTRICT dst, int dst_stride) { + uint32_t alpha_mask = 0xff; + int i, j; + + for (j = 0; j < height; ++j) { + for (i = 0; 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; + } + + return (alpha_mask != 0xff); +} + +static void DispatchAlphaToGreen_C(const uint8_t* WEBP_RESTRICT alpha, + int alpha_stride, int width, int height, + uint32_t* WEBP_RESTRICT dst, + int dst_stride) { + int i, j; + for (j = 0; j < height; ++j) { + for (i = 0; i < width; ++i) { + dst[i] = alpha[i] << 8; // leave A/R/B channels zero'd. + } + alpha += alpha_stride; + dst += dst_stride; + } +} + +static int ExtractAlpha_C(const uint8_t* WEBP_RESTRICT argb, int argb_stride, + int width, int height, + uint8_t* WEBP_RESTRICT alpha, int alpha_stride) { + uint8_t alpha_mask = 0xff; + int i, j; + + for (j = 0; j < height; ++j) { + for (i = 0; i < width; ++i) { + const uint8_t alpha_value = argb[4 * i]; + alpha[i] = alpha_value; + alpha_mask &= alpha_value; + } + argb += argb_stride; + alpha += alpha_stride; + } + return (alpha_mask == 0xff); +} + +static void ExtractGreen_C(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT alpha, int size) { + int i; + for (i = 0; i < size; ++i) alpha[i] = argb[i] >> 8; +} +#endif // !WEBP_NEON_OMIT_C_CODE + +//------------------------------------------------------------------------------ + +static int HasAlpha8b_C(const uint8_t* src, int length) { + while (length-- > 0) if (*src++ != 0xff) return 1; + return 0; +} + +static int HasAlpha32b_C(const uint8_t* src, int length) { + int x; + for (x = 0; length-- > 0; x += 4) if (src[x] != 0xff) return 1; + return 0; +} + +static void AlphaReplace_C(uint32_t* src, int length, uint32_t color) { + int x; + for (x = 0; x < length; ++x) if ((src[x] >> 24) == 0) src[x] = color; +} + +//------------------------------------------------------------------------------ +// Simple channel manipulations. + +static WEBP_INLINE uint32_t MakeARGB32(int a, int r, int g, int b) { + return (((uint32_t)a << 24) | (r << 16) | (g << 8) | b); +} + +#ifdef WORDS_BIGENDIAN +static void PackARGB_C(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT r, + const uint8_t* WEBP_RESTRICT g, + const uint8_t* WEBP_RESTRICT b, + int len, uint32_t* WEBP_RESTRICT out) { + int i; + for (i = 0; i < len; ++i) { + out[i] = MakeARGB32(a[4 * i], r[4 * i], g[4 * i], b[4 * i]); + } +} +#endif + +static void PackRGB_C(const uint8_t* WEBP_RESTRICT r, + const uint8_t* WEBP_RESTRICT g, + const uint8_t* WEBP_RESTRICT b, + int len, int step, uint32_t* WEBP_RESTRICT out) { + int i, offset = 0; + for (i = 0; i < len; ++i) { + out[i] = MakeARGB32(0xff, r[offset], g[offset], b[offset]); + offset += step; + } +} + +void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int); +void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int); +int (*WebPDispatchAlpha)(const uint8_t* WEBP_RESTRICT, int, int, int, + uint8_t* WEBP_RESTRICT, int); +void (*WebPDispatchAlphaToGreen)(const uint8_t* WEBP_RESTRICT, int, int, int, + uint32_t* WEBP_RESTRICT, int); +int (*WebPExtractAlpha)(const uint8_t* WEBP_RESTRICT, int, int, int, + uint8_t* WEBP_RESTRICT, int); +void (*WebPExtractGreen)(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT alpha, int size); +#ifdef WORDS_BIGENDIAN +void (*WebPPackARGB)(const uint8_t* a, const uint8_t* r, const uint8_t* g, + const uint8_t* b, int, uint32_t*); +#endif +void (*WebPPackRGB)(const uint8_t* WEBP_RESTRICT r, + const uint8_t* WEBP_RESTRICT g, + const uint8_t* WEBP_RESTRICT b, + int len, int step, uint32_t* WEBP_RESTRICT out); + +int (*WebPHasAlpha8b)(const uint8_t* src, int length); +int (*WebPHasAlpha32b)(const uint8_t* src, int length); +void (*WebPAlphaReplace)(uint32_t* src, int length, uint32_t color); + +//------------------------------------------------------------------------------ +// Init function + +extern void WebPInitAlphaProcessingMIPSdspR2(void); +extern void WebPInitAlphaProcessingSSE2(void); +extern void WebPInitAlphaProcessingSSE41(void); +extern void WebPInitAlphaProcessingNEON(void); + +WEBP_DSP_INIT_FUNC(WebPInitAlphaProcessing) { + WebPMultARGBRow = WebPMultARGBRow_C; + WebPMultRow = WebPMultRow_C; + WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply_16b_C; + +#ifdef WORDS_BIGENDIAN + WebPPackARGB = PackARGB_C; +#endif + WebPPackRGB = PackRGB_C; +#if !WEBP_NEON_OMIT_C_CODE + WebPApplyAlphaMultiply = ApplyAlphaMultiply_C; + WebPDispatchAlpha = DispatchAlpha_C; + WebPDispatchAlphaToGreen = DispatchAlphaToGreen_C; + WebPExtractAlpha = ExtractAlpha_C; + WebPExtractGreen = ExtractGreen_C; +#endif + + WebPHasAlpha8b = HasAlpha8b_C; + WebPHasAlpha32b = HasAlpha32b_C; + WebPAlphaReplace = AlphaReplace_C; + + // If defined, use CPUInfo() to overwrite some pointers with faster versions. + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + WebPInitAlphaProcessingSSE2(); +#if defined(WEBP_HAVE_SSE41) + if (VP8GetCPUInfo(kSSE4_1)) { + WebPInitAlphaProcessingSSE41(); + } +#endif + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + WebPInitAlphaProcessingMIPSdspR2(); + } +#endif + } + +#if defined(WEBP_HAVE_NEON) + if (WEBP_NEON_OMIT_C_CODE || + (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) { + WebPInitAlphaProcessingNEON(); + } +#endif + + assert(WebPMultARGBRow != NULL); + assert(WebPMultRow != NULL); + assert(WebPApplyAlphaMultiply != NULL); + assert(WebPApplyAlphaMultiply4444 != NULL); + assert(WebPDispatchAlpha != NULL); + assert(WebPDispatchAlphaToGreen != NULL); + assert(WebPExtractAlpha != NULL); + assert(WebPExtractGreen != NULL); +#ifdef WORDS_BIGENDIAN + assert(WebPPackARGB != NULL); +#endif + assert(WebPPackRGB != NULL); + assert(WebPHasAlpha8b != NULL); + assert(WebPHasAlpha32b != NULL); + assert(WebPAlphaReplace != NULL); +} diff --git a/internal/libwebp-1.2.1/src/dsp/alpha_processing_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/alpha_processing_mips_dsp_r2.c new file mode 100644 index 0000000..0090e87 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/alpha_processing_mips_dsp_r2.c @@ -0,0 +1,228 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Utilities for processing transparent channel. +// +// Author(s): Branimir Vasic (branimir.vasic@imgtec.com) +// Djordje Pesut (djordje.pesut@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) + +static int DispatchAlpha_MIPSdspR2(const uint8_t* alpha, int alpha_stride, + int width, int height, + uint8_t* dst, int dst_stride) { + uint32_t alpha_mask = 0xffffffff; + int i, j, temp0; + + for (j = 0; j < height; ++j) { + uint8_t* pdst = dst; + const uint8_t* palpha = alpha; + for (i = 0; i < (width >> 2); ++i) { + int temp1, temp2, temp3; + + __asm__ volatile ( + "ulw %[temp0], 0(%[palpha]) \n\t" + "addiu %[palpha], %[palpha], 4 \n\t" + "addiu %[pdst], %[pdst], 16 \n\t" + "srl %[temp1], %[temp0], 8 \n\t" + "srl %[temp2], %[temp0], 16 \n\t" + "srl %[temp3], %[temp0], 24 \n\t" + "and %[alpha_mask], %[alpha_mask], %[temp0] \n\t" + "sb %[temp0], -16(%[pdst]) \n\t" + "sb %[temp1], -12(%[pdst]) \n\t" + "sb %[temp2], -8(%[pdst]) \n\t" + "sb %[temp3], -4(%[pdst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [palpha]"+r"(palpha), [pdst]"+r"(pdst), + [alpha_mask]"+r"(alpha_mask) + : + : "memory" + ); + } + + for (i = 0; i < (width & 3); ++i) { + __asm__ volatile ( + "lbu %[temp0], 0(%[palpha]) \n\t" + "addiu %[palpha], %[palpha], 1 \n\t" + "sb %[temp0], 0(%[pdst]) \n\t" + "and %[alpha_mask], %[alpha_mask], %[temp0] \n\t" + "addiu %[pdst], %[pdst], 4 \n\t" + : [temp0]"=&r"(temp0), [palpha]"+r"(palpha), [pdst]"+r"(pdst), + [alpha_mask]"+r"(alpha_mask) + : + : "memory" + ); + } + alpha += alpha_stride; + dst += dst_stride; + } + + __asm__ volatile ( + "ext %[temp0], %[alpha_mask], 0, 16 \n\t" + "srl %[alpha_mask], %[alpha_mask], 16 \n\t" + "and %[alpha_mask], %[alpha_mask], %[temp0] \n\t" + "ext %[temp0], %[alpha_mask], 0, 8 \n\t" + "srl %[alpha_mask], %[alpha_mask], 8 \n\t" + "and %[alpha_mask], %[alpha_mask], %[temp0] \n\t" + : [temp0]"=&r"(temp0), [alpha_mask]"+r"(alpha_mask) + : + ); + + return (alpha_mask != 0xff); +} + +static void MultARGBRow_MIPSdspR2(uint32_t* const ptr, int width, + int inverse) { + int x; + const uint32_t c_00ffffff = 0x00ffffffu; + const uint32_t c_ff000000 = 0xff000000u; + const uint32_t c_8000000 = 0x00800000u; + const uint32_t c_8000080 = 0x00800080u; + for (x = 0; x < width; ++x) { + const uint32_t argb = ptr[x]; + if (argb < 0xff000000u) { // alpha < 255 + if (argb <= 0x00ffffffu) { // alpha == 0 + ptr[x] = 0; + } else { + int temp0, temp1, temp2, temp3, alpha; + __asm__ volatile ( + "srl %[alpha], %[argb], 24 \n\t" + "replv.qb %[temp0], %[alpha] \n\t" + "and %[temp0], %[temp0], %[c_00ffffff] \n\t" + "beqz %[inverse], 0f \n\t" + "divu $zero, %[c_ff000000], %[alpha] \n\t" + "mflo %[temp0] \n\t" + "0: \n\t" + "andi %[temp1], %[argb], 0xff \n\t" + "ext %[temp2], %[argb], 8, 8 \n\t" + "ext %[temp3], %[argb], 16, 8 \n\t" + "mul %[temp1], %[temp1], %[temp0] \n\t" + "mul %[temp2], %[temp2], %[temp0] \n\t" + "mul %[temp3], %[temp3], %[temp0] \n\t" + "precrq.ph.w %[temp1], %[temp2], %[temp1] \n\t" + "addu %[temp3], %[temp3], %[c_8000000] \n\t" + "addu %[temp1], %[temp1], %[c_8000080] \n\t" + "precrq.ph.w %[temp3], %[argb], %[temp3] \n\t" + "precrq.qb.ph %[temp1], %[temp3], %[temp1] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [alpha]"=&r"(alpha) + : [inverse]"r"(inverse), [c_00ffffff]"r"(c_00ffffff), + [c_8000000]"r"(c_8000000), [c_8000080]"r"(c_8000080), + [c_ff000000]"r"(c_ff000000), [argb]"r"(argb) + : "memory", "hi", "lo" + ); + ptr[x] = temp1; + } + } + } +} + +#ifdef WORDS_BIGENDIAN +static void PackARGB_MIPSdspR2(const uint8_t* a, const uint8_t* r, + const uint8_t* g, const uint8_t* b, int len, + uint32_t* out) { + int temp0, temp1, temp2, temp3, offset; + const int rest = len & 1; + const uint32_t* const loop_end = out + len - rest; + const int step = 4; + __asm__ volatile ( + "xor %[offset], %[offset], %[offset] \n\t" + "beq %[loop_end], %[out], 0f \n\t" + "2: \n\t" + "lbux %[temp0], %[offset](%[a]) \n\t" + "lbux %[temp1], %[offset](%[r]) \n\t" + "lbux %[temp2], %[offset](%[g]) \n\t" + "lbux %[temp3], %[offset](%[b]) \n\t" + "ins %[temp1], %[temp0], 16, 16 \n\t" + "ins %[temp3], %[temp2], 16, 16 \n\t" + "addiu %[out], %[out], 4 \n\t" + "precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t" + "sw %[temp0], -4(%[out]) \n\t" + "addu %[offset], %[offset], %[step] \n\t" + "bne %[loop_end], %[out], 2b \n\t" + "0: \n\t" + "beq %[rest], $zero, 1f \n\t" + "lbux %[temp0], %[offset](%[a]) \n\t" + "lbux %[temp1], %[offset](%[r]) \n\t" + "lbux %[temp2], %[offset](%[g]) \n\t" + "lbux %[temp3], %[offset](%[b]) \n\t" + "ins %[temp1], %[temp0], 16, 16 \n\t" + "ins %[temp3], %[temp2], 16, 16 \n\t" + "precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t" + "sw %[temp0], 0(%[out]) \n\t" + "1: \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [offset]"=&r"(offset), [out]"+&r"(out) + : [a]"r"(a), [r]"r"(r), [g]"r"(g), [b]"r"(b), [step]"r"(step), + [loop_end]"r"(loop_end), [rest]"r"(rest) + : "memory" + ); +} +#endif // WORDS_BIGENDIAN + +static void PackRGB_MIPSdspR2(const uint8_t* r, const uint8_t* g, + const uint8_t* b, int len, int step, + uint32_t* out) { + int temp0, temp1, temp2, offset; + const int rest = len & 1; + const int a = 0xff; + const uint32_t* const loop_end = out + len - rest; + __asm__ volatile ( + "xor %[offset], %[offset], %[offset] \n\t" + "beq %[loop_end], %[out], 0f \n\t" + "2: \n\t" + "lbux %[temp0], %[offset](%[r]) \n\t" + "lbux %[temp1], %[offset](%[g]) \n\t" + "lbux %[temp2], %[offset](%[b]) \n\t" + "ins %[temp0], %[a], 16, 16 \n\t" + "ins %[temp2], %[temp1], 16, 16 \n\t" + "addiu %[out], %[out], 4 \n\t" + "precr.qb.ph %[temp0], %[temp0], %[temp2] \n\t" + "sw %[temp0], -4(%[out]) \n\t" + "addu %[offset], %[offset], %[step] \n\t" + "bne %[loop_end], %[out], 2b \n\t" + "0: \n\t" + "beq %[rest], $zero, 1f \n\t" + "lbux %[temp0], %[offset](%[r]) \n\t" + "lbux %[temp1], %[offset](%[g]) \n\t" + "lbux %[temp2], %[offset](%[b]) \n\t" + "ins %[temp0], %[a], 16, 16 \n\t" + "ins %[temp2], %[temp1], 16, 16 \n\t" + "precr.qb.ph %[temp0], %[temp0], %[temp2] \n\t" + "sw %[temp0], 0(%[out]) \n\t" + "1: \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [offset]"=&r"(offset), [out]"+&r"(out) + : [a]"r"(a), [r]"r"(r), [g]"r"(g), [b]"r"(b), [step]"r"(step), + [loop_end]"r"(loop_end), [rest]"r"(rest) + : "memory" + ); +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPInitAlphaProcessingMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingMIPSdspR2(void) { + WebPDispatchAlpha = DispatchAlpha_MIPSdspR2; + WebPMultARGBRow = MultARGBRow_MIPSdspR2; +#ifdef WORDS_BIGENDIAN + WebPPackARGB = PackARGB_MIPSdspR2; +#endif + WebPPackRGB = PackRGB_MIPSdspR2; +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 diff --git a/internal/libwebp-1.2.1/src/dsp/alpha_processing_neon.c b/internal/libwebp-1.2.1/src/dsp/alpha_processing_neon.c new file mode 100644 index 0000000..9e0ace9 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/alpha_processing_neon.c @@ -0,0 +1,192 @@ +// 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 "src/dsp/dsp.h" + +#if defined(WEBP_USE_NEON) + +#include "src/dsp/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* WEBP_RESTRICT alpha, + int alpha_stride, int width, int height, + uint8_t* WEBP_RESTRICT 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* WEBP_RESTRICT alpha, + int alpha_stride, int width, int height, + uint32_t* WEBP_RESTRICT 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* WEBP_RESTRICT argb, int argb_stride, + int width, int height, + uint8_t* WEBP_RESTRICT 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* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT 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/internal/libwebp-1.2.1/src/dsp/alpha_processing_sse2.c b/internal/libwebp-1.2.1/src/dsp/alpha_processing_sse2.c new file mode 100644 index 0000000..a5f8c9f --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/alpha_processing_sse2.c @@ -0,0 +1,367 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Utilities for processing transparent channel. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) +#include + +//------------------------------------------------------------------------------ + +static int DispatchAlpha_SSE2(const uint8_t* WEBP_RESTRICT alpha, + int alpha_stride, int width, int height, + uint8_t* WEBP_RESTRICT dst, int dst_stride) { + // alpha_and stores an 'and' operation of all the alpha[] values. The final + // value is not 0xff if any of the alpha[] is not equal to 0xff. + uint32_t alpha_and = 0xff; + int i, j; + const __m128i zero = _mm_setzero_si128(); + const __m128i rgb_mask = _mm_set1_epi32(0xffffff00u); // to preserve RGB + const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u); + __m128i all_alphas = all_0xff; + + // We must be able to access 3 extra bytes after the last written byte + // 'dst[4 * width - 4]', because we don't know if alpha is the first or the + // last byte of the quadruplet. + const int limit = (width - 1) & ~7; + + for (j = 0; j < height; ++j) { + __m128i* out = (__m128i*)dst; + for (i = 0; i < limit; i += 8) { + // load 8 alpha bytes + const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[i]); + const __m128i a1 = _mm_unpacklo_epi8(a0, zero); + const __m128i a2_lo = _mm_unpacklo_epi16(a1, zero); + const __m128i a2_hi = _mm_unpackhi_epi16(a1, zero); + // load 8 dst pixels (32 bytes) + const __m128i b0_lo = _mm_loadu_si128(out + 0); + const __m128i b0_hi = _mm_loadu_si128(out + 1); + // mask dst alpha values + const __m128i b1_lo = _mm_and_si128(b0_lo, rgb_mask); + const __m128i b1_hi = _mm_and_si128(b0_hi, rgb_mask); + // combine + const __m128i b2_lo = _mm_or_si128(b1_lo, a2_lo); + const __m128i b2_hi = _mm_or_si128(b1_hi, a2_hi); + // store + _mm_storeu_si128(out + 0, b2_lo); + _mm_storeu_si128(out + 1, b2_hi); + // accumulate eight alpha 'and' in parallel + all_alphas = _mm_and_si128(all_alphas, a0); + out += 2; + } + for (; i < width; ++i) { + const uint32_t alpha_value = alpha[i]; + dst[4 * i] = alpha_value; + alpha_and &= alpha_value; + } + alpha += alpha_stride; + dst += dst_stride; + } + // Combine the eight alpha 'and' into a 8-bit mask. + alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff)); + return (alpha_and != 0xff); +} + +static void DispatchAlphaToGreen_SSE2(const uint8_t* WEBP_RESTRICT alpha, + int alpha_stride, int width, int height, + uint32_t* WEBP_RESTRICT dst, + int dst_stride) { + int i, j; + const __m128i zero = _mm_setzero_si128(); + const int limit = width & ~15; + for (j = 0; j < height; ++j) { + for (i = 0; i < limit; i += 16) { // process 16 alpha bytes + const __m128i a0 = _mm_loadu_si128((const __m128i*)&alpha[i]); + const __m128i a1 = _mm_unpacklo_epi8(zero, a0); // note the 'zero' first! + const __m128i b1 = _mm_unpackhi_epi8(zero, a0); + const __m128i a2_lo = _mm_unpacklo_epi16(a1, zero); + const __m128i b2_lo = _mm_unpacklo_epi16(b1, zero); + const __m128i a2_hi = _mm_unpackhi_epi16(a1, zero); + const __m128i b2_hi = _mm_unpackhi_epi16(b1, zero); + _mm_storeu_si128((__m128i*)&dst[i + 0], a2_lo); + _mm_storeu_si128((__m128i*)&dst[i + 4], a2_hi); + _mm_storeu_si128((__m128i*)&dst[i + 8], b2_lo); + _mm_storeu_si128((__m128i*)&dst[i + 12], b2_hi); + } + for (; i < width; ++i) dst[i] = alpha[i] << 8; + alpha += alpha_stride; + dst += dst_stride; + } +} + +static int ExtractAlpha_SSE2(const uint8_t* WEBP_RESTRICT argb, int argb_stride, + int width, int height, + uint8_t* WEBP_RESTRICT alpha, int alpha_stride) { + // alpha_and stores an 'and' operation of all the alpha[] values. The final + // value is not 0xff if any of the alpha[] is not equal to 0xff. + uint32_t alpha_and = 0xff; + int i, j; + const __m128i a_mask = _mm_set1_epi32(0xffu); // to preserve alpha + const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u); + __m128i all_alphas = all_0xff; + + // We must be able to access 3 extra bytes after the last written byte + // 'src[4 * width - 4]', because we don't know if alpha is the first or the + // last byte of the quadruplet. + const int limit = (width - 1) & ~7; + + for (j = 0; j < height; ++j) { + const __m128i* src = (const __m128i*)argb; + for (i = 0; i < limit; i += 8) { + // load 32 argb bytes + const __m128i a0 = _mm_loadu_si128(src + 0); + const __m128i a1 = _mm_loadu_si128(src + 1); + const __m128i b0 = _mm_and_si128(a0, a_mask); + const __m128i b1 = _mm_and_si128(a1, a_mask); + const __m128i c0 = _mm_packs_epi32(b0, b1); + const __m128i d0 = _mm_packus_epi16(c0, c0); + // store + _mm_storel_epi64((__m128i*)&alpha[i], d0); + // accumulate eight alpha 'and' in parallel + all_alphas = _mm_and_si128(all_alphas, d0); + src += 2; + } + for (; i < width; ++i) { + const uint32_t alpha_value = argb[4 * i]; + alpha[i] = alpha_value; + alpha_and &= alpha_value; + } + argb += argb_stride; + alpha += alpha_stride; + } + // Combine the eight alpha 'and' into a 8-bit mask. + alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff)); + return (alpha_and == 0xff); +} + +//------------------------------------------------------------------------------ +// Non-dither premultiplied modes + +#define MULTIPLIER(a) ((a) * 0x8081) +#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 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_SSE2(uint8_t* rgba, int alpha_first, + int w, int h, int stride) { + const __m128i zero = _mm_setzero_si128(); + 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) { + for (i = 0; i + kSpan <= w; i += kSpan) { + APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(2, 3, 3, 3)); + } + } else { + for (i = 0; i + kSpan <= w; i += kSpan) { + APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(0, 0, 0, 1)); + } + } + // 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 MULTIPLIER +#undef PREMULTIPLY + +//------------------------------------------------------------------------------ +// Alpha detection + +static int HasAlpha8b_SSE2(const uint8_t* src, int length) { + const __m128i all_0xff = _mm_set1_epi8((char)0xff); + int i = 0; + for (; i + 16 <= length; i += 16) { + const __m128i v = _mm_loadu_si128((const __m128i*)(src + i)); + const __m128i bits = _mm_cmpeq_epi8(v, all_0xff); + const int mask = _mm_movemask_epi8(bits); + if (mask != 0xffff) return 1; + } + for (; i < length; ++i) if (src[i] != 0xff) return 1; + return 0; +} + +static int HasAlpha32b_SSE2(const uint8_t* src, int length) { + const __m128i alpha_mask = _mm_set1_epi32(0xff); + const __m128i all_0xff = _mm_set1_epi8((char)0xff); + int i = 0; + // We don't know if we can access the last 3 bytes after the last alpha + // value 'src[4 * length - 4]' (because we don't know if alpha is the first + // or the last byte of the quadruplet). Hence the '-3' protection below. + length = length * 4 - 3; // size in bytes + for (; i + 64 <= length; i += 64) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0)); + const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 16)); + const __m128i a2 = _mm_loadu_si128((const __m128i*)(src + i + 32)); + const __m128i a3 = _mm_loadu_si128((const __m128i*)(src + i + 48)); + const __m128i b0 = _mm_and_si128(a0, alpha_mask); + const __m128i b1 = _mm_and_si128(a1, alpha_mask); + const __m128i b2 = _mm_and_si128(a2, alpha_mask); + const __m128i b3 = _mm_and_si128(a3, alpha_mask); + const __m128i c0 = _mm_packs_epi32(b0, b1); + const __m128i c1 = _mm_packs_epi32(b2, b3); + const __m128i d = _mm_packus_epi16(c0, c1); + const __m128i bits = _mm_cmpeq_epi8(d, all_0xff); + const int mask = _mm_movemask_epi8(bits); + if (mask != 0xffff) return 1; + } + for (; i + 32 <= length; i += 32) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0)); + const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 16)); + const __m128i b0 = _mm_and_si128(a0, alpha_mask); + const __m128i b1 = _mm_and_si128(a1, alpha_mask); + const __m128i c = _mm_packs_epi32(b0, b1); + const __m128i d = _mm_packus_epi16(c, c); + const __m128i bits = _mm_cmpeq_epi8(d, all_0xff); + const int mask = _mm_movemask_epi8(bits); + if (mask != 0xffff) return 1; + } + for (; i <= length; i += 4) if (src[i] != 0xff) return 1; + return 0; +} + +static void AlphaReplace_SSE2(uint32_t* src, int length, uint32_t color) { + const __m128i m_color = _mm_set1_epi32(color); + const __m128i zero = _mm_setzero_si128(); + int i = 0; + for (; i + 8 <= length; i += 8) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0)); + const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 4)); + const __m128i b0 = _mm_srai_epi32(a0, 24); + const __m128i b1 = _mm_srai_epi32(a1, 24); + const __m128i c0 = _mm_cmpeq_epi32(b0, zero); + const __m128i c1 = _mm_cmpeq_epi32(b1, zero); + const __m128i d0 = _mm_and_si128(c0, m_color); + const __m128i d1 = _mm_and_si128(c1, m_color); + const __m128i e0 = _mm_andnot_si128(c0, a0); + const __m128i e1 = _mm_andnot_si128(c1, a1); + _mm_storeu_si128((__m128i*)(src + i + 0), _mm_or_si128(d0, e0)); + _mm_storeu_si128((__m128i*)(src + i + 4), _mm_or_si128(d1, e1)); + } + for (; i < length; ++i) if ((src[i] >> 24) == 0) src[i] = color; +} + +// ----------------------------------------------------------------------------- +// Apply alpha value to rows + +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 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) WebPMultARGBRow_C(ptr + x, width, inverse); +} + +static void MultRow_SSE2(uint8_t* WEBP_RESTRICT const ptr, + const uint8_t* WEBP_RESTRICT const alpha, + int width, int inverse) { + int x = 0; + if (!inverse) { + const __m128i zero = _mm_setzero_si128(); + 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 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; + if (width > 0) WebPMultRow_C(ptr + x, alpha + x, width, inverse); +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPInitAlphaProcessingSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) { + WebPMultARGBRow = MultARGBRow_SSE2; + WebPMultRow = MultRow_SSE2; + WebPApplyAlphaMultiply = ApplyAlphaMultiply_SSE2; + WebPDispatchAlpha = DispatchAlpha_SSE2; + WebPDispatchAlphaToGreen = DispatchAlphaToGreen_SSE2; + WebPExtractAlpha = ExtractAlpha_SSE2; + + WebPHasAlpha8b = HasAlpha8b_SSE2; + WebPHasAlpha32b = HasAlpha32b_SSE2; + WebPAlphaReplace = AlphaReplace_SSE2; +} + +#else // !WEBP_USE_SSE2 + +WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingSSE2) + +#endif // WEBP_USE_SSE2 diff --git a/internal/libwebp-1.2.1/src/dsp/alpha_processing_sse41.c b/internal/libwebp-1.2.1/src/dsp/alpha_processing_sse41.c new file mode 100644 index 0000000..cdf877c --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/alpha_processing_sse41.c @@ -0,0 +1,92 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Utilities for processing transparent channel, SSE4.1 variant. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE41) + +#include + +//------------------------------------------------------------------------------ + +static int ExtractAlpha_SSE41(const uint8_t* WEBP_RESTRICT argb, + int argb_stride, int width, int height, + uint8_t* WEBP_RESTRICT alpha, int alpha_stride) { + // alpha_and stores an 'and' operation of all the alpha[] values. The final + // value is not 0xff if any of the alpha[] is not equal to 0xff. + uint32_t alpha_and = 0xff; + int i, j; + const __m128i all_0xff = _mm_set1_epi32(~0u); + __m128i all_alphas = all_0xff; + + // We must be able to access 3 extra bytes after the last written byte + // 'src[4 * width - 4]', because we don't know if alpha is the first or the + // last byte of the quadruplet. + const int limit = (width - 1) & ~15; + const __m128i kCstAlpha0 = _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 12, 8, 4, 0); + const __m128i kCstAlpha1 = _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, + 12, 8, 4, 0, -1, -1, -1, -1); + const __m128i kCstAlpha2 = _mm_set_epi8(-1, -1, -1, -1, 12, 8, 4, 0, + -1, -1, -1, -1, -1, -1, -1, -1); + const __m128i kCstAlpha3 = _mm_set_epi8(12, 8, 4, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1); + for (j = 0; j < height; ++j) { + const __m128i* src = (const __m128i*)argb; + for (i = 0; i < limit; i += 16) { + // load 64 argb bytes + const __m128i a0 = _mm_loadu_si128(src + 0); + const __m128i a1 = _mm_loadu_si128(src + 1); + const __m128i a2 = _mm_loadu_si128(src + 2); + const __m128i a3 = _mm_loadu_si128(src + 3); + const __m128i b0 = _mm_shuffle_epi8(a0, kCstAlpha0); + const __m128i b1 = _mm_shuffle_epi8(a1, kCstAlpha1); + const __m128i b2 = _mm_shuffle_epi8(a2, kCstAlpha2); + const __m128i b3 = _mm_shuffle_epi8(a3, kCstAlpha3); + const __m128i c0 = _mm_or_si128(b0, b1); + const __m128i c1 = _mm_or_si128(b2, b3); + const __m128i d0 = _mm_or_si128(c0, c1); + // store + _mm_storeu_si128((__m128i*)&alpha[i], d0); + // accumulate sixteen alpha 'and' in parallel + all_alphas = _mm_and_si128(all_alphas, d0); + src += 4; + } + for (; i < width; ++i) { + const uint32_t alpha_value = argb[4 * i]; + alpha[i] = alpha_value; + alpha_and &= alpha_value; + } + argb += argb_stride; + alpha += alpha_stride; + } + // Combine the sixteen alpha 'and' into an 8-bit mask. + alpha_and |= 0xff00u; // pretend the upper bits [8..15] were tested ok. + alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff)); + return (alpha_and == 0xffffu); +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPInitAlphaProcessingSSE41(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE41(void) { + WebPExtractAlpha = ExtractAlpha_SSE41; +} + +#else // !WEBP_USE_SSE41 + +WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingSSE41) + +#endif // WEBP_USE_SSE41 diff --git a/internal/libwebp-1.2.1/src/dsp/common_sse2.h b/internal/libwebp-1.2.1/src/dsp/common_sse2.h new file mode 100644 index 0000000..e9f1ebf --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/common_sse2.h @@ -0,0 +1,194 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE2 code common to several files. +// +// Author: Vincent Rabaud (vrabaud@google.com) + +#ifndef WEBP_DSP_COMMON_SSE2_H_ +#define WEBP_DSP_COMMON_SSE2_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WEBP_USE_SSE2) + +#include + +//------------------------------------------------------------------------------ +// Quite useful macro for debugging. Left here for convenience. + +#if 0 +#include +static WEBP_INLINE void PrintReg(const __m128i r, const char* const name, + int size) { + int n; + union { + __m128i r; + uint8_t i8[16]; + uint16_t i16[8]; + uint32_t i32[4]; + uint64_t i64[2]; + } tmp; + tmp.r = r; + fprintf(stderr, "%s\t: ", name); + if (size == 8) { + for (n = 0; n < 16; ++n) fprintf(stderr, "%.2x ", tmp.i8[n]); + } else if (size == 16) { + for (n = 0; n < 8; ++n) fprintf(stderr, "%.4x ", tmp.i16[n]); + } else if (size == 32) { + for (n = 0; n < 4; ++n) fprintf(stderr, "%.8x ", tmp.i32[n]); + } else { + for (n = 0; n < 2; ++n) fprintf(stderr, "%.16lx ", tmp.i64[n]); + } + fprintf(stderr, "\n"); +} +#endif + +//------------------------------------------------------------------------------ +// Math functions. + +// Return the sum of all the 8b in the register. +static WEBP_INLINE int VP8HorizontalAdd8b(const __m128i* const a) { + const __m128i zero = _mm_setzero_si128(); + const __m128i sad8x2 = _mm_sad_epu8(*a, zero); + // sum the two sads: sad8x2[0:1] + sad8x2[8:9] + const __m128i sum = _mm_add_epi32(sad8x2, _mm_shuffle_epi32(sad8x2, 2)); + return _mm_cvtsi128_si32(sum); +} + +// Transpose two 4x4 16b matrices horizontally stored in registers. +static WEBP_INLINE void VP8Transpose_2_4x4_16b( + const __m128i* const in0, const __m128i* const in1, + const __m128i* const in2, const __m128i* const in3, __m128i* const out0, + __m128i* const out1, __m128i* const out2, __m128i* const out3) { + // Transpose the two 4x4. + // a00 a01 a02 a03 b00 b01 b02 b03 + // a10 a11 a12 a13 b10 b11 b12 b13 + // a20 a21 a22 a23 b20 b21 b22 b23 + // a30 a31 a32 a33 b30 b31 b32 b33 + const __m128i transpose0_0 = _mm_unpacklo_epi16(*in0, *in1); + const __m128i transpose0_1 = _mm_unpacklo_epi16(*in2, *in3); + const __m128i transpose0_2 = _mm_unpackhi_epi16(*in0, *in1); + const __m128i transpose0_3 = _mm_unpackhi_epi16(*in2, *in3); + // a00 a10 a01 a11 a02 a12 a03 a13 + // a20 a30 a21 a31 a22 a32 a23 a33 + // b00 b10 b01 b11 b02 b12 b03 b13 + // b20 b30 b21 b31 b22 b32 b23 b33 + const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1); + const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3); + const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1); + const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3); + // a00 a10 a20 a30 a01 a11 a21 a31 + // b00 b10 b20 b30 b01 b11 b21 b31 + // a02 a12 a22 a32 a03 a13 a23 a33 + // b02 b12 a22 b32 b03 b13 b23 b33 + *out0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1); + *out1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1); + *out2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3); + *out3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3); + // a00 a10 a20 a30 b00 b10 b20 b30 + // a01 a11 a21 a31 b01 b11 b21 b31 + // a02 a12 a22 a32 b02 b12 b22 b32 + // 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_SSE2( + __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_SSE2(__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 +} // extern "C" +#endif + +#endif // WEBP_DSP_COMMON_SSE2_H_ diff --git a/internal/libwebp-1.2.1/src/dsp/common_sse41.h b/internal/libwebp-1.2.1/src/dsp/common_sse41.h new file mode 100644 index 0000000..2f173c0 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/common_sse41.h @@ -0,0 +1,132 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE4 code common to several files. +// +// Author: Vincent Rabaud (vrabaud@google.com) + +#ifndef WEBP_DSP_COMMON_SSE41_H_ +#define WEBP_DSP_COMMON_SSE41_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WEBP_USE_SSE41) +#include + +//------------------------------------------------------------------------------ +// Channel mixing. +// Shuffles the input buffer as A0 0 0 A1 0 0 A2 ... +#define WEBP_SSE41_SHUFF(OUT, IN0, IN1) \ + OUT##0 = _mm_shuffle_epi8(*IN0, shuff0); \ + OUT##1 = _mm_shuffle_epi8(*IN0, shuff1); \ + OUT##2 = _mm_shuffle_epi8(*IN0, shuff2); \ + OUT##3 = _mm_shuffle_epi8(*IN1, shuff0); \ + OUT##4 = _mm_shuffle_epi8(*IN1, shuff1); \ + OUT##5 = _mm_shuffle_epi8(*IN1, shuff2); + +// 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_SSE41( + __m128i* const in0, __m128i* const in1, __m128i* const in2, + __m128i* const in3, __m128i* const in4, __m128i* const in5) { + __m128i R0, R1, R2, R3, R4, R5; + __m128i G0, G1, G2, G3, G4, G5; + __m128i B0, B1, B2, B3, B4, B5; + + // Process R. + { + const __m128i shuff0 = _mm_set_epi8( + 5, -1, -1, 4, -1, -1, 3, -1, -1, 2, -1, -1, 1, -1, -1, 0); + const __m128i shuff1 = _mm_set_epi8( + -1, 10, -1, -1, 9, -1, -1, 8, -1, -1, 7, -1, -1, 6, -1, -1); + const __m128i shuff2 = _mm_set_epi8( + -1, -1, 15, -1, -1, 14, -1, -1, 13, -1, -1, 12, -1, -1, 11, -1); + WEBP_SSE41_SHUFF(R, in0, in1) + } + + // Process G. + { + // Same as before, just shifted to the left by one and including the right + // padding. + const __m128i shuff0 = _mm_set_epi8( + -1, -1, 4, -1, -1, 3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1); + const __m128i shuff1 = _mm_set_epi8( + 10, -1, -1, 9, -1, -1, 8, -1, -1, 7, -1, -1, 6, -1, -1, 5); + const __m128i shuff2 = _mm_set_epi8( + -1, 15, -1, -1, 14, -1, -1, 13, -1, -1, 12, -1, -1, 11, -1, -1); + WEBP_SSE41_SHUFF(G, in2, in3) + } + + // Process B. + { + const __m128i shuff0 = _mm_set_epi8( + -1, 4, -1, -1, 3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1, -1); + const __m128i shuff1 = _mm_set_epi8( + -1, -1, 9, -1, -1, 8, -1, -1, 7, -1, -1, 6, -1, -1, 5, -1); + const __m128i shuff2 = _mm_set_epi8( + 15, -1, -1, 14, -1, -1, 13, -1, -1, 12, -1, -1, 11, -1, -1, 10); + WEBP_SSE41_SHUFF(B, in4, in5) + } + + // OR the different channels. + { + const __m128i RG0 = _mm_or_si128(R0, G0); + const __m128i RG1 = _mm_or_si128(R1, G1); + const __m128i RG2 = _mm_or_si128(R2, G2); + const __m128i RG3 = _mm_or_si128(R3, G3); + const __m128i RG4 = _mm_or_si128(R4, G4); + const __m128i RG5 = _mm_or_si128(R5, G5); + *in0 = _mm_or_si128(RG0, B0); + *in1 = _mm_or_si128(RG1, B1); + *in2 = _mm_or_si128(RG2, B2); + *in3 = _mm_or_si128(RG3, B3); + *in4 = _mm_or_si128(RG4, B4); + *in5 = _mm_or_si128(RG5, B5); + } +} + +#undef WEBP_SSE41_SHUFF + +// Convert four packed four-channel buffers like argbargbargbargb... into the +// split channels aaaaa ... rrrr ... gggg .... bbbbb ...... +static WEBP_INLINE void VP8L32bToPlanar_SSE41(__m128i* const in0, + __m128i* const in1, + __m128i* const in2, + __m128i* const in3) { + // aaaarrrrggggbbbb + const __m128i shuff0 = + _mm_set_epi8(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0); + const __m128i A0 = _mm_shuffle_epi8(*in0, shuff0); + const __m128i A1 = _mm_shuffle_epi8(*in1, shuff0); + const __m128i A2 = _mm_shuffle_epi8(*in2, shuff0); + const __m128i A3 = _mm_shuffle_epi8(*in3, shuff0); + // A0A1R0R1 + // G0G1B0B1 + // A2A3R2R3 + // G0G1B0B1 + const __m128i B0 = _mm_unpacklo_epi32(A0, A1); + const __m128i B1 = _mm_unpackhi_epi32(A0, A1); + const __m128i B2 = _mm_unpacklo_epi32(A2, A3); + const __m128i B3 = _mm_unpackhi_epi32(A2, A3); + *in3 = _mm_unpacklo_epi64(B0, B2); + *in2 = _mm_unpackhi_epi64(B0, B2); + *in1 = _mm_unpacklo_epi64(B1, B3); + *in0 = _mm_unpackhi_epi64(B1, B3); +} + +#endif // WEBP_USE_SSE41 + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_DSP_COMMON_SSE41_H_ diff --git a/internal/libwebp-1.2.1/src/dsp/cost.c b/internal/libwebp-1.2.1/src/dsp/cost.c new file mode 100644 index 0000000..460ec4f --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/cost.c @@ -0,0 +1,411 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" +#include "src/enc/cost_enc.h" + +//------------------------------------------------------------------------------ +// Boolean-cost cost table + +const uint16_t VP8EntropyCost[256] = { + 1792, 1792, 1792, 1536, 1536, 1408, 1366, 1280, 1280, 1216, + 1178, 1152, 1110, 1076, 1061, 1024, 1024, 992, 968, 951, + 939, 911, 896, 878, 871, 854, 838, 820, 811, 794, + 786, 768, 768, 752, 740, 732, 720, 709, 704, 690, + 683, 672, 666, 655, 647, 640, 631, 622, 615, 607, + 598, 592, 586, 576, 572, 564, 559, 555, 547, 541, + 534, 528, 522, 512, 512, 504, 500, 494, 488, 483, + 477, 473, 467, 461, 458, 452, 448, 443, 438, 434, + 427, 424, 419, 415, 410, 406, 403, 399, 394, 390, + 384, 384, 377, 374, 370, 366, 362, 359, 355, 351, + 347, 342, 342, 336, 333, 330, 326, 323, 320, 316, + 312, 308, 305, 302, 299, 296, 293, 288, 287, 283, + 280, 277, 274, 272, 268, 266, 262, 256, 256, 256, + 251, 248, 245, 242, 240, 237, 234, 232, 228, 226, + 223, 221, 218, 216, 214, 211, 208, 205, 203, 201, + 198, 196, 192, 191, 188, 187, 183, 181, 179, 176, + 175, 171, 171, 168, 165, 163, 160, 159, 156, 154, + 152, 150, 148, 146, 144, 142, 139, 138, 135, 133, + 131, 128, 128, 125, 123, 121, 119, 117, 115, 113, + 111, 110, 107, 105, 103, 102, 100, 98, 96, 94, + 92, 91, 89, 86, 86, 83, 82, 80, 77, 76, + 74, 73, 71, 69, 67, 66, 64, 63, 61, 59, + 57, 55, 54, 52, 51, 49, 47, 46, 44, 43, + 41, 40, 38, 36, 35, 33, 32, 30, 29, 27, + 25, 24, 22, 21, 19, 18, 16, 15, 13, 12, + 10, 9, 7, 6, 4, 3 +}; + +//------------------------------------------------------------------------------ +// Level cost tables + +// fixed costs for coding levels, deduce from the coding tree. +// This is only the part that doesn't depend on the probability state. +const uint16_t VP8LevelFixedCosts[MAX_LEVEL + 1] = { + 0, 256, 256, 256, 256, 432, 618, 630, + 731, 640, 640, 828, 901, 948, 1021, 1101, + 1174, 1221, 1294, 1042, 1085, 1115, 1158, 1202, + 1245, 1275, 1318, 1337, 1380, 1410, 1453, 1497, + 1540, 1570, 1613, 1280, 1295, 1317, 1332, 1358, + 1373, 1395, 1410, 1454, 1469, 1491, 1506, 1532, + 1547, 1569, 1584, 1601, 1616, 1638, 1653, 1679, + 1694, 1716, 1731, 1775, 1790, 1812, 1827, 1853, + 1868, 1890, 1905, 1727, 1733, 1742, 1748, 1759, + 1765, 1774, 1780, 1800, 1806, 1815, 1821, 1832, + 1838, 1847, 1853, 1878, 1884, 1893, 1899, 1910, + 1916, 1925, 1931, 1951, 1957, 1966, 1972, 1983, + 1989, 1998, 2004, 2027, 2033, 2042, 2048, 2059, + 2065, 2074, 2080, 2100, 2106, 2115, 2121, 2132, + 2138, 2147, 2153, 2178, 2184, 2193, 2199, 2210, + 2216, 2225, 2231, 2251, 2257, 2266, 2272, 2283, + 2289, 2298, 2304, 2168, 2174, 2183, 2189, 2200, + 2206, 2215, 2221, 2241, 2247, 2256, 2262, 2273, + 2279, 2288, 2294, 2319, 2325, 2334, 2340, 2351, + 2357, 2366, 2372, 2392, 2398, 2407, 2413, 2424, + 2430, 2439, 2445, 2468, 2474, 2483, 2489, 2500, + 2506, 2515, 2521, 2541, 2547, 2556, 2562, 2573, + 2579, 2588, 2594, 2619, 2625, 2634, 2640, 2651, + 2657, 2666, 2672, 2692, 2698, 2707, 2713, 2724, + 2730, 2739, 2745, 2540, 2546, 2555, 2561, 2572, + 2578, 2587, 2593, 2613, 2619, 2628, 2634, 2645, + 2651, 2660, 2666, 2691, 2697, 2706, 2712, 2723, + 2729, 2738, 2744, 2764, 2770, 2779, 2785, 2796, + 2802, 2811, 2817, 2840, 2846, 2855, 2861, 2872, + 2878, 2887, 2893, 2913, 2919, 2928, 2934, 2945, + 2951, 2960, 2966, 2991, 2997, 3006, 3012, 3023, + 3029, 3038, 3044, 3064, 3070, 3079, 3085, 3096, + 3102, 3111, 3117, 2981, 2987, 2996, 3002, 3013, + 3019, 3028, 3034, 3054, 3060, 3069, 3075, 3086, + 3092, 3101, 3107, 3132, 3138, 3147, 3153, 3164, + 3170, 3179, 3185, 3205, 3211, 3220, 3226, 3237, + 3243, 3252, 3258, 3281, 3287, 3296, 3302, 3313, + 3319, 3328, 3334, 3354, 3360, 3369, 3375, 3386, + 3392, 3401, 3407, 3432, 3438, 3447, 3453, 3464, + 3470, 3479, 3485, 3505, 3511, 3520, 3526, 3537, + 3543, 3552, 3558, 2816, 2822, 2831, 2837, 2848, + 2854, 2863, 2869, 2889, 2895, 2904, 2910, 2921, + 2927, 2936, 2942, 2967, 2973, 2982, 2988, 2999, + 3005, 3014, 3020, 3040, 3046, 3055, 3061, 3072, + 3078, 3087, 3093, 3116, 3122, 3131, 3137, 3148, + 3154, 3163, 3169, 3189, 3195, 3204, 3210, 3221, + 3227, 3236, 3242, 3267, 3273, 3282, 3288, 3299, + 3305, 3314, 3320, 3340, 3346, 3355, 3361, 3372, + 3378, 3387, 3393, 3257, 3263, 3272, 3278, 3289, + 3295, 3304, 3310, 3330, 3336, 3345, 3351, 3362, + 3368, 3377, 3383, 3408, 3414, 3423, 3429, 3440, + 3446, 3455, 3461, 3481, 3487, 3496, 3502, 3513, + 3519, 3528, 3534, 3557, 3563, 3572, 3578, 3589, + 3595, 3604, 3610, 3630, 3636, 3645, 3651, 3662, + 3668, 3677, 3683, 3708, 3714, 3723, 3729, 3740, + 3746, 3755, 3761, 3781, 3787, 3796, 3802, 3813, + 3819, 3828, 3834, 3629, 3635, 3644, 3650, 3661, + 3667, 3676, 3682, 3702, 3708, 3717, 3723, 3734, + 3740, 3749, 3755, 3780, 3786, 3795, 3801, 3812, + 3818, 3827, 3833, 3853, 3859, 3868, 3874, 3885, + 3891, 3900, 3906, 3929, 3935, 3944, 3950, 3961, + 3967, 3976, 3982, 4002, 4008, 4017, 4023, 4034, + 4040, 4049, 4055, 4080, 4086, 4095, 4101, 4112, + 4118, 4127, 4133, 4153, 4159, 4168, 4174, 4185, + 4191, 4200, 4206, 4070, 4076, 4085, 4091, 4102, + 4108, 4117, 4123, 4143, 4149, 4158, 4164, 4175, + 4181, 4190, 4196, 4221, 4227, 4236, 4242, 4253, + 4259, 4268, 4274, 4294, 4300, 4309, 4315, 4326, + 4332, 4341, 4347, 4370, 4376, 4385, 4391, 4402, + 4408, 4417, 4423, 4443, 4449, 4458, 4464, 4475, + 4481, 4490, 4496, 4521, 4527, 4536, 4542, 4553, + 4559, 4568, 4574, 4594, 4600, 4609, 4615, 4626, + 4632, 4641, 4647, 3515, 3521, 3530, 3536, 3547, + 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620, + 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698, + 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771, + 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847, + 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920, + 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998, + 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071, + 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988, + 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061, + 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139, + 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212, + 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288, + 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361, + 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439, + 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512, + 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360, + 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433, + 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511, + 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584, + 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660, + 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733, + 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811, + 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884, + 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801, + 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874, + 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952, + 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025, + 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101, + 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174, + 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252, + 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325, + 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636, + 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709, + 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787, + 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860, + 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936, + 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009, + 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087, + 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160, + 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077, + 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150, + 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228, + 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301, + 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377, + 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450, + 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528, + 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601, + 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449, + 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522, + 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600, + 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673, + 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749, + 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822, + 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900, + 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973, + 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890, + 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963, + 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041, + 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114, + 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190, + 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263, + 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341, + 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414, + 6420, 6429, 6435, 3515, 3521, 3530, 3536, 3547, + 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620, + 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698, + 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771, + 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847, + 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920, + 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998, + 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071, + 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988, + 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061, + 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139, + 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212, + 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288, + 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361, + 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439, + 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512, + 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360, + 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433, + 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511, + 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584, + 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660, + 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733, + 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811, + 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884, + 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801, + 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874, + 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952, + 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025, + 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101, + 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174, + 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252, + 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325, + 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636, + 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709, + 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787, + 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860, + 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936, + 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009, + 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087, + 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160, + 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077, + 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150, + 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228, + 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301, + 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377, + 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450, + 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528, + 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601, + 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449, + 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522, + 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600, + 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673, + 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749, + 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822, + 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900, + 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973, + 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890, + 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963, + 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041, + 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114, + 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190, + 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263, + 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341, + 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414, + 6420, 6429, 6435, 5303, 5309, 5318, 5324, 5335, + 5341, 5350, 5356, 5376, 5382, 5391, 5397, 5408, + 5414, 5423, 5429, 5454, 5460, 5469, 5475, 5486, + 5492, 5501, 5507, 5527, 5533, 5542, 5548, 5559, + 5565, 5574, 5580, 5603, 5609, 5618, 5624, 5635, + 5641, 5650, 5656, 5676, 5682, 5691, 5697, 5708, + 5714, 5723, 5729, 5754, 5760, 5769, 5775, 5786, + 5792, 5801, 5807, 5827, 5833, 5842, 5848, 5859, + 5865, 5874, 5880, 5744, 5750, 5759, 5765, 5776, + 5782, 5791, 5797, 5817, 5823, 5832, 5838, 5849, + 5855, 5864, 5870, 5895, 5901, 5910, 5916, 5927, + 5933, 5942, 5948, 5968, 5974, 5983, 5989, 6000, + 6006, 6015, 6021, 6044, 6050, 6059, 6065, 6076, + 6082, 6091, 6097, 6117, 6123, 6132, 6138, 6149, + 6155, 6164, 6170, 6195, 6201, 6210, 6216, 6227, + 6233, 6242, 6248, 6268, 6274, 6283, 6289, 6300, + 6306, 6315, 6321, 6116, 6122, 6131, 6137, 6148, + 6154, 6163, 6169, 6189, 6195, 6204, 6210, 6221, + 6227, 6236, 6242, 6267, 6273, 6282, 6288, 6299, + 6305, 6314, 6320, 6340, 6346, 6355, 6361, 6372, + 6378, 6387, 6393, 6416, 6422, 6431, 6437, 6448, + 6454, 6463, 6469, 6489, 6495, 6504, 6510, 6521, + 6527, 6536, 6542, 6567, 6573, 6582, 6588, 6599, + 6605, 6614, 6620, 6640, 6646, 6655, 6661, 6672, + 6678, 6687, 6693, 6557, 6563, 6572, 6578, 6589, + 6595, 6604, 6610, 6630, 6636, 6645, 6651, 6662, + 6668, 6677, 6683, 6708, 6714, 6723, 6729, 6740, + 6746, 6755, 6761, 6781, 6787, 6796, 6802, 6813, + 6819, 6828, 6834, 6857, 6863, 6872, 6878, 6889, + 6895, 6904, 6910, 6930, 6936, 6945, 6951, 6962, + 6968, 6977, 6983, 7008, 7014, 7023, 7029, 7040, + 7046, 7055, 7061, 7081, 7087, 7096, 7102, 7113, + 7119, 7128, 7134, 6392, 6398, 6407, 6413, 6424, + 6430, 6439, 6445, 6465, 6471, 6480, 6486, 6497, + 6503, 6512, 6518, 6543, 6549, 6558, 6564, 6575, + 6581, 6590, 6596, 6616, 6622, 6631, 6637, 6648, + 6654, 6663, 6669, 6692, 6698, 6707, 6713, 6724, + 6730, 6739, 6745, 6765, 6771, 6780, 6786, 6797, + 6803, 6812, 6818, 6843, 6849, 6858, 6864, 6875, + 6881, 6890, 6896, 6916, 6922, 6931, 6937, 6948, + 6954, 6963, 6969, 6833, 6839, 6848, 6854, 6865, + 6871, 6880, 6886, 6906, 6912, 6921, 6927, 6938, + 6944, 6953, 6959, 6984, 6990, 6999, 7005, 7016, + 7022, 7031, 7037, 7057, 7063, 7072, 7078, 7089, + 7095, 7104, 7110, 7133, 7139, 7148, 7154, 7165, + 7171, 7180, 7186, 7206, 7212, 7221, 7227, 7238, + 7244, 7253, 7259, 7284, 7290, 7299, 7305, 7316, + 7322, 7331, 7337, 7357, 7363, 7372, 7378, 7389, + 7395, 7404, 7410, 7205, 7211, 7220, 7226, 7237, + 7243, 7252, 7258, 7278, 7284, 7293, 7299, 7310, + 7316, 7325, 7331, 7356, 7362, 7371, 7377, 7388, + 7394, 7403, 7409, 7429, 7435, 7444, 7450, 7461, + 7467, 7476, 7482, 7505, 7511, 7520, 7526, 7537, + 7543, 7552, 7558, 7578, 7584, 7593, 7599, 7610, + 7616, 7625, 7631, 7656, 7662, 7671, 7677, 7688, + 7694, 7703, 7709, 7729, 7735, 7744, 7750, 7761 +}; + +//------------------------------------------------------------------------------ +// Tables for level coding + +const uint8_t VP8EncBands[16 + 1] = { + 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, + 0 // sentinel +}; + +//------------------------------------------------------------------------------ +// Mode costs + +static int GetResidualCost_C(int ctx0, const VP8Residual* const res) { + int n = res->first; + // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 + const int p0 = res->prob[n][ctx0][0]; + CostArrayPtr const costs = res->costs; + const uint16_t* t = costs[n][ctx0]; + // bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0 + // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll + // be missing during the loop. + int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0; + + if (res->last < 0) { + return VP8BitCost(0, p0); + } + for (; n < res->last; ++n) { + const int v = abs(res->coeffs[n]); + const int ctx = (v >= 2) ? 2 : v; + cost += VP8LevelCost(t, v); + t = costs[n + 1][ctx]; + } + // Last coefficient is always non-zero + { + const int v = abs(res->coeffs[n]); + assert(v != 0); + cost += VP8LevelCost(t, v); + if (n < 15) { + const int b = VP8EncBands[n + 1]; + const int ctx = (v == 1) ? 1 : 2; + const int last_p0 = res->prob[b][ctx][0]; + cost += VP8BitCost(0, last_p0); + } + } + return cost; +} + +static void SetResidualCoeffs_C(const int16_t* const coeffs, + VP8Residual* const res) { + int n; + res->last = -1; + assert(res->first == 0 || coeffs[0] == 0); + for (n = 15; n >= 0; --n) { + if (coeffs[n]) { + res->last = n; + break; + } + } + res->coeffs = coeffs; +} + +//------------------------------------------------------------------------------ +// init function + +VP8GetResidualCostFunc VP8GetResidualCost; +VP8SetResidualCoeffsFunc VP8SetResidualCoeffs; + +extern void VP8EncDspCostInitMIPS32(void); +extern void VP8EncDspCostInitMIPSdspR2(void); +extern void VP8EncDspCostInitSSE2(void); +extern void VP8EncDspCostInitNEON(void); + +WEBP_DSP_INIT_FUNC(VP8EncDspCostInit) { + VP8GetResidualCost = GetResidualCost_C; + VP8SetResidualCoeffs = SetResidualCoeffs_C; + + // If defined, use CPUInfo() to overwrite some pointers with faster versions. + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_USE_MIPS32) + if (VP8GetCPUInfo(kMIPS32)) { + VP8EncDspCostInitMIPS32(); + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + VP8EncDspCostInitMIPSdspR2(); + } +#endif +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + VP8EncDspCostInitSSE2(); + } +#endif +#if defined(WEBP_HAVE_NEON) + if (VP8GetCPUInfo(kNEON)) { + VP8EncDspCostInitNEON(); + } +#endif + } +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/src/dsp/cost_mips32.c b/internal/libwebp-1.2.1/src/dsp/cost_mips32.c new file mode 100644 index 0000000..0500f88 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/cost_mips32.c @@ -0,0 +1,154 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Author: Djordje Pesut (djordje.pesut@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS32) + +#include "src/enc/cost_enc.h" + +static int GetResidualCost_MIPS32(int ctx0, const VP8Residual* const res) { + int temp0, temp1; + int v_reg, ctx_reg; + int n = res->first; + // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 + int p0 = res->prob[n][ctx0][0]; + CostArrayPtr const costs = res->costs; + const uint16_t* t = costs[n][ctx0]; + // bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0 + // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll + // be missing during the loop. + int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0; + const int16_t* res_coeffs = res->coeffs; + const int res_last = res->last; + const int const_max_level = MAX_VARIABLE_LEVEL; + const int const_2 = 2; + const uint16_t** p_costs = &costs[n][0]; + const size_t inc_p_costs = NUM_CTX * sizeof(*p_costs); + + if (res->last < 0) { + return VP8BitCost(0, p0); + } + + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "subu %[temp1], %[res_last], %[n] \n\t" + "sll %[temp0], %[n], 1 \n\t" + "blez %[temp1], 2f \n\t" + " addu %[res_coeffs], %[res_coeffs], %[temp0] \n\t" + "1: \n\t" + "lh %[v_reg], 0(%[res_coeffs]) \n\t" + "addiu %[n], %[n], 1 \n\t" + "negu %[temp0], %[v_reg] \n\t" + "slti %[temp1], %[v_reg], 0 \n\t" + "movn %[v_reg], %[temp0], %[temp1] \n\t" + "sltiu %[temp0], %[v_reg], 2 \n\t" + "move %[ctx_reg], %[v_reg] \n\t" + "movz %[ctx_reg], %[const_2], %[temp0] \n\t" + "sll %[temp1], %[v_reg], 1 \n\t" + "addu %[temp1], %[temp1], %[VP8LevelFixedCosts] \n\t" + "lhu %[temp1], 0(%[temp1]) \n\t" + "slt %[temp0], %[v_reg], %[const_max_level] \n\t" + "movz %[v_reg], %[const_max_level], %[temp0] \n\t" + "addu %[cost], %[cost], %[temp1] \n\t" + "sll %[v_reg], %[v_reg], 1 \n\t" + "sll %[ctx_reg], %[ctx_reg], 2 \n\t" + "addu %[v_reg], %[v_reg], %[t] \n\t" + "lhu %[temp0], 0(%[v_reg]) \n\t" + "addu %[p_costs], %[p_costs], %[inc_p_costs] \n\t" + "addu %[t], %[p_costs], %[ctx_reg] \n\t" + "addu %[cost], %[cost], %[temp0] \n\t" + "addiu %[res_coeffs], %[res_coeffs], 2 \n\t" + "bne %[n], %[res_last], 1b \n\t" + " lw %[t], 0(%[t]) \n\t" + "2: \n\t" + ".set pop \n\t" + : [cost]"+&r"(cost), [t]"+&r"(t), [n]"+&r"(n), [v_reg]"=&r"(v_reg), + [ctx_reg]"=&r"(ctx_reg), [p_costs]"+&r"(p_costs), [temp0]"=&r"(temp0), + [temp1]"=&r"(temp1), [res_coeffs]"+&r"(res_coeffs) + : [const_2]"r"(const_2), [const_max_level]"r"(const_max_level), + [VP8LevelFixedCosts]"r"(VP8LevelFixedCosts), [res_last]"r"(res_last), + [inc_p_costs]"r"(inc_p_costs) + : "memory" + ); + + // Last coefficient is always non-zero + { + const int v = abs(res->coeffs[n]); + assert(v != 0); + cost += VP8LevelCost(t, v); + if (n < 15) { + const int b = VP8EncBands[n + 1]; + const int ctx = (v == 1) ? 1 : 2; + const int last_p0 = res->prob[b][ctx][0]; + cost += VP8BitCost(0, last_p0); + } + } + return cost; +} + +static void SetResidualCoeffs_MIPS32(const int16_t* const coeffs, + VP8Residual* const res) { + const int16_t* p_coeffs = (int16_t*)coeffs; + int temp0, temp1, temp2, n, n1; + assert(res->first == 0 || coeffs[0] == 0); + + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[p_coeffs], %[p_coeffs], 28 \n\t" + "li %[n], 15 \n\t" + "li %[temp2], -1 \n\t" + "0: \n\t" + "ulw %[temp0], 0(%[p_coeffs]) \n\t" + "beqz %[temp0], 1f \n\t" +#if defined(WORDS_BIGENDIAN) + " sll %[temp1], %[temp0], 16 \n\t" +#else + " srl %[temp1], %[temp0], 16 \n\t" +#endif + "addiu %[n1], %[n], -1 \n\t" + "movz %[temp0], %[n1], %[temp1] \n\t" + "movn %[temp0], %[n], %[temp1] \n\t" + "j 2f \n\t" + " addiu %[temp2], %[temp0], 0 \n\t" + "1: \n\t" + "addiu %[n], %[n], -2 \n\t" + "bgtz %[n], 0b \n\t" + " addiu %[p_coeffs], %[p_coeffs], -4 \n\t" + "2: \n\t" + ".set pop \n\t" + : [p_coeffs]"+&r"(p_coeffs), [temp0]"=&r"(temp0), + [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [n]"=&r"(n), [n1]"=&r"(n1) + : + : "memory" + ); + res->last = temp2; + res->coeffs = coeffs; +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspCostInitMIPS32(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInitMIPS32(void) { + VP8GetResidualCost = GetResidualCost_MIPS32; + VP8SetResidualCoeffs = SetResidualCoeffs_MIPS32; +} + +#else // !WEBP_USE_MIPS32 + +WEBP_DSP_INIT_STUB(VP8EncDspCostInitMIPS32) + +#endif // WEBP_USE_MIPS32 diff --git a/internal/libwebp-1.2.1/src/dsp/cost_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/cost_mips_dsp_r2.c new file mode 100644 index 0000000..51248de --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/cost_mips_dsp_r2.c @@ -0,0 +1,107 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Author: Djordje Pesut (djordje.pesut@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) + +#include "src/enc/cost_enc.h" + +static int GetResidualCost_MIPSdspR2(int ctx0, const VP8Residual* const res) { + int temp0, temp1; + int v_reg, ctx_reg; + int n = res->first; + // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 + int p0 = res->prob[n][ctx0][0]; + CostArrayPtr const costs = res->costs; + const uint16_t* t = costs[n][ctx0]; + // bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0 + // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll + // be missing during the loop. + int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0; + const int16_t* res_coeffs = res->coeffs; + const int res_last = res->last; + const int const_max_level = MAX_VARIABLE_LEVEL; + const int const_2 = 2; + const uint16_t** p_costs = &costs[n][0]; + const size_t inc_p_costs = NUM_CTX * sizeof(*p_costs); + + if (res->last < 0) { + return VP8BitCost(0, p0); + } + + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "subu %[temp1], %[res_last], %[n] \n\t" + "blez %[temp1], 2f \n\t" + " nop \n\t" + "1: \n\t" + "sll %[temp0], %[n], 1 \n\t" + "lhx %[v_reg], %[temp0](%[res_coeffs]) \n\t" + "addiu %[n], %[n], 1 \n\t" + "absq_s.w %[v_reg], %[v_reg] \n\t" + "sltiu %[temp0], %[v_reg], 2 \n\t" + "move %[ctx_reg], %[v_reg] \n\t" + "movz %[ctx_reg], %[const_2], %[temp0] \n\t" + "sll %[temp1], %[v_reg], 1 \n\t" + "lhx %[temp1], %[temp1](%[VP8LevelFixedCosts]) \n\t" + "slt %[temp0], %[v_reg], %[const_max_level] \n\t" + "movz %[v_reg], %[const_max_level], %[temp0] \n\t" + "addu %[cost], %[cost], %[temp1] \n\t" + "sll %[v_reg], %[v_reg], 1 \n\t" + "sll %[ctx_reg], %[ctx_reg], 2 \n\t" + "lhx %[temp0], %[v_reg](%[t]) \n\t" + "addu %[p_costs], %[p_costs], %[inc_p_costs] \n\t" + "addu %[t], %[p_costs], %[ctx_reg] \n\t" + "addu %[cost], %[cost], %[temp0] \n\t" + "bne %[n], %[res_last], 1b \n\t" + " lw %[t], 0(%[t]) \n\t" + "2: \n\t" + ".set pop \n\t" + : [cost]"+&r"(cost), [t]"+&r"(t), [n]"+&r"(n), [v_reg]"=&r"(v_reg), + [ctx_reg]"=&r"(ctx_reg), [p_costs]"+&r"(p_costs), [temp0]"=&r"(temp0), + [temp1]"=&r"(temp1) + : [const_2]"r"(const_2), [const_max_level]"r"(const_max_level), + [VP8LevelFixedCosts]"r"(VP8LevelFixedCosts), [res_last]"r"(res_last), + [res_coeffs]"r"(res_coeffs), [inc_p_costs]"r"(inc_p_costs) + : "memory" + ); + + // Last coefficient is always non-zero + { + const int v = abs(res->coeffs[n]); + assert(v != 0); + cost += VP8LevelCost(t, v); + if (n < 15) { + const int b = VP8EncBands[n + 1]; + const int ctx = (v == 1) ? 1 : 2; + const int last_p0 = res->prob[b][ctx][0]; + cost += VP8BitCost(0, last_p0); + } + } + return cost; +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspCostInitMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInitMIPSdspR2(void) { + VP8GetResidualCost = GetResidualCost_MIPSdspR2; +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(VP8EncDspCostInitMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 diff --git a/internal/libwebp-1.2.1/src/dsp/cost_neon.c b/internal/libwebp-1.2.1/src/dsp/cost_neon.c new file mode 100644 index 0000000..8cc8ce5 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/cost_neon.c @@ -0,0 +1,122 @@ +// Copyright 2018 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. +// ----------------------------------------------------------------------------- +// +// ARM NEON version of cost functions + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_NEON) + +#include "src/dsp/neon.h" +#include "src/enc/cost_enc.h" + +static const uint8_t position[16] = { 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 }; + +static void SetResidualCoeffs_NEON(const int16_t* const coeffs, + VP8Residual* const res) { + const int16x8_t minus_one = vdupq_n_s16(-1); + const int16x8_t coeffs_0 = vld1q_s16(coeffs); + const int16x8_t coeffs_1 = vld1q_s16(coeffs + 8); + const uint16x8_t eob_0 = vtstq_s16(coeffs_0, minus_one); + const uint16x8_t eob_1 = vtstq_s16(coeffs_1, minus_one); + const uint8x16_t eob = vcombine_u8(vqmovn_u16(eob_0), vqmovn_u16(eob_1)); + const uint8x16_t masked = vandq_u8(eob, vld1q_u8(position)); + +#ifdef __aarch64__ + res->last = vmaxvq_u8(masked) - 1; +#else + const uint8x8_t eob_8x8 = vmax_u8(vget_low_u8(masked), vget_high_u8(masked)); + const uint16x8_t eob_16x8 = vmovl_u8(eob_8x8); + const uint16x4_t eob_16x4 = + vmax_u16(vget_low_u16(eob_16x8), vget_high_u16(eob_16x8)); + const uint32x4_t eob_32x4 = vmovl_u16(eob_16x4); + uint32x2_t eob_32x2 = + vmax_u32(vget_low_u32(eob_32x4), vget_high_u32(eob_32x4)); + eob_32x2 = vpmax_u32(eob_32x2, eob_32x2); + + vst1_lane_s32(&res->last, vreinterpret_s32_u32(eob_32x2), 0); + --res->last; +#endif // __aarch64__ + + res->coeffs = coeffs; +} + +static int GetResidualCost_NEON(int ctx0, const VP8Residual* const res) { + uint8_t levels[16], ctxs[16]; + uint16_t abs_levels[16]; + int n = res->first; + // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 + const int p0 = res->prob[n][ctx0][0]; + CostArrayPtr const costs = res->costs; + const uint16_t* t = costs[n][ctx0]; + // bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0 + // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll + // be missing during the loop. + int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0; + + if (res->last < 0) { + return VP8BitCost(0, p0); + } + + { // precompute clamped levels and contexts, packed to 8b. + const uint8x16_t kCst2 = vdupq_n_u8(2); + const uint8x16_t kCst67 = vdupq_n_u8(MAX_VARIABLE_LEVEL); + const int16x8_t c0 = vld1q_s16(res->coeffs); + const int16x8_t c1 = vld1q_s16(res->coeffs + 8); + const uint16x8_t E0 = vreinterpretq_u16_s16(vabsq_s16(c0)); + const uint16x8_t E1 = vreinterpretq_u16_s16(vabsq_s16(c1)); + const uint8x16_t F = vcombine_u8(vqmovn_u16(E0), vqmovn_u16(E1)); + const uint8x16_t G = vminq_u8(F, kCst2); // context = 0,1,2 + const uint8x16_t H = vminq_u8(F, kCst67); // clamp_level in [0..67] + + vst1q_u8(ctxs, G); + vst1q_u8(levels, H); + + vst1q_u16(abs_levels, E0); + vst1q_u16(abs_levels + 8, E1); + } + for (; n < res->last; ++n) { + const int ctx = ctxs[n]; + const int level = levels[n]; + const int flevel = abs_levels[n]; // full level + cost += VP8LevelFixedCosts[flevel] + t[level]; // simplified VP8LevelCost() + t = costs[n + 1][ctx]; + } + // Last coefficient is always non-zero + { + const int level = levels[n]; + const int flevel = abs_levels[n]; + assert(flevel != 0); + cost += VP8LevelFixedCosts[flevel] + t[level]; + if (n < 15) { + const int b = VP8EncBands[n + 1]; + const int ctx = ctxs[n]; + const int last_p0 = res->prob[b][ctx][0]; + cost += VP8BitCost(0, last_p0); + } + } + return cost; +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspCostInitNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInitNEON(void) { + VP8SetResidualCoeffs = SetResidualCoeffs_NEON; + VP8GetResidualCost = GetResidualCost_NEON; +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(VP8EncDspCostInitNEON) + +#endif // WEBP_USE_NEON diff --git a/internal/libwebp-1.2.1/src/dsp/cost_sse2.c b/internal/libwebp-1.2.1/src/dsp/cost_sse2.c new file mode 100644 index 0000000..487a079 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/cost_sse2.c @@ -0,0 +1,119 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE2 version of cost functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) +#include + +#include "src/enc/cost_enc.h" +#include "src/enc/vp8i_enc.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ + +static void SetResidualCoeffs_SSE2(const int16_t* const coeffs, + VP8Residual* const res) { + const __m128i c0 = _mm_loadu_si128((const __m128i*)(coeffs + 0)); + const __m128i c1 = _mm_loadu_si128((const __m128i*)(coeffs + 8)); + // Use SSE2 to compare 16 values with a single instruction. + const __m128i zero = _mm_setzero_si128(); + const __m128i m0 = _mm_packs_epi16(c0, c1); + const __m128i m1 = _mm_cmpeq_epi8(m0, zero); + // Get the comparison results as a bitmask into 16bits. Negate the mask to get + // the position of entries that are not equal to zero. We don't need to mask + // out least significant bits according to res->first, since coeffs[0] is 0 + // if res->first > 0. + const uint32_t mask = 0x0000ffffu ^ (uint32_t)_mm_movemask_epi8(m1); + // The position of the most significant non-zero bit indicates the position of + // the last non-zero value. + assert(res->first == 0 || coeffs[0] == 0); + res->last = mask ? BitsLog2Floor(mask) : -1; + res->coeffs = coeffs; +} + +static int GetResidualCost_SSE2(int ctx0, const VP8Residual* const res) { + uint8_t levels[16], ctxs[16]; + uint16_t abs_levels[16]; + int n = res->first; + // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 + const int p0 = res->prob[n][ctx0][0]; + CostArrayPtr const costs = res->costs; + const uint16_t* t = costs[n][ctx0]; + // bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0 + // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll + // be missing during the loop. + int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0; + + if (res->last < 0) { + return VP8BitCost(0, p0); + } + + { // precompute clamped levels and contexts, packed to 8b. + const __m128i zero = _mm_setzero_si128(); + const __m128i kCst2 = _mm_set1_epi8(2); + const __m128i kCst67 = _mm_set1_epi8(MAX_VARIABLE_LEVEL); + const __m128i c0 = _mm_loadu_si128((const __m128i*)&res->coeffs[0]); + const __m128i c1 = _mm_loadu_si128((const __m128i*)&res->coeffs[8]); + const __m128i D0 = _mm_sub_epi16(zero, c0); + const __m128i D1 = _mm_sub_epi16(zero, c1); + const __m128i E0 = _mm_max_epi16(c0, D0); // abs(v), 16b + const __m128i E1 = _mm_max_epi16(c1, D1); + const __m128i F = _mm_packs_epi16(E0, E1); + const __m128i G = _mm_min_epu8(F, kCst2); // context = 0,1,2 + const __m128i H = _mm_min_epu8(F, kCst67); // clamp_level in [0..67] + + _mm_storeu_si128((__m128i*)&ctxs[0], G); + _mm_storeu_si128((__m128i*)&levels[0], H); + + _mm_storeu_si128((__m128i*)&abs_levels[0], E0); + _mm_storeu_si128((__m128i*)&abs_levels[8], E1); + } + for (; n < res->last; ++n) { + const int ctx = ctxs[n]; + const int level = levels[n]; + const int flevel = abs_levels[n]; // full level + cost += VP8LevelFixedCosts[flevel] + t[level]; // simplified VP8LevelCost() + t = costs[n + 1][ctx]; + } + // Last coefficient is always non-zero + { + const int level = levels[n]; + const int flevel = abs_levels[n]; + assert(flevel != 0); + cost += VP8LevelFixedCosts[flevel] + t[level]; + if (n < 15) { + const int b = VP8EncBands[n + 1]; + const int ctx = ctxs[n]; + const int last_p0 = res->prob[b][ctx][0]; + cost += VP8BitCost(0, last_p0); + } + } + return cost; +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspCostInitSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInitSSE2(void) { + VP8SetResidualCoeffs = SetResidualCoeffs_SSE2; + VP8GetResidualCost = GetResidualCost_SSE2; +} + +#else // !WEBP_USE_SSE2 + +WEBP_DSP_INIT_STUB(VP8EncDspCostInitSSE2) + +#endif // WEBP_USE_SSE2 diff --git a/internal/libwebp-1.2.1/src/dsp/cpu.c b/internal/libwebp-1.2.1/src/dsp/cpu.c new file mode 100644 index 0000000..3145e19 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/cpu.c @@ -0,0 +1,253 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// CPU detection +// +// Author: Christian Duvivier (cduvivier@google.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_HAVE_NEON_RTCD) +#include +#include +#endif + +#if defined(WEBP_ANDROID_NEON) +#include +#endif + +//------------------------------------------------------------------------------ +// SSE2 detection. +// + +// apple/darwin gcc-4.0.1 defines __PIC__, but not __pic__ with -fPIC. +#if (defined(__pic__) || defined(__PIC__)) && defined(__i386__) +static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { + __asm__ volatile ( + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type), "c"(0)); +} +#elif defined(__x86_64__) && \ + (defined(__code_model_medium__) || defined(__code_model_large__)) && \ + defined(__PIC__) +static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { + __asm__ volatile ( + "xchg{q}\t{%%rbx}, %q1\n" + "cpuid\n" + "xchg{q}\t{%%rbx}, %q1\n" + : "=a"(cpu_info[0]), "=&r"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "a"(info_type), "c"(0)); +} +#elif defined(__i386__) || defined(__x86_64__) +static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { + __asm__ volatile ( + "cpuid\n" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type), "c"(0)); +} +#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) + +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 150030729 // >= VS2008 SP1 +#include +#define GetCPUInfo(info, type) __cpuidex(info, type, 0) // set ecx=0 +#define WEBP_HAVE_MSC_CPUID +#elif _MSC_VER > 1310 +#include +#define GetCPUInfo __cpuid +#define WEBP_HAVE_MSC_CPUID +#endif + +#endif + +// NaCl has no support for xgetbv or the raw opcode. +#if !defined(__native_client__) && (defined(__i386__) || defined(__x86_64__)) +static WEBP_INLINE uint64_t xgetbv(void) { + const uint32_t ecx = 0; + uint32_t eax, edx; + // Use the raw opcode for xgetbv for compatibility with older toolchains. + __asm__ volatile ( + ".byte 0x0f, 0x01, 0xd0\n" + : "=a"(eax), "=d"(edx) : "c" (ecx)); + return ((uint64_t)edx << 32) | eax; +} +#elif (defined(_M_X64) || defined(_M_IX86)) && \ + defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219 // >= VS2010 SP1 +#include +#define xgetbv() _xgetbv(0) +#elif defined(_MSC_VER) && defined(_M_IX86) +static WEBP_INLINE uint64_t xgetbv(void) { + uint32_t eax_, edx_; + __asm { + xor ecx, ecx // ecx = 0 + // Use the raw opcode for xgetbv for compatibility with older toolchains. + __asm _emit 0x0f __asm _emit 0x01 __asm _emit 0xd0 + mov eax_, eax + mov edx_, edx + } + return ((uint64_t)edx_ << 32) | eax_; +} +#else +#define xgetbv() 0U // no AVX for older x64 or unrecognized toolchains. +#endif + +#if defined(__i386__) || defined(__x86_64__) || defined(WEBP_HAVE_MSC_CPUID) + +// 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 !!(cpu_info[3] & (1 << 26)); + } + if (feature == kSSE3) { + return !!(cpu_info[2] & (1 << 0)); + } + if (feature == kSlowSSSE3) { + if (is_intel && (cpu_info[2] & (1 << 9))) { // SSSE3? + return CheckSlowModel(cpu_info[0]); + } + return 0; + } + + if (feature == kSSE4_1) { + return !!(cpu_info[2] & (1 << 19)); + } + if (feature == kAVX) { + // bits 27 (OSXSAVE) & 28 (256-bit AVX) + if ((cpu_info[2] & 0x18000000) == 0x18000000) { + // XMM state and YMM state enabled by the OS. + return (xgetbv() & 0x6) == 0x6; + } + } + if (feature == kAVX2) { + if (x86CPUInfo(kAVX) && max_cpuid_value >= 7) { + GetCPUInfo(cpu_info, 7); + return !!(cpu_info[1] & (1 << 5)); + } + } + return 0; +} +VP8CPUInfo VP8GetCPUInfo = x86CPUInfo; +#elif defined(WEBP_ANDROID_NEON) // NB: needs to be before generic NEON test. +static int AndroidCPUInfo(CPUFeature feature) { + const AndroidCpuFamily cpu_family = android_getCpuFamily(); + const uint64_t cpu_features = android_getCpuFeatures(); + if (feature == kNEON) { + return cpu_family == ANDROID_CPU_FAMILY_ARM && + (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON) != 0; + } + return 0; +} +VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo; +#elif defined(EMSCRIPTEN) // also needs to be before generic NEON test +// Use compile flags as an indicator of SIMD support instead of a runtime check. +static int wasmCPUInfo(CPUFeature feature) { + switch (feature) { +#ifdef WEBP_HAVE_SSE2 + case kSSE2: + return 1; +#endif +#ifdef WEBP_HAVE_SSE41 + case kSSE3: + case kSlowSSSE3: + case kSSE4_1: + return 1; +#endif +#ifdef WEBP_HAVE_NEON + case kNEON: + return 1; +#endif + default: + break; + } + return 0; +} +VP8CPUInfo VP8GetCPUInfo = wasmCPUInfo; +#elif defined(WEBP_HAVE_NEON) +// In most cases this function doesn't check for NEON support (it's assumed by +// the configuration), but enables turning off NEON at runtime, for testing +// purposes, by setting VP8DecGetCPUInfo = NULL. +static int armCPUInfo(CPUFeature feature) { + if (feature != kNEON) return 0; +#if defined(__linux__) && defined(WEBP_HAVE_NEON_RTCD) + { + int has_neon = 0; + char line[200]; + FILE* const cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo == NULL) return 0; + while (fgets(line, sizeof(line), cpuinfo)) { + if (!strncmp(line, "Features", 8)) { + if (strstr(line, " neon ") != NULL) { + has_neon = 1; + break; + } + } + } + fclose(cpuinfo); + return has_neon; + } +#else + return 1; +#endif +} +VP8CPUInfo VP8GetCPUInfo = armCPUInfo; +#elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2) || \ + defined(WEBP_USE_MSA) +static int mipsCPUInfo(CPUFeature feature) { + if ((feature == kMIPS32) || (feature == kMIPSdspR2) || (feature == kMSA)) { + return 1; + } else { + return 0; + } + +} +VP8CPUInfo VP8GetCPUInfo = mipsCPUInfo; +#else +VP8CPUInfo VP8GetCPUInfo = NULL; +#endif diff --git a/internal/libwebp-1.2.1/src/dsp/dec.c b/internal/libwebp-1.2.1/src/dsp/dec.c new file mode 100644 index 0000000..537c701 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/dec.c @@ -0,0 +1,887 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Speed-critical decoding functions, default plain-C implementations. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include + +#include "src/dsp/dsp.h" +#include "src/dec/vp8i_dec.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ + +static WEBP_INLINE uint8_t clip_8b(int v) { + return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255; +} + +//------------------------------------------------------------------------------ +// Transforms (Paragraph 14.4) + +#define STORE(x, y, v) \ + dst[(x) + (y) * BPS] = clip_8b(dst[(x) + (y) * BPS] + ((v) >> 3)) + +#define STORE2(y, dc, d, c) do { \ + const int DC = (dc); \ + STORE(0, y, DC + (d)); \ + STORE(1, y, DC + (c)); \ + STORE(2, y, DC - (c)); \ + STORE(3, y, DC - (d)); \ +} while (0) + +#define MUL1(a) ((((a) * 20091) >> 16) + (a)) +#define MUL2(a) (((a) * 35468) >> 16) + +#if !WEBP_NEON_OMIT_C_CODE +static void TransformOne_C(const int16_t* in, uint8_t* dst) { + int C[4 * 4], *tmp; + int i; + tmp = C; + for (i = 0; i < 4; ++i) { // vertical pass + const int a = in[0] + in[8]; // [-4096, 4094] + const int b = in[0] - in[8]; // [-4095, 4095] + const int c = MUL2(in[4]) - MUL1(in[12]); // [-3783, 3783] + const int d = MUL1(in[4]) + MUL2(in[12]); // [-3785, 3781] + tmp[0] = a + d; // [-7881, 7875] + tmp[1] = b + c; // [-7878, 7878] + tmp[2] = b - c; // [-7878, 7878] + tmp[3] = a - d; // [-7877, 7879] + tmp += 4; + in++; + } + // Each pass is expanding the dynamic range by ~3.85 (upper bound). + // The exact value is (2. + (20091 + 35468) / 65536). + // After the second pass, maximum interval is [-3794, 3794], assuming + // an input in [-2048, 2047] interval. We then need to add a dst value + // in the [0, 255] range. + // In the worst case scenario, the input to clip_8b() can be as large as + // [-60713, 60968]. + tmp = C; + for (i = 0; i < 4; ++i) { // horizontal pass + const int dc = tmp[0] + 4; + const int a = dc + tmp[8]; + const int b = dc - tmp[8]; + const int c = MUL2(tmp[4]) - MUL1(tmp[12]); + const int d = MUL1(tmp[4]) + MUL2(tmp[12]); + STORE(0, 0, a + d); + STORE(1, 0, b + c); + STORE(2, 0, b - c); + STORE(3, 0, a - d); + tmp++; + dst += BPS; + } +} + +// Simplified transform when only in[0], in[1] and in[4] are non-zero +static void TransformAC3_C(const int16_t* in, uint8_t* dst) { + const int a = in[0] + 4; + const int c4 = MUL2(in[4]); + const int d4 = MUL1(in[4]); + const int c1 = MUL2(in[1]); + const int d1 = MUL1(in[1]); + STORE2(0, a + d4, d1, c1); + STORE2(1, a + c4, d1, c1); + STORE2(2, a - c4, d1, c1); + STORE2(3, a - d4, d1, c1); +} +#undef MUL1 +#undef MUL2 +#undef STORE2 + +static void TransformTwo_C(const int16_t* in, uint8_t* dst, int do_two) { + TransformOne_C(in, dst); + if (do_two) { + TransformOne_C(in + 16, dst + 4); + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +static void TransformUV_C(const int16_t* in, uint8_t* dst) { + VP8Transform(in + 0 * 16, dst, 1); + VP8Transform(in + 2 * 16, dst + 4 * BPS, 1); +} + +#if !WEBP_NEON_OMIT_C_CODE +static void TransformDC_C(const int16_t* in, uint8_t* dst) { + const int DC = in[0] + 4; + int i, j; + for (j = 0; j < 4; ++j) { + for (i = 0; i < 4; ++i) { + STORE(i, j, DC); + } + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +static void TransformDCUV_C(const int16_t* in, uint8_t* dst) { + if (in[0 * 16]) VP8TransformDC(in + 0 * 16, dst); + if (in[1 * 16]) VP8TransformDC(in + 1 * 16, dst + 4); + if (in[2 * 16]) VP8TransformDC(in + 2 * 16, dst + 4 * BPS); + if (in[3 * 16]) VP8TransformDC(in + 3 * 16, dst + 4 * BPS + 4); +} + +#undef STORE + +//------------------------------------------------------------------------------ +// Paragraph 14.3 + +#if !WEBP_NEON_OMIT_C_CODE +static void TransformWHT_C(const int16_t* in, int16_t* out) { + int tmp[16]; + int i; + for (i = 0; i < 4; ++i) { + const int a0 = in[0 + i] + in[12 + i]; + const int a1 = in[4 + i] + in[ 8 + i]; + const int a2 = in[4 + i] - in[ 8 + i]; + const int a3 = in[0 + i] - in[12 + i]; + tmp[0 + i] = a0 + a1; + tmp[8 + i] = a0 - a1; + tmp[4 + i] = a3 + a2; + tmp[12 + i] = a3 - a2; + } + for (i = 0; i < 4; ++i) { + const int dc = tmp[0 + i * 4] + 3; // w/ rounder + const int a0 = dc + tmp[3 + i * 4]; + const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4]; + const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4]; + const int a3 = dc - tmp[3 + i * 4]; + out[ 0] = (a0 + a1) >> 3; + out[16] = (a3 + a2) >> 3; + out[32] = (a0 - a1) >> 3; + out[48] = (a3 - a2) >> 3; + out += 64; + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +void (*VP8TransformWHT)(const int16_t* in, int16_t* out); + +//------------------------------------------------------------------------------ +// Intra predictions + +#define DST(x, y) dst[(x) + (y) * BPS] + +#if !WEBP_NEON_OMIT_C_CODE +static WEBP_INLINE void TrueMotion(uint8_t* dst, int size) { + const uint8_t* top = dst - BPS; + const uint8_t* const clip0 = VP8kclip1 - top[-1]; + int y; + for (y = 0; y < size; ++y) { + const uint8_t* const clip = clip0 + dst[-1]; + int x; + for (x = 0; x < size; ++x) { + dst[x] = clip[top[x]]; + } + dst += BPS; + } +} +static void TM4_C(uint8_t* dst) { TrueMotion(dst, 4); } +static void TM8uv_C(uint8_t* dst) { TrueMotion(dst, 8); } +static void TM16_C(uint8_t* dst) { TrueMotion(dst, 16); } + +//------------------------------------------------------------------------------ +// 16x16 + +static void VE16_C(uint8_t* dst) { // vertical + int j; + for (j = 0; j < 16; ++j) { + memcpy(dst + j * BPS, dst - BPS, 16); + } +} + +static void HE16_C(uint8_t* dst) { // horizontal + int j; + for (j = 16; j > 0; --j) { + memset(dst, dst[-1], 16); + dst += BPS; + } +} + +static WEBP_INLINE void Put16(int v, uint8_t* dst) { + int j; + for (j = 0; j < 16; ++j) { + memset(dst + j * BPS, v, 16); + } +} + +static void DC16_C(uint8_t* dst) { // DC + int DC = 16; + int j; + for (j = 0; j < 16; ++j) { + DC += dst[-1 + j * BPS] + dst[j - BPS]; + } + Put16(DC >> 5, dst); +} + +static void DC16NoTop_C(uint8_t* dst) { // DC with top samples not available + int DC = 8; + int j; + for (j = 0; j < 16; ++j) { + DC += dst[-1 + j * BPS]; + } + Put16(DC >> 4, dst); +} + +static void DC16NoLeft_C(uint8_t* dst) { // DC with left samples not available + int DC = 8; + int i; + for (i = 0; i < 16; ++i) { + DC += dst[i - BPS]; + } + Put16(DC >> 4, dst); +} + +static void DC16NoTopLeft_C(uint8_t* dst) { // DC with no top and left samples + Put16(0x80, dst); +} +#endif // !WEBP_NEON_OMIT_C_CODE + +VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES]; + +//------------------------------------------------------------------------------ +// 4x4 + +#define AVG3(a, b, c) ((uint8_t)(((a) + 2 * (b) + (c) + 2) >> 2)) +#define AVG2(a, b) (((a) + (b) + 1) >> 1) + +#if !WEBP_NEON_OMIT_C_CODE +static void VE4_C(uint8_t* dst) { // vertical + const uint8_t* top = dst - BPS; + const uint8_t vals[4] = { + AVG3(top[-1], top[0], top[1]), + AVG3(top[ 0], top[1], top[2]), + AVG3(top[ 1], top[2], top[3]), + AVG3(top[ 2], top[3], top[4]) + }; + int i; + for (i = 0; i < 4; ++i) { + memcpy(dst + i * BPS, vals, sizeof(vals)); + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +static void HE4_C(uint8_t* dst) { // horizontal + const int A = dst[-1 - BPS]; + const int B = dst[-1]; + const int C = dst[-1 + BPS]; + const int D = dst[-1 + 2 * BPS]; + const int E = dst[-1 + 3 * BPS]; + WebPUint32ToMem(dst + 0 * BPS, 0x01010101U * AVG3(A, B, C)); + WebPUint32ToMem(dst + 1 * BPS, 0x01010101U * AVG3(B, C, D)); + WebPUint32ToMem(dst + 2 * BPS, 0x01010101U * AVG3(C, D, E)); + WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(D, E, E)); +} + +#if !WEBP_NEON_OMIT_C_CODE +static void DC4_C(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; + for (i = 0; i < 4; ++i) memset(dst + i * BPS, dc, 4); +} + +static void RD4_C(uint8_t* dst) { // Down-right + const int I = dst[-1 + 0 * BPS]; + const int J = dst[-1 + 1 * BPS]; + const int K = dst[-1 + 2 * BPS]; + const int L = dst[-1 + 3 * BPS]; + const int X = dst[-1 - BPS]; + const int A = dst[0 - BPS]; + const int B = dst[1 - BPS]; + const int C = dst[2 - BPS]; + const int D = dst[3 - BPS]; + DST(0, 3) = AVG3(J, K, L); + DST(1, 3) = DST(0, 2) = AVG3(I, J, K); + DST(2, 3) = DST(1, 2) = DST(0, 1) = AVG3(X, I, J); + DST(3, 3) = DST(2, 2) = DST(1, 1) = DST(0, 0) = AVG3(A, X, I); + DST(3, 2) = DST(2, 1) = DST(1, 0) = AVG3(B, A, X); + DST(3, 1) = DST(2, 0) = AVG3(C, B, A); + DST(3, 0) = AVG3(D, C, B); +} + +static void LD4_C(uint8_t* dst) { // Down-Left + const int A = dst[0 - BPS]; + const int B = dst[1 - BPS]; + const int C = dst[2 - BPS]; + const int D = dst[3 - BPS]; + const int E = dst[4 - BPS]; + const int F = dst[5 - BPS]; + const int G = dst[6 - BPS]; + const int H = dst[7 - BPS]; + DST(0, 0) = AVG3(A, B, C); + DST(1, 0) = DST(0, 1) = AVG3(B, C, D); + DST(2, 0) = DST(1, 1) = DST(0, 2) = AVG3(C, D, E); + DST(3, 0) = DST(2, 1) = DST(1, 2) = DST(0, 3) = AVG3(D, E, F); + DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G); + DST(3, 2) = DST(2, 3) = AVG3(F, G, H); + DST(3, 3) = AVG3(G, H, H); +} +#endif // !WEBP_NEON_OMIT_C_CODE + +static void VR4_C(uint8_t* dst) { // Vertical-Right + const int I = dst[-1 + 0 * BPS]; + const int J = dst[-1 + 1 * BPS]; + const int K = dst[-1 + 2 * BPS]; + const int X = dst[-1 - BPS]; + const int A = dst[0 - BPS]; + const int B = dst[1 - BPS]; + const int C = dst[2 - BPS]; + const int D = dst[3 - BPS]; + 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 void VL4_C(uint8_t* dst) { // Vertical-Left + const int A = dst[0 - BPS]; + const int B = dst[1 - BPS]; + const int C = dst[2 - BPS]; + const int D = dst[3 - BPS]; + const int E = dst[4 - BPS]; + const int F = dst[5 - BPS]; + const int G = dst[6 - BPS]; + const int H = dst[7 - BPS]; + 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 void HU4_C(uint8_t* dst) { // Horizontal-Up + const int I = dst[-1 + 0 * BPS]; + const int J = dst[-1 + 1 * BPS]; + const int K = dst[-1 + 2 * BPS]; + const int L = dst[-1 + 3 * BPS]; + 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 void HD4_C(uint8_t* dst) { // Horizontal-Down + const int I = dst[-1 + 0 * BPS]; + const int J = dst[-1 + 1 * BPS]; + const int K = dst[-1 + 2 * BPS]; + const int L = dst[-1 + 3 * BPS]; + const int X = dst[-1 - BPS]; + const int A = dst[0 - BPS]; + const int B = dst[1 - BPS]; + const int C = dst[2 - BPS]; + + 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); +} + +#undef DST +#undef AVG3 +#undef AVG2 + +VP8PredFunc VP8PredLuma4[NUM_BMODES]; + +//------------------------------------------------------------------------------ +// Chroma + +#if !WEBP_NEON_OMIT_C_CODE +static void VE8uv_C(uint8_t* dst) { // vertical + int j; + for (j = 0; j < 8; ++j) { + memcpy(dst + j * BPS, dst - BPS, 8); + } +} + +static void HE8uv_C(uint8_t* dst) { // horizontal + int j; + for (j = 0; j < 8; ++j) { + memset(dst, dst[-1], 8); + dst += BPS; + } +} + +// helper for chroma-DC predictions +static WEBP_INLINE void Put8x8uv(uint8_t value, uint8_t* dst) { + int j; + for (j = 0; j < 8; ++j) { + memset(dst + j * BPS, value, 8); + } +} + +static void DC8uv_C(uint8_t* dst) { // DC + int dc0 = 8; + int i; + for (i = 0; i < 8; ++i) { + dc0 += dst[i - BPS] + dst[-1 + i * BPS]; + } + Put8x8uv(dc0 >> 4, dst); +} + +static void DC8uvNoLeft_C(uint8_t* dst) { // DC with no left samples + int dc0 = 4; + int i; + for (i = 0; i < 8; ++i) { + dc0 += dst[i - BPS]; + } + Put8x8uv(dc0 >> 3, dst); +} + +static void DC8uvNoTop_C(uint8_t* dst) { // DC with no top samples + int dc0 = 4; + int i; + for (i = 0; i < 8; ++i) { + dc0 += dst[-1 + i * BPS]; + } + Put8x8uv(dc0 >> 3, dst); +} + +static void DC8uvNoTopLeft_C(uint8_t* dst) { // DC with nothing + Put8x8uv(0x80, dst); +} +#endif // !WEBP_NEON_OMIT_C_CODE + +VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES]; + +//------------------------------------------------------------------------------ +// Edge filtering functions + +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC +// 4 pixels in, 2 pixels out +static WEBP_INLINE void DoFilter2_C(uint8_t* p, int step) { + const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; + const int a = 3 * (q0 - p0) + VP8ksclip1[p1 - q1]; // in [-893,892] + const int a1 = VP8ksclip2[(a + 4) >> 3]; // in [-16,15] + const int a2 = VP8ksclip2[(a + 3) >> 3]; + p[-step] = VP8kclip1[p0 + a2]; + p[ 0] = VP8kclip1[q0 - a1]; +} + +// 4 pixels in, 4 pixels out +static WEBP_INLINE void DoFilter4_C(uint8_t* p, int step) { + const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; + const int a = 3 * (q0 - p0); + const int a1 = VP8ksclip2[(a + 4) >> 3]; + const int a2 = VP8ksclip2[(a + 3) >> 3]; + const int a3 = (a1 + 1) >> 1; + p[-2*step] = VP8kclip1[p1 + a3]; + p[- step] = VP8kclip1[p0 + a2]; + p[ 0] = VP8kclip1[q0 - a1]; + p[ step] = VP8kclip1[q1 - a3]; +} + +// 6 pixels in, 6 pixels out +static WEBP_INLINE void DoFilter6_C(uint8_t* p, int step) { + const int p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step]; + const int q0 = p[0], q1 = p[step], q2 = p[2*step]; + const int a = VP8ksclip1[3 * (q0 - p0) + VP8ksclip1[p1 - q1]]; + // a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9] + const int a1 = (27 * a + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7 + const int a2 = (18 * a + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7 + const int a3 = (9 * a + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7 + p[-3*step] = VP8kclip1[p2 + a3]; + p[-2*step] = VP8kclip1[p1 + a2]; + p[- step] = VP8kclip1[p0 + a1]; + p[ 0] = VP8kclip1[q0 - a1]; + p[ step] = VP8kclip1[q1 - a2]; + p[ 2*step] = VP8kclip1[q2 - a3]; +} + +static WEBP_INLINE int Hev(const uint8_t* p, int step, int thresh) { + const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step]; + return (VP8kabs0[p1 - p0] > thresh) || (VP8kabs0[q1 - q0] > thresh); +} +#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC + +#if !WEBP_NEON_OMIT_C_CODE +static WEBP_INLINE int NeedsFilter_C(const uint8_t* p, int step, int t) { + const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step]; + return ((4 * VP8kabs0[p0 - q0] + VP8kabs0[p1 - q1]) <= t); +} +#endif // !WEBP_NEON_OMIT_C_CODE + +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC +static WEBP_INLINE int NeedsFilter2_C(const uint8_t* p, + int step, int t, int it) { + const int p3 = p[-4 * step], p2 = p[-3 * step], p1 = p[-2 * step]; + const int p0 = p[-step], q0 = p[0]; + const int q1 = p[step], q2 = p[2 * step], q3 = p[3 * step]; + if ((4 * VP8kabs0[p0 - q0] + VP8kabs0[p1 - q1]) > t) return 0; + return VP8kabs0[p3 - p2] <= it && VP8kabs0[p2 - p1] <= it && + VP8kabs0[p1 - p0] <= it && VP8kabs0[q3 - q2] <= it && + VP8kabs0[q2 - q1] <= it && VP8kabs0[q1 - q0] <= it; +} +#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC + +//------------------------------------------------------------------------------ +// Simple In-loop filtering (Paragraph 15.2) + +#if !WEBP_NEON_OMIT_C_CODE +static void SimpleVFilter16_C(uint8_t* p, int stride, int thresh) { + int i; + const int thresh2 = 2 * thresh + 1; + for (i = 0; i < 16; ++i) { + if (NeedsFilter_C(p + i, stride, thresh2)) { + DoFilter2_C(p + i, stride); + } + } +} + +static void SimpleHFilter16_C(uint8_t* p, int stride, int thresh) { + int i; + const int thresh2 = 2 * thresh + 1; + for (i = 0; i < 16; ++i) { + if (NeedsFilter_C(p + i * stride, 1, thresh2)) { + DoFilter2_C(p + i * stride, 1); + } + } +} + +static void SimpleVFilter16i_C(uint8_t* p, int stride, int thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4 * stride; + SimpleVFilter16_C(p, stride, thresh); + } +} + +static void SimpleHFilter16i_C(uint8_t* p, int stride, int thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4; + SimpleHFilter16_C(p, stride, thresh); + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +//------------------------------------------------------------------------------ +// Complex In-loop filtering (Paragraph 15.3) + +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC +static WEBP_INLINE void FilterLoop26_C(uint8_t* p, + int hstride, int vstride, int size, + int thresh, int ithresh, + int hev_thresh) { + const int thresh2 = 2 * thresh + 1; + while (size-- > 0) { + if (NeedsFilter2_C(p, hstride, thresh2, ithresh)) { + if (Hev(p, hstride, hev_thresh)) { + DoFilter2_C(p, hstride); + } else { + DoFilter6_C(p, hstride); + } + } + p += vstride; + } +} + +static WEBP_INLINE void FilterLoop24_C(uint8_t* p, + int hstride, int vstride, int size, + int thresh, int ithresh, + int hev_thresh) { + const int thresh2 = 2 * thresh + 1; + while (size-- > 0) { + if (NeedsFilter2_C(p, hstride, thresh2, ithresh)) { + if (Hev(p, hstride, hev_thresh)) { + DoFilter2_C(p, hstride); + } else { + DoFilter4_C(p, hstride); + } + } + p += vstride; + } +} +#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC + +#if !WEBP_NEON_OMIT_C_CODE +// on macroblock edges +static void VFilter16_C(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26_C(p, stride, 1, 16, thresh, ithresh, hev_thresh); +} + +static void HFilter16_C(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26_C(p, 1, stride, 16, thresh, ithresh, hev_thresh); +} + +// on three inner edges +static void VFilter16i_C(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4 * stride; + FilterLoop24_C(p, stride, 1, 16, thresh, ithresh, hev_thresh); + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC +static void HFilter16i_C(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4; + FilterLoop24_C(p, 1, stride, 16, thresh, ithresh, hev_thresh); + } +} +#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC + +#if !WEBP_NEON_OMIT_C_CODE +// 8-pixels wide variant, for chroma filtering +static void VFilter8_C(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26_C(u, stride, 1, 8, thresh, ithresh, hev_thresh); + FilterLoop26_C(v, stride, 1, 8, thresh, ithresh, hev_thresh); +} +#endif // !WEBP_NEON_OMIT_C_CODE + +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC +static void HFilter8_C(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26_C(u, 1, stride, 8, thresh, ithresh, hev_thresh); + FilterLoop26_C(v, 1, stride, 8, thresh, ithresh, hev_thresh); +} +#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC + +#if !WEBP_NEON_OMIT_C_CODE +static void VFilter8i_C(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop24_C(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); + FilterLoop24_C(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); +} +#endif // !WEBP_NEON_OMIT_C_CODE + +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC +static void HFilter8i_C(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop24_C(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh); + FilterLoop24_C(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh); +} +#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC + +//------------------------------------------------------------------------------ + +static void DitherCombine8x8_C(const uint8_t* dither, uint8_t* dst, + int dst_stride) { + int i, j; + for (j = 0; j < 8; ++j) { + for (i = 0; i < 8; ++i) { + const int delta0 = dither[i] - VP8_DITHER_AMP_CENTER; + const int delta1 = + (delta0 + VP8_DITHER_DESCALE_ROUNDER) >> VP8_DITHER_DESCALE; + dst[i] = clip_8b((int)dst[i] + delta1); + } + dst += dst_stride; + dither += 8; + } +} + +//------------------------------------------------------------------------------ + +VP8DecIdct2 VP8Transform; +VP8DecIdct VP8TransformAC3; +VP8DecIdct VP8TransformUV; +VP8DecIdct VP8TransformDC; +VP8DecIdct VP8TransformDCUV; + +VP8LumaFilterFunc VP8VFilter16; +VP8LumaFilterFunc VP8HFilter16; +VP8ChromaFilterFunc VP8VFilter8; +VP8ChromaFilterFunc VP8HFilter8; +VP8LumaFilterFunc VP8VFilter16i; +VP8LumaFilterFunc VP8HFilter16i; +VP8ChromaFilterFunc VP8VFilter8i; +VP8ChromaFilterFunc VP8HFilter8i; +VP8SimpleFilterFunc VP8SimpleVFilter16; +VP8SimpleFilterFunc VP8SimpleHFilter16; +VP8SimpleFilterFunc VP8SimpleVFilter16i; +VP8SimpleFilterFunc VP8SimpleHFilter16i; + +void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst, + int dst_stride); + +extern void VP8DspInitSSE2(void); +extern void VP8DspInitSSE41(void); +extern void VP8DspInitNEON(void); +extern void VP8DspInitMIPS32(void); +extern void VP8DspInitMIPSdspR2(void); +extern void VP8DspInitMSA(void); + +WEBP_DSP_INIT_FUNC(VP8DspInit) { + VP8InitClipTables(); + +#if !WEBP_NEON_OMIT_C_CODE + VP8TransformWHT = TransformWHT_C; + VP8Transform = TransformTwo_C; + VP8TransformDC = TransformDC_C; + VP8TransformAC3 = TransformAC3_C; +#endif + VP8TransformUV = TransformUV_C; + VP8TransformDCUV = TransformDCUV_C; + +#if !WEBP_NEON_OMIT_C_CODE + VP8VFilter16 = VFilter16_C; + VP8VFilter16i = VFilter16i_C; + VP8HFilter16 = HFilter16_C; + VP8VFilter8 = VFilter8_C; + VP8VFilter8i = VFilter8i_C; + VP8SimpleVFilter16 = SimpleVFilter16_C; + VP8SimpleHFilter16 = SimpleHFilter16_C; + VP8SimpleVFilter16i = SimpleVFilter16i_C; + VP8SimpleHFilter16i = SimpleHFilter16i_C; +#endif + +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC + VP8HFilter16i = HFilter16i_C; + VP8HFilter8 = HFilter8_C; + VP8HFilter8i = HFilter8i_C; +#endif + +#if !WEBP_NEON_OMIT_C_CODE + VP8PredLuma4[0] = DC4_C; + VP8PredLuma4[1] = TM4_C; + VP8PredLuma4[2] = VE4_C; + VP8PredLuma4[4] = RD4_C; + VP8PredLuma4[6] = LD4_C; +#endif + + VP8PredLuma4[3] = HE4_C; + VP8PredLuma4[5] = VR4_C; + VP8PredLuma4[7] = VL4_C; + VP8PredLuma4[8] = HD4_C; + VP8PredLuma4[9] = HU4_C; + +#if !WEBP_NEON_OMIT_C_CODE + VP8PredLuma16[0] = DC16_C; + VP8PredLuma16[1] = TM16_C; + VP8PredLuma16[2] = VE16_C; + VP8PredLuma16[3] = HE16_C; + VP8PredLuma16[4] = DC16NoTop_C; + VP8PredLuma16[5] = DC16NoLeft_C; + VP8PredLuma16[6] = DC16NoTopLeft_C; + + VP8PredChroma8[0] = DC8uv_C; + VP8PredChroma8[1] = TM8uv_C; + VP8PredChroma8[2] = VE8uv_C; + VP8PredChroma8[3] = HE8uv_C; + VP8PredChroma8[4] = DC8uvNoTop_C; + VP8PredChroma8[5] = DC8uvNoLeft_C; + VP8PredChroma8[6] = DC8uvNoTopLeft_C; +#endif + + VP8DitherCombine8x8 = DitherCombine8x8_C; + + // If defined, use CPUInfo() to overwrite some pointers with faster versions. + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + VP8DspInitSSE2(); +#if defined(WEBP_HAVE_SSE41) + if (VP8GetCPUInfo(kSSE4_1)) { + VP8DspInitSSE41(); + } +#endif + } +#endif +#if defined(WEBP_USE_MIPS32) + if (VP8GetCPUInfo(kMIPS32)) { + VP8DspInitMIPS32(); + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + VP8DspInitMIPSdspR2(); + } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8DspInitMSA(); + } +#endif + } + +#if defined(WEBP_HAVE_NEON) + if (WEBP_NEON_OMIT_C_CODE || + (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) { + VP8DspInitNEON(); + } +#endif + + assert(VP8TransformWHT != NULL); + assert(VP8Transform != NULL); + assert(VP8TransformDC != NULL); + assert(VP8TransformAC3 != NULL); + assert(VP8TransformUV != NULL); + assert(VP8TransformDCUV != NULL); + assert(VP8VFilter16 != NULL); + assert(VP8HFilter16 != NULL); + assert(VP8VFilter8 != NULL); + assert(VP8HFilter8 != NULL); + assert(VP8VFilter16i != NULL); + assert(VP8HFilter16i != NULL); + assert(VP8VFilter8i != NULL); + assert(VP8HFilter8i != NULL); + assert(VP8SimpleVFilter16 != NULL); + assert(VP8SimpleHFilter16 != NULL); + assert(VP8SimpleVFilter16i != NULL); + assert(VP8SimpleHFilter16i != NULL); + assert(VP8PredLuma4[0] != NULL); + assert(VP8PredLuma4[1] != NULL); + assert(VP8PredLuma4[2] != NULL); + assert(VP8PredLuma4[3] != NULL); + assert(VP8PredLuma4[4] != NULL); + assert(VP8PredLuma4[5] != NULL); + assert(VP8PredLuma4[6] != NULL); + assert(VP8PredLuma4[7] != NULL); + assert(VP8PredLuma4[8] != NULL); + assert(VP8PredLuma4[9] != NULL); + assert(VP8PredLuma16[0] != NULL); + assert(VP8PredLuma16[1] != NULL); + assert(VP8PredLuma16[2] != NULL); + assert(VP8PredLuma16[3] != NULL); + assert(VP8PredLuma16[4] != NULL); + assert(VP8PredLuma16[5] != NULL); + assert(VP8PredLuma16[6] != NULL); + assert(VP8PredChroma8[0] != NULL); + assert(VP8PredChroma8[1] != NULL); + assert(VP8PredChroma8[2] != NULL); + assert(VP8PredChroma8[3] != NULL); + assert(VP8PredChroma8[4] != NULL); + assert(VP8PredChroma8[5] != NULL); + assert(VP8PredChroma8[6] != NULL); + assert(VP8DitherCombine8x8 != NULL); +} diff --git a/internal/libwebp-1.2.1/src/dsp/dec_clip_tables.c b/internal/libwebp-1.2.1/src/dsp/dec_clip_tables.c new file mode 100644 index 0000000..427b74f --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/dec_clip_tables.c @@ -0,0 +1,369 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Clipping tables for filtering +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +// define to 0 to have run-time table initialization +#if !defined(USE_STATIC_TABLES) +#define USE_STATIC_TABLES 1 // ALTERNATE_CODE +#endif + +#if (USE_STATIC_TABLES == 1) + +static const uint8_t abs0[255 + 255 + 1] = { + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, + 0xf3, 0xf2, 0xf1, 0xf0, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, + 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xdf, 0xde, 0xdd, 0xdc, + 0xdb, 0xda, 0xd9, 0xd8, 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, + 0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, 0xc7, 0xc6, 0xc5, 0xc4, + 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, 0xaf, 0xae, 0xad, 0xac, + 0xab, 0xaa, 0xa9, 0xa8, 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, + 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, + 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x7f, 0x7e, 0x7d, 0x7c, + 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, + 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, + 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, + 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4e, 0x4d, 0x4c, + 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, + 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, + 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, + 0x03, 0x02, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, + 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, + 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, + 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f +}; + +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, + 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, + 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, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f +}; + +static const uint8_t clip1[255 + 511 + 1] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, + 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, + 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, + 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +#else + +// uninitialized tables +static uint8_t abs0[255 + 255 + 1]; +static int8_t sclip1[1020 + 1020 + 1]; +static int8_t sclip2[112 + 112 + 1]; +static uint8_t clip1[255 + 511 + 1]; + +// We declare this variable 'volatile' to prevent instruction reordering +// and make sure it's set to true _last_ (so as to be thread-safe) +static volatile int tables_ok = 0; + +#endif // USE_STATIC_TABLES + +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]; + +WEBP_TSAN_IGNORE_FUNCTION void VP8InitClipTables(void) { +#if (USE_STATIC_TABLES == 0) + int i; + if (!tables_ok) { + for (i = -255; i <= 255; ++i) { + abs0[255 + i] = (i < 0) ? -i : i; + } + for (i = -1020; i <= 1020; ++i) { + sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i; + } + for (i = -112; i <= 112; ++i) { + sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i; + } + for (i = -255; i <= 255 + 255; ++i) { + clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i; + } + tables_ok = 1; + } +#endif // USE_STATIC_TABLES +} diff --git a/internal/libwebp-1.2.1/src/dsp/dec_mips32.c b/internal/libwebp-1.2.1/src/dsp/dec_mips32.c new file mode 100644 index 0000000..e4e7096 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/dec_mips32.c @@ -0,0 +1,587 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS version of dsp functions +// +// Author(s): Djordje Pesut (djordje.pesut@imgtec.com) +// Jovan Zelincevic (jovan.zelincevic@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS32) + +#include "src/dsp/mips_macro.h" + +static const int kC1 = 20091 + (1 << 16); +static const int kC2 = 35468; + +static WEBP_INLINE int abs_mips32(int x) { + const int sign = x >> 31; + return (x ^ sign) - sign; +} + +// 4 pixels in, 2 pixels out +static WEBP_INLINE void do_filter2(uint8_t* p, int step) { + const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step]; + const int a = 3 * (q0 - p0) + VP8ksclip1[p1 - q1]; + const int a1 = VP8ksclip2[(a + 4) >> 3]; + const int a2 = VP8ksclip2[(a + 3) >> 3]; + p[-step] = VP8kclip1[p0 + a2]; + p[ 0] = VP8kclip1[q0 - a1]; +} + +// 4 pixels in, 4 pixels out +static WEBP_INLINE void do_filter4(uint8_t* p, int step) { + const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step]; + const int a = 3 * (q0 - p0); + const int a1 = VP8ksclip2[(a + 4) >> 3]; + const int a2 = VP8ksclip2[(a + 3) >> 3]; + const int a3 = (a1 + 1) >> 1; + p[-2 * step] = VP8kclip1[p1 + a3]; + p[- step] = VP8kclip1[p0 + a2]; + p[ 0] = VP8kclip1[q0 - a1]; + p[ step] = VP8kclip1[q1 - a3]; +} + +// 6 pixels in, 6 pixels out +static WEBP_INLINE void do_filter6(uint8_t* p, int step) { + const int p2 = p[-3 * step], p1 = p[-2 * step], p0 = p[-step]; + const int q0 = p[0], q1 = p[step], q2 = p[2 * step]; + const int a = VP8ksclip1[3 * (q0 - p0) + VP8ksclip1[p1 - q1]]; + // a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9] + const int a1 = (27 * a + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7 + const int a2 = (18 * a + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7 + const int a3 = (9 * a + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7 + p[-3 * step] = VP8kclip1[p2 + a3]; + p[-2 * step] = VP8kclip1[p1 + a2]; + p[- step] = VP8kclip1[p0 + a1]; + p[ 0] = VP8kclip1[q0 - a1]; + p[ step] = VP8kclip1[q1 - a2]; + p[ 2 * step] = VP8kclip1[q2 - a3]; +} + +static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) { + const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step]; + return (abs_mips32(p1 - p0) > thresh) || (abs_mips32(q1 - q0) > thresh); +} + +static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int t) { + const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step]; + return ((4 * abs_mips32(p0 - q0) + abs_mips32(p1 - q1)) <= t); +} + +static WEBP_INLINE int needs_filter2(const uint8_t* p, + int step, int t, int it) { + const int p3 = p[-4 * step], p2 = p[-3 * step]; + const int p1 = p[-2 * step], p0 = p[-step]; + const int q0 = p[0], q1 = p[step], q2 = p[2 * step], q3 = p[3 * step]; + if ((4 * abs_mips32(p0 - q0) + abs_mips32(p1 - q1)) > t) { + return 0; + } + return abs_mips32(p3 - p2) <= it && abs_mips32(p2 - p1) <= it && + abs_mips32(p1 - p0) <= it && abs_mips32(q3 - q2) <= it && + abs_mips32(q2 - q1) <= it && abs_mips32(q1 - q0) <= it; +} + +static WEBP_INLINE void FilterLoop26(uint8_t* p, + int hstride, int vstride, int size, + int thresh, int ithresh, int hev_thresh) { + const int thresh2 = 2 * thresh + 1; + while (size-- > 0) { + if (needs_filter2(p, hstride, thresh2, ithresh)) { + if (hev(p, hstride, hev_thresh)) { + do_filter2(p, hstride); + } else { + do_filter6(p, hstride); + } + } + p += vstride; + } +} + +static WEBP_INLINE void FilterLoop24(uint8_t* p, + int hstride, int vstride, int size, + int thresh, int ithresh, int hev_thresh) { + const int thresh2 = 2 * thresh + 1; + while (size-- > 0) { + if (needs_filter2(p, hstride, thresh2, ithresh)) { + if (hev(p, hstride, hev_thresh)) { + do_filter2(p, hstride); + } else { + do_filter4(p, hstride); + } + } + p += vstride; + } +} + +// on macroblock edges +static void VFilter16(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26(p, stride, 1, 16, thresh, ithresh, hev_thresh); +} + +static void HFilter16(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26(p, 1, stride, 16, thresh, ithresh, hev_thresh); +} + +// 8-pixels wide variant, for chroma filtering +static void VFilter8(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26(u, stride, 1, 8, thresh, ithresh, hev_thresh); + FilterLoop26(v, stride, 1, 8, thresh, ithresh, hev_thresh); +} + +static void HFilter8(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26(u, 1, stride, 8, thresh, ithresh, hev_thresh); + FilterLoop26(v, 1, stride, 8, thresh, ithresh, hev_thresh); +} + +static void VFilter8i(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); + FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); +} + +static void HFilter8i(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh); + FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh); +} + +// on three inner edges +static void VFilter16i(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4 * stride; + FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh); + } +} + +static void HFilter16i(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4; + FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh); + } +} + +//------------------------------------------------------------------------------ +// Simple In-loop filtering (Paragraph 15.2) + +static void SimpleVFilter16(uint8_t* p, int stride, int thresh) { + int i; + const int thresh2 = 2 * thresh + 1; + for (i = 0; i < 16; ++i) { + if (needs_filter(p + i, stride, thresh2)) { + do_filter2(p + i, stride); + } + } +} + +static void SimpleHFilter16(uint8_t* p, int stride, int thresh) { + int i; + const int thresh2 = 2 * thresh + 1; + for (i = 0; i < 16; ++i) { + if (needs_filter(p + i * stride, 1, thresh2)) { + do_filter2(p + i * stride, 1); + } + } +} + +static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4 * stride; + SimpleVFilter16(p, stride, thresh); + } +} + +static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4; + SimpleHFilter16(p, stride, thresh); + } +} + +static void TransformOne(const int16_t* in, uint8_t* dst) { + int temp0, temp1, temp2, temp3, temp4; + int temp5, temp6, temp7, temp8, temp9; + int temp10, temp11, temp12, temp13, temp14; + int temp15, temp16, temp17, temp18; + int16_t* p_in = (int16_t*)in; + + // loops unrolled and merged to avoid usage of tmp buffer + // and to reduce number of stalls. MUL macro is written + // in assembler and inlined + __asm__ volatile( + "lh %[temp0], 0(%[in]) \n\t" + "lh %[temp8], 16(%[in]) \n\t" + "lh %[temp4], 8(%[in]) \n\t" + "lh %[temp12], 24(%[in]) \n\t" + "addu %[temp16], %[temp0], %[temp8] \n\t" + "subu %[temp0], %[temp0], %[temp8] \n\t" + "mul %[temp8], %[temp4], %[kC2] \n\t" + "mul %[temp17], %[temp12], %[kC1] \n\t" + "mul %[temp4], %[temp4], %[kC1] \n\t" + "mul %[temp12], %[temp12], %[kC2] \n\t" + "lh %[temp1], 2(%[in]) \n\t" + "lh %[temp5], 10(%[in]) \n\t" + "lh %[temp9], 18(%[in]) \n\t" + "lh %[temp13], 26(%[in]) \n\t" + "sra %[temp8], %[temp8], 16 \n\t" + "sra %[temp17], %[temp17], 16 \n\t" + "sra %[temp4], %[temp4], 16 \n\t" + "sra %[temp12], %[temp12], 16 \n\t" + "lh %[temp2], 4(%[in]) \n\t" + "lh %[temp6], 12(%[in]) \n\t" + "lh %[temp10], 20(%[in]) \n\t" + "lh %[temp14], 28(%[in]) \n\t" + "subu %[temp17], %[temp8], %[temp17] \n\t" + "addu %[temp4], %[temp4], %[temp12] \n\t" + "addu %[temp8], %[temp16], %[temp4] \n\t" + "subu %[temp4], %[temp16], %[temp4] \n\t" + "addu %[temp16], %[temp1], %[temp9] \n\t" + "subu %[temp1], %[temp1], %[temp9] \n\t" + "lh %[temp3], 6(%[in]) \n\t" + "lh %[temp7], 14(%[in]) \n\t" + "lh %[temp11], 22(%[in]) \n\t" + "lh %[temp15], 30(%[in]) \n\t" + "addu %[temp12], %[temp0], %[temp17] \n\t" + "subu %[temp0], %[temp0], %[temp17] \n\t" + "mul %[temp9], %[temp5], %[kC2] \n\t" + "mul %[temp17], %[temp13], %[kC1] \n\t" + "mul %[temp5], %[temp5], %[kC1] \n\t" + "mul %[temp13], %[temp13], %[kC2] \n\t" + "sra %[temp9], %[temp9], 16 \n\t" + "sra %[temp17], %[temp17], 16 \n\t" + "subu %[temp17], %[temp9], %[temp17] \n\t" + "sra %[temp5], %[temp5], 16 \n\t" + "sra %[temp13], %[temp13], 16 \n\t" + "addu %[temp5], %[temp5], %[temp13] \n\t" + "addu %[temp13], %[temp1], %[temp17] \n\t" + "subu %[temp1], %[temp1], %[temp17] \n\t" + "mul %[temp17], %[temp14], %[kC1] \n\t" + "mul %[temp14], %[temp14], %[kC2] \n\t" + "addu %[temp9], %[temp16], %[temp5] \n\t" + "subu %[temp5], %[temp16], %[temp5] \n\t" + "addu %[temp16], %[temp2], %[temp10] \n\t" + "subu %[temp2], %[temp2], %[temp10] \n\t" + "mul %[temp10], %[temp6], %[kC2] \n\t" + "mul %[temp6], %[temp6], %[kC1] \n\t" + "sra %[temp17], %[temp17], 16 \n\t" + "sra %[temp14], %[temp14], 16 \n\t" + "sra %[temp10], %[temp10], 16 \n\t" + "sra %[temp6], %[temp6], 16 \n\t" + "subu %[temp17], %[temp10], %[temp17] \n\t" + "addu %[temp6], %[temp6], %[temp14] \n\t" + "addu %[temp10], %[temp16], %[temp6] \n\t" + "subu %[temp6], %[temp16], %[temp6] \n\t" + "addu %[temp14], %[temp2], %[temp17] \n\t" + "subu %[temp2], %[temp2], %[temp17] \n\t" + "mul %[temp17], %[temp15], %[kC1] \n\t" + "mul %[temp15], %[temp15], %[kC2] \n\t" + "addu %[temp16], %[temp3], %[temp11] \n\t" + "subu %[temp3], %[temp3], %[temp11] \n\t" + "mul %[temp11], %[temp7], %[kC2] \n\t" + "mul %[temp7], %[temp7], %[kC1] \n\t" + "addiu %[temp8], %[temp8], 4 \n\t" + "addiu %[temp12], %[temp12], 4 \n\t" + "addiu %[temp0], %[temp0], 4 \n\t" + "addiu %[temp4], %[temp4], 4 \n\t" + "sra %[temp17], %[temp17], 16 \n\t" + "sra %[temp15], %[temp15], 16 \n\t" + "sra %[temp11], %[temp11], 16 \n\t" + "sra %[temp7], %[temp7], 16 \n\t" + "subu %[temp17], %[temp11], %[temp17] \n\t" + "addu %[temp7], %[temp7], %[temp15] \n\t" + "addu %[temp15], %[temp3], %[temp17] \n\t" + "subu %[temp3], %[temp3], %[temp17] \n\t" + "addu %[temp11], %[temp16], %[temp7] \n\t" + "subu %[temp7], %[temp16], %[temp7] \n\t" + "addu %[temp16], %[temp8], %[temp10] \n\t" + "subu %[temp8], %[temp8], %[temp10] \n\t" + "mul %[temp10], %[temp9], %[kC2] \n\t" + "mul %[temp17], %[temp11], %[kC1] \n\t" + "mul %[temp9], %[temp9], %[kC1] \n\t" + "mul %[temp11], %[temp11], %[kC2] \n\t" + "sra %[temp10], %[temp10], 16 \n\t" + "sra %[temp17], %[temp17], 16 \n\t" + "sra %[temp9], %[temp9], 16 \n\t" + "sra %[temp11], %[temp11], 16 \n\t" + "subu %[temp17], %[temp10], %[temp17] \n\t" + "addu %[temp11], %[temp9], %[temp11] \n\t" + "addu %[temp10], %[temp12], %[temp14] \n\t" + "subu %[temp12], %[temp12], %[temp14] \n\t" + "mul %[temp14], %[temp13], %[kC2] \n\t" + "mul %[temp9], %[temp15], %[kC1] \n\t" + "mul %[temp13], %[temp13], %[kC1] \n\t" + "mul %[temp15], %[temp15], %[kC2] \n\t" + "sra %[temp14], %[temp14], 16 \n\t" + "sra %[temp9], %[temp9], 16 \n\t" + "sra %[temp13], %[temp13], 16 \n\t" + "sra %[temp15], %[temp15], 16 \n\t" + "subu %[temp9], %[temp14], %[temp9] \n\t" + "addu %[temp15], %[temp13], %[temp15] \n\t" + "addu %[temp14], %[temp0], %[temp2] \n\t" + "subu %[temp0], %[temp0], %[temp2] \n\t" + "mul %[temp2], %[temp1], %[kC2] \n\t" + "mul %[temp13], %[temp3], %[kC1] \n\t" + "mul %[temp1], %[temp1], %[kC1] \n\t" + "mul %[temp3], %[temp3], %[kC2] \n\t" + "sra %[temp2], %[temp2], 16 \n\t" + "sra %[temp13], %[temp13], 16 \n\t" + "sra %[temp1], %[temp1], 16 \n\t" + "sra %[temp3], %[temp3], 16 \n\t" + "subu %[temp13], %[temp2], %[temp13] \n\t" + "addu %[temp3], %[temp1], %[temp3] \n\t" + "addu %[temp2], %[temp4], %[temp6] \n\t" + "subu %[temp4], %[temp4], %[temp6] \n\t" + "mul %[temp6], %[temp5], %[kC2] \n\t" + "mul %[temp1], %[temp7], %[kC1] \n\t" + "mul %[temp5], %[temp5], %[kC1] \n\t" + "mul %[temp7], %[temp7], %[kC2] \n\t" + "sra %[temp6], %[temp6], 16 \n\t" + "sra %[temp1], %[temp1], 16 \n\t" + "sra %[temp5], %[temp5], 16 \n\t" + "sra %[temp7], %[temp7], 16 \n\t" + "subu %[temp1], %[temp6], %[temp1] \n\t" + "addu %[temp7], %[temp5], %[temp7] \n\t" + "addu %[temp5], %[temp16], %[temp11] \n\t" + "subu %[temp16], %[temp16], %[temp11] \n\t" + "addu %[temp11], %[temp8], %[temp17] \n\t" + "subu %[temp8], %[temp8], %[temp17] \n\t" + "sra %[temp5], %[temp5], 3 \n\t" + "sra %[temp16], %[temp16], 3 \n\t" + "sra %[temp11], %[temp11], 3 \n\t" + "sra %[temp8], %[temp8], 3 \n\t" + "addu %[temp17], %[temp10], %[temp15] \n\t" + "subu %[temp10], %[temp10], %[temp15] \n\t" + "addu %[temp15], %[temp12], %[temp9] \n\t" + "subu %[temp12], %[temp12], %[temp9] \n\t" + "sra %[temp17], %[temp17], 3 \n\t" + "sra %[temp10], %[temp10], 3 \n\t" + "sra %[temp15], %[temp15], 3 \n\t" + "sra %[temp12], %[temp12], 3 \n\t" + "addu %[temp9], %[temp14], %[temp3] \n\t" + "subu %[temp14], %[temp14], %[temp3] \n\t" + "addu %[temp3], %[temp0], %[temp13] \n\t" + "subu %[temp0], %[temp0], %[temp13] \n\t" + "sra %[temp9], %[temp9], 3 \n\t" + "sra %[temp14], %[temp14], 3 \n\t" + "sra %[temp3], %[temp3], 3 \n\t" + "sra %[temp0], %[temp0], 3 \n\t" + "addu %[temp13], %[temp2], %[temp7] \n\t" + "subu %[temp2], %[temp2], %[temp7] \n\t" + "addu %[temp7], %[temp4], %[temp1] \n\t" + "subu %[temp4], %[temp4], %[temp1] \n\t" + "sra %[temp13], %[temp13], 3 \n\t" + "sra %[temp2], %[temp2], 3 \n\t" + "sra %[temp7], %[temp7], 3 \n\t" + "sra %[temp4], %[temp4], 3 \n\t" + "addiu %[temp6], $zero, 255 \n\t" + "lbu %[temp1], 0+0*" XSTR(BPS) "(%[dst]) \n\t" + "addu %[temp1], %[temp1], %[temp5] \n\t" + "sra %[temp5], %[temp1], 8 \n\t" + "sra %[temp18], %[temp1], 31 \n\t" + "beqz %[temp5], 1f \n\t" + "xor %[temp1], %[temp1], %[temp1] \n\t" + "movz %[temp1], %[temp6], %[temp18] \n\t" + "1: \n\t" + "lbu %[temp18], 1+0*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp1], 0+0*" XSTR(BPS) "(%[dst]) \n\t" + "addu %[temp18], %[temp18], %[temp11] \n\t" + "sra %[temp11], %[temp18], 8 \n\t" + "sra %[temp1], %[temp18], 31 \n\t" + "beqz %[temp11], 2f \n\t" + "xor %[temp18], %[temp18], %[temp18] \n\t" + "movz %[temp18], %[temp6], %[temp1] \n\t" + "2: \n\t" + "lbu %[temp1], 2+0*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp18], 1+0*" XSTR(BPS) "(%[dst]) \n\t" + "addu %[temp1], %[temp1], %[temp8] \n\t" + "sra %[temp8], %[temp1], 8 \n\t" + "sra %[temp18], %[temp1], 31 \n\t" + "beqz %[temp8], 3f \n\t" + "xor %[temp1], %[temp1], %[temp1] \n\t" + "movz %[temp1], %[temp6], %[temp18] \n\t" + "3: \n\t" + "lbu %[temp18], 3+0*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp1], 2+0*" XSTR(BPS) "(%[dst]) \n\t" + "addu %[temp18], %[temp18], %[temp16] \n\t" + "sra %[temp16], %[temp18], 8 \n\t" + "sra %[temp1], %[temp18], 31 \n\t" + "beqz %[temp16], 4f \n\t" + "xor %[temp18], %[temp18], %[temp18] \n\t" + "movz %[temp18], %[temp6], %[temp1] \n\t" + "4: \n\t" + "sb %[temp18], 3+0*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp5], 0+1*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp8], 1+1*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp11], 2+1*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp16], 3+1*" XSTR(BPS) "(%[dst]) \n\t" + "addu %[temp5], %[temp5], %[temp17] \n\t" + "addu %[temp8], %[temp8], %[temp15] \n\t" + "addu %[temp11], %[temp11], %[temp12] \n\t" + "addu %[temp16], %[temp16], %[temp10] \n\t" + "sra %[temp18], %[temp5], 8 \n\t" + "sra %[temp1], %[temp5], 31 \n\t" + "beqz %[temp18], 5f \n\t" + "xor %[temp5], %[temp5], %[temp5] \n\t" + "movz %[temp5], %[temp6], %[temp1] \n\t" + "5: \n\t" + "sra %[temp18], %[temp8], 8 \n\t" + "sra %[temp1], %[temp8], 31 \n\t" + "beqz %[temp18], 6f \n\t" + "xor %[temp8], %[temp8], %[temp8] \n\t" + "movz %[temp8], %[temp6], %[temp1] \n\t" + "6: \n\t" + "sra %[temp18], %[temp11], 8 \n\t" + "sra %[temp1], %[temp11], 31 \n\t" + "sra %[temp17], %[temp16], 8 \n\t" + "sra %[temp15], %[temp16], 31 \n\t" + "beqz %[temp18], 7f \n\t" + "xor %[temp11], %[temp11], %[temp11] \n\t" + "movz %[temp11], %[temp6], %[temp1] \n\t" + "7: \n\t" + "beqz %[temp17], 8f \n\t" + "xor %[temp16], %[temp16], %[temp16] \n\t" + "movz %[temp16], %[temp6], %[temp15] \n\t" + "8: \n\t" + "sb %[temp5], 0+1*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp8], 1+1*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp11], 2+1*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp16], 3+1*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp5], 0+2*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp8], 1+2*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp11], 2+2*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp16], 3+2*" XSTR(BPS) "(%[dst]) \n\t" + "addu %[temp5], %[temp5], %[temp9] \n\t" + "addu %[temp8], %[temp8], %[temp3] \n\t" + "addu %[temp11], %[temp11], %[temp0] \n\t" + "addu %[temp16], %[temp16], %[temp14] \n\t" + "sra %[temp18], %[temp5], 8 \n\t" + "sra %[temp1], %[temp5], 31 \n\t" + "sra %[temp17], %[temp8], 8 \n\t" + "sra %[temp15], %[temp8], 31 \n\t" + "sra %[temp12], %[temp11], 8 \n\t" + "sra %[temp10], %[temp11], 31 \n\t" + "sra %[temp9], %[temp16], 8 \n\t" + "sra %[temp3], %[temp16], 31 \n\t" + "beqz %[temp18], 9f \n\t" + "xor %[temp5], %[temp5], %[temp5] \n\t" + "movz %[temp5], %[temp6], %[temp1] \n\t" + "9: \n\t" + "beqz %[temp17], 10f \n\t" + "xor %[temp8], %[temp8], %[temp8] \n\t" + "movz %[temp8], %[temp6], %[temp15] \n\t" + "10: \n\t" + "beqz %[temp12], 11f \n\t" + "xor %[temp11], %[temp11], %[temp11] \n\t" + "movz %[temp11], %[temp6], %[temp10] \n\t" + "11: \n\t" + "beqz %[temp9], 12f \n\t" + "xor %[temp16], %[temp16], %[temp16] \n\t" + "movz %[temp16], %[temp6], %[temp3] \n\t" + "12: \n\t" + "sb %[temp5], 0+2*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp8], 1+2*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp11], 2+2*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp16], 3+2*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp5], 0+3*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp8], 1+3*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp11], 2+3*" XSTR(BPS) "(%[dst]) \n\t" + "lbu %[temp16], 3+3*" XSTR(BPS) "(%[dst]) \n\t" + "addu %[temp5], %[temp5], %[temp13] \n\t" + "addu %[temp8], %[temp8], %[temp7] \n\t" + "addu %[temp11], %[temp11], %[temp4] \n\t" + "addu %[temp16], %[temp16], %[temp2] \n\t" + "sra %[temp18], %[temp5], 8 \n\t" + "sra %[temp1], %[temp5], 31 \n\t" + "sra %[temp17], %[temp8], 8 \n\t" + "sra %[temp15], %[temp8], 31 \n\t" + "sra %[temp12], %[temp11], 8 \n\t" + "sra %[temp10], %[temp11], 31 \n\t" + "sra %[temp9], %[temp16], 8 \n\t" + "sra %[temp3], %[temp16], 31 \n\t" + "beqz %[temp18], 13f \n\t" + "xor %[temp5], %[temp5], %[temp5] \n\t" + "movz %[temp5], %[temp6], %[temp1] \n\t" + "13: \n\t" + "beqz %[temp17], 14f \n\t" + "xor %[temp8], %[temp8], %[temp8] \n\t" + "movz %[temp8], %[temp6], %[temp15] \n\t" + "14: \n\t" + "beqz %[temp12], 15f \n\t" + "xor %[temp11], %[temp11], %[temp11] \n\t" + "movz %[temp11], %[temp6], %[temp10] \n\t" + "15: \n\t" + "beqz %[temp9], 16f \n\t" + "xor %[temp16], %[temp16], %[temp16] \n\t" + "movz %[temp16], %[temp6], %[temp3] \n\t" + "16: \n\t" + "sb %[temp5], 0+3*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp8], 1+3*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp11], 2+3*" XSTR(BPS) "(%[dst]) \n\t" + "sb %[temp16], 3+3*" XSTR(BPS) "(%[dst]) \n\t" + + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11), + [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), [temp14]"=&r"(temp14), + [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), [temp17]"=&r"(temp17), + [temp18]"=&r"(temp18) + : [in]"r"(p_in), [kC1]"r"(kC1), [kC2]"r"(kC2), [dst]"r"(dst) + : "memory", "hi", "lo" + ); +} + +static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) { + TransformOne(in, dst); + if (do_two) { + TransformOne(in + 16, dst + 4); + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8DspInitMIPS32(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitMIPS32(void) { + VP8InitClipTables(); + + VP8Transform = TransformTwo; + + VP8VFilter16 = VFilter16; + VP8HFilter16 = HFilter16; + VP8VFilter8 = VFilter8; + VP8HFilter8 = HFilter8; + VP8VFilter16i = VFilter16i; + VP8HFilter16i = HFilter16i; + VP8VFilter8i = VFilter8i; + VP8HFilter8i = HFilter8i; + + VP8SimpleVFilter16 = SimpleVFilter16; + VP8SimpleHFilter16 = SimpleHFilter16; + VP8SimpleVFilter16i = SimpleVFilter16i; + VP8SimpleHFilter16i = SimpleHFilter16i; +} + +#else // !WEBP_USE_MIPS32 + +WEBP_DSP_INIT_STUB(VP8DspInitMIPS32) + +#endif // WEBP_USE_MIPS32 diff --git a/internal/libwebp-1.2.1/src/dsp/dec_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/dec_mips_dsp_r2.c new file mode 100644 index 0000000..b0936bc --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/dec_mips_dsp_r2.c @@ -0,0 +1,994 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS version of dsp functions +// +// Author(s): Djordje Pesut (djordje.pesut@imgtec.com) +// Jovan Zelincevic (jovan.zelincevic@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) + +#include "src/dsp/mips_macro.h" + +static const int kC1 = 20091 + (1 << 16); +static const int kC2 = 35468; + +#define MUL(a, b) (((a) * (b)) >> 16) + +static void TransformDC(const int16_t* in, uint8_t* dst) { + int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9, temp10; + + __asm__ volatile ( + LOAD_WITH_OFFSET_X4(temp1, temp2, temp3, temp4, dst, + 0, 0, 0, 0, + 0, 1, 2, 3, + BPS) + "lh %[temp5], 0(%[in]) \n\t" + "addiu %[temp5], %[temp5], 4 \n\t" + "ins %[temp5], %[temp5], 16, 16 \n\t" + "shra.ph %[temp5], %[temp5], 3 \n\t" + CONVERT_2_BYTES_TO_HALF(temp6, temp7, temp8, temp9, temp10, temp1, temp2, + temp3, temp1, temp2, temp3, temp4) + STORE_SAT_SUM_X2(temp6, temp7, temp8, temp9, temp10, temp1, temp2, temp3, + temp5, temp5, temp5, temp5, temp5, temp5, temp5, temp5, + dst, 0, 1, 2, 3, BPS) + + OUTPUT_EARLY_CLOBBER_REGS_10() + : [in]"r"(in), [dst]"r"(dst) + : "memory" + ); +} + +static void TransformAC3(const int16_t* in, uint8_t* dst) { + const int a = in[0] + 4; + int c4 = MUL(in[4], kC2); + const int d4 = MUL(in[4], kC1); + const int c1 = MUL(in[1], kC2); + const int d1 = MUL(in[1], kC1); + int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9; + int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18; + + __asm__ volatile ( + "ins %[c4], %[d4], 16, 16 \n\t" + "replv.ph %[temp1], %[a] \n\t" + "replv.ph %[temp4], %[d1] \n\t" + ADD_SUB_HALVES(temp2, temp3, temp1, c4) + "replv.ph %[temp5], %[c1] \n\t" + SHIFT_R_SUM_X2(temp1, temp6, temp7, temp8, temp2, temp9, temp10, temp4, + temp2, temp2, temp3, temp3, temp4, temp5, temp4, temp5) + LOAD_WITH_OFFSET_X4(temp3, temp5, temp11, temp12, dst, + 0, 0, 0, 0, + 0, 1, 2, 3, + BPS) + CONVERT_2_BYTES_TO_HALF(temp13, temp14, temp3, temp15, temp5, temp16, + temp11, temp17, temp3, temp5, temp11, temp12) + PACK_2_HALVES_TO_WORD(temp12, temp18, temp7, temp6, temp1, temp8, temp2, + temp4, temp7, temp6, temp10, temp9) + STORE_SAT_SUM_X2(temp13, temp14, temp3, temp15, temp5, temp16, temp11, + temp17, temp12, temp18, temp1, temp8, temp2, temp4, + temp7, temp6, dst, 0, 1, 2, 3, BPS) + + OUTPUT_EARLY_CLOBBER_REGS_18(), + [c4]"+&r"(c4) + : [dst]"r"(dst), [a]"r"(a), [d1]"r"(d1), [d4]"r"(d4), [c1]"r"(c1) + : "memory" + ); +} + +static void TransformOne(const int16_t* in, uint8_t* dst) { + int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9; + int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18; + + __asm__ volatile ( + "ulw %[temp1], 0(%[in]) \n\t" + "ulw %[temp2], 16(%[in]) \n\t" + LOAD_IN_X2(temp5, temp6, 24, 26) + ADD_SUB_HALVES(temp3, temp4, temp1, temp2) + LOAD_IN_X2(temp1, temp2, 8, 10) + MUL_SHIFT_SUM(temp7, temp8, temp9, temp10, temp11, temp12, temp13, temp14, + temp10, temp8, temp9, temp7, temp1, temp2, temp5, temp6, + temp13, temp11, temp14, temp12) + INSERT_HALF_X2(temp8, temp7, temp10, temp9) + "ulw %[temp17], 4(%[in]) \n\t" + "ulw %[temp18], 20(%[in]) \n\t" + ADD_SUB_HALVES(temp1, temp2, temp3, temp8) + ADD_SUB_HALVES(temp5, temp6, temp4, temp7) + ADD_SUB_HALVES(temp7, temp8, temp17, temp18) + LOAD_IN_X2(temp17, temp18, 12, 14) + LOAD_IN_X2(temp9, temp10, 28, 30) + MUL_SHIFT_SUM(temp11, temp12, temp13, temp14, temp15, temp16, temp4, temp17, + temp12, temp14, temp11, temp13, temp17, temp18, temp9, temp10, + temp15, temp4, temp16, temp17) + INSERT_HALF_X2(temp11, temp12, temp13, temp14) + ADD_SUB_HALVES(temp17, temp8, temp8, temp11) + ADD_SUB_HALVES(temp3, temp4, temp7, temp12) + + // horizontal + SRA_16(temp9, temp10, temp11, temp12, temp1, temp2, temp5, temp6) + INSERT_HALF_X2(temp1, temp6, temp5, temp2) + SRA_16(temp13, temp14, temp15, temp16, temp3, temp4, temp17, temp8) + "repl.ph %[temp2], 0x4 \n\t" + INSERT_HALF_X2(temp3, temp8, temp17, temp4) + "addq.ph %[temp1], %[temp1], %[temp2] \n\t" + "addq.ph %[temp6], %[temp6], %[temp2] \n\t" + ADD_SUB_HALVES(temp2, temp4, temp1, temp3) + ADD_SUB_HALVES(temp5, temp7, temp6, temp8) + MUL_SHIFT_SUM(temp1, temp3, temp6, temp8, temp9, temp13, temp17, temp18, + temp3, temp13, temp1, temp9, temp9, temp13, temp11, temp15, + temp6, temp17, temp8, temp18) + MUL_SHIFT_SUM(temp6, temp8, temp18, temp17, temp11, temp15, temp12, temp16, + temp8, temp15, temp6, temp11, temp12, temp16, temp10, temp14, + temp18, temp12, temp17, temp16) + INSERT_HALF_X2(temp1, temp3, temp9, temp13) + INSERT_HALF_X2(temp6, temp8, temp11, temp15) + SHIFT_R_SUM_X2(temp9, temp10, temp11, temp12, temp13, temp14, temp15, + temp16, temp2, temp4, temp5, temp7, temp3, temp1, temp8, + temp6) + PACK_2_HALVES_TO_WORD(temp1, temp2, temp3, temp4, temp9, temp12, temp13, + temp16, temp11, temp10, temp15, temp14) + LOAD_WITH_OFFSET_X4(temp10, temp11, temp14, temp15, dst, + 0, 0, 0, 0, + 0, 1, 2, 3, + BPS) + CONVERT_2_BYTES_TO_HALF(temp5, temp6, temp7, temp8, temp17, temp18, temp10, + temp11, temp10, temp11, temp14, temp15) + STORE_SAT_SUM_X2(temp5, temp6, temp7, temp8, temp17, temp18, temp10, temp11, + temp9, temp12, temp1, temp2, temp13, temp16, temp3, temp4, + dst, 0, 1, 2, 3, BPS) + + OUTPUT_EARLY_CLOBBER_REGS_18() + : [dst]"r"(dst), [in]"r"(in), [kC1]"r"(kC1), [kC2]"r"(kC2) + : "memory", "hi", "lo" + ); +} + +static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) { + TransformOne(in, dst); + if (do_two) { + TransformOne(in + 16, dst + 4); + } +} + +static WEBP_INLINE void FilterLoop26(uint8_t* p, + int hstride, int vstride, int size, + int thresh, int ithresh, int hev_thresh) { + const int thresh2 = 2 * thresh + 1; + int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9; + int temp10, temp11, temp12, temp13, temp14, temp15; + + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "1: \n\t" + "negu %[temp1], %[hstride] \n\t" + "addiu %[size], %[size], -1 \n\t" + "sll %[temp2], %[hstride], 1 \n\t" + "sll %[temp3], %[temp1], 1 \n\t" + "addu %[temp4], %[temp2], %[hstride] \n\t" + "addu %[temp5], %[temp3], %[temp1] \n\t" + "lbu %[temp7], 0(%[p]) \n\t" + "sll %[temp6], %[temp3], 1 \n\t" + "lbux %[temp8], %[temp5](%[p]) \n\t" + "lbux %[temp9], %[temp3](%[p]) \n\t" + "lbux %[temp10], %[temp1](%[p]) \n\t" + "lbux %[temp11], %[temp6](%[p]) \n\t" + "lbux %[temp12], %[hstride](%[p]) \n\t" + "lbux %[temp13], %[temp2](%[p]) \n\t" + "lbux %[temp14], %[temp4](%[p]) \n\t" + "subu %[temp1], %[temp10], %[temp7] \n\t" + "subu %[temp2], %[temp9], %[temp12] \n\t" + "absq_s.w %[temp3], %[temp1] \n\t" + "absq_s.w %[temp4], %[temp2] \n\t" + "negu %[temp1], %[temp1] \n\t" + "sll %[temp3], %[temp3], 2 \n\t" + "addu %[temp15], %[temp3], %[temp4] \n\t" + "subu %[temp3], %[temp15], %[thresh2] \n\t" + "sll %[temp6], %[temp1], 1 \n\t" + "bgtz %[temp3], 3f \n\t" + " subu %[temp4], %[temp11], %[temp8] \n\t" + "absq_s.w %[temp4], %[temp4] \n\t" + "shll_s.w %[temp2], %[temp2], 24 \n\t" + "subu %[temp4], %[temp4], %[ithresh] \n\t" + "bgtz %[temp4], 3f \n\t" + " subu %[temp3], %[temp8], %[temp9] \n\t" + "absq_s.w %[temp3], %[temp3] \n\t" + "subu %[temp3], %[temp3], %[ithresh] \n\t" + "bgtz %[temp3], 3f \n\t" + " subu %[temp5], %[temp9], %[temp10] \n\t" + "absq_s.w %[temp3], %[temp5] \n\t" + "absq_s.w %[temp5], %[temp5] \n\t" + "subu %[temp3], %[temp3], %[ithresh] \n\t" + "bgtz %[temp3], 3f \n\t" + " subu %[temp3], %[temp14], %[temp13] \n\t" + "absq_s.w %[temp3], %[temp3] \n\t" + "slt %[temp5], %[hev_thresh], %[temp5] \n\t" + "subu %[temp3], %[temp3], %[ithresh] \n\t" + "bgtz %[temp3], 3f \n\t" + " subu %[temp3], %[temp13], %[temp12] \n\t" + "absq_s.w %[temp3], %[temp3] \n\t" + "sra %[temp4], %[temp2], 24 \n\t" + "subu %[temp3], %[temp3], %[ithresh] \n\t" + "bgtz %[temp3], 3f \n\t" + " subu %[temp15], %[temp12], %[temp7] \n\t" + "absq_s.w %[temp3], %[temp15] \n\t" + "absq_s.w %[temp15], %[temp15] \n\t" + "subu %[temp3], %[temp3], %[ithresh] \n\t" + "bgtz %[temp3], 3f \n\t" + " slt %[temp15], %[hev_thresh], %[temp15] \n\t" + "addu %[temp3], %[temp6], %[temp1] \n\t" + "or %[temp2], %[temp5], %[temp15] \n\t" + "addu %[temp5], %[temp4], %[temp3] \n\t" + "beqz %[temp2], 4f \n\t" + " shra_r.w %[temp1], %[temp5], 3 \n\t" + "addiu %[temp2], %[temp5], 3 \n\t" + "sra %[temp2], %[temp2], 3 \n\t" + "shll_s.w %[temp1], %[temp1], 27 \n\t" + "shll_s.w %[temp2], %[temp2], 27 \n\t" + "subu %[temp3], %[p], %[hstride] \n\t" + "sra %[temp1], %[temp1], 27 \n\t" + "sra %[temp2], %[temp2], 27 \n\t" + "subu %[temp1], %[temp7], %[temp1] \n\t" + "addu %[temp2], %[temp10], %[temp2] \n\t" + "lbux %[temp2], %[temp2](%[VP8kclip1]) \n\t" + "lbux %[temp1], %[temp1](%[VP8kclip1]) \n\t" + "sb %[temp2], 0(%[temp3]) \n\t" + "j 3f \n\t" + " sb %[temp1], 0(%[p]) \n\t" + "4: \n\t" + "shll_s.w %[temp5], %[temp5], 24 \n\t" + "subu %[temp14], %[p], %[hstride] \n\t" + "subu %[temp11], %[temp14], %[hstride] \n\t" + "sra %[temp6], %[temp5], 24 \n\t" + "sll %[temp1], %[temp6], 3 \n\t" + "subu %[temp15], %[temp11], %[hstride] \n\t" + "addu %[temp2], %[temp6], %[temp1] \n\t" + "sll %[temp3], %[temp2], 1 \n\t" + "addu %[temp4], %[temp3], %[temp2] \n\t" + "addiu %[temp2], %[temp2], 63 \n\t" + "addiu %[temp3], %[temp3], 63 \n\t" + "addiu %[temp4], %[temp4], 63 \n\t" + "sra %[temp2], %[temp2], 7 \n\t" + "sra %[temp3], %[temp3], 7 \n\t" + "sra %[temp4], %[temp4], 7 \n\t" + "addu %[temp1], %[temp8], %[temp2] \n\t" + "addu %[temp5], %[temp9], %[temp3] \n\t" + "addu %[temp6], %[temp10], %[temp4] \n\t" + "subu %[temp8], %[temp7], %[temp4] \n\t" + "subu %[temp7], %[temp12], %[temp3] \n\t" + "addu %[temp10], %[p], %[hstride] \n\t" + "subu %[temp9], %[temp13], %[temp2] \n\t" + "addu %[temp12], %[temp10], %[hstride] \n\t" + "lbux %[temp2], %[temp1](%[VP8kclip1]) \n\t" + "lbux %[temp3], %[temp5](%[VP8kclip1]) \n\t" + "lbux %[temp4], %[temp6](%[VP8kclip1]) \n\t" + "lbux %[temp5], %[temp8](%[VP8kclip1]) \n\t" + "lbux %[temp6], %[temp7](%[VP8kclip1]) \n\t" + "lbux %[temp8], %[temp9](%[VP8kclip1]) \n\t" + "sb %[temp2], 0(%[temp15]) \n\t" + "sb %[temp3], 0(%[temp11]) \n\t" + "sb %[temp4], 0(%[temp14]) \n\t" + "sb %[temp5], 0(%[p]) \n\t" + "sb %[temp6], 0(%[temp10]) \n\t" + "sb %[temp8], 0(%[temp12]) \n\t" + "3: \n\t" + "bgtz %[size], 1b \n\t" + " addu %[p], %[p], %[vstride] \n\t" + ".set pop \n\t" + : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),[temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [temp6]"=&r"(temp6), + [temp7]"=&r"(temp7),[temp8]"=&r"(temp8),[temp9]"=&r"(temp9), + [temp10]"=&r"(temp10),[temp11]"=&r"(temp11),[temp12]"=&r"(temp12), + [temp13]"=&r"(temp13),[temp14]"=&r"(temp14),[temp15]"=&r"(temp15), + [size]"+&r"(size), [p]"+&r"(p) + : [hstride]"r"(hstride), [thresh2]"r"(thresh2), + [ithresh]"r"(ithresh),[vstride]"r"(vstride), [hev_thresh]"r"(hev_thresh), + [VP8kclip1]"r"(VP8kclip1) + : "memory" + ); +} + +static WEBP_INLINE void FilterLoop24(uint8_t* p, + int hstride, int vstride, int size, + int thresh, int ithresh, int hev_thresh) { + int p0, q0, p1, q1, p2, q2, p3, q3; + int step1, step2, temp1, temp2, temp3, temp4; + uint8_t* pTemp0; + uint8_t* pTemp1; + const int thresh2 = 2 * thresh + 1; + + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "bltz %[size], 3f \n\t" + " nop \n\t" + "2: \n\t" + "negu %[step1], %[hstride] \n\t" + "lbu %[q0], 0(%[p]) \n\t" + "lbux %[p0], %[step1](%[p]) \n\t" + "subu %[step1], %[step1], %[hstride] \n\t" + "lbux %[q1], %[hstride](%[p]) \n\t" + "subu %[temp1], %[p0], %[q0] \n\t" + "lbux %[p1], %[step1](%[p]) \n\t" + "addu %[step2], %[hstride], %[hstride] \n\t" + "absq_s.w %[temp2], %[temp1] \n\t" + "subu %[temp3], %[p1], %[q1] \n\t" + "absq_s.w %[temp4], %[temp3] \n\t" + "sll %[temp2], %[temp2], 2 \n\t" + "addu %[temp2], %[temp2], %[temp4] \n\t" + "subu %[temp4], %[temp2], %[thresh2] \n\t" + "subu %[step1], %[step1], %[hstride] \n\t" + "bgtz %[temp4], 0f \n\t" + " lbux %[p2], %[step1](%[p]) \n\t" + "subu %[step1], %[step1], %[hstride] \n\t" + "lbux %[q2], %[step2](%[p]) \n\t" + "lbux %[p3], %[step1](%[p]) \n\t" + "subu %[temp4], %[p2], %[p1] \n\t" + "addu %[step2], %[step2], %[hstride] \n\t" + "subu %[temp2], %[p3], %[p2] \n\t" + "absq_s.w %[temp4], %[temp4] \n\t" + "absq_s.w %[temp2], %[temp2] \n\t" + "lbux %[q3], %[step2](%[p]) \n\t" + "subu %[temp4], %[temp4], %[ithresh] \n\t" + "negu %[temp1], %[temp1] \n\t" + "bgtz %[temp4], 0f \n\t" + " subu %[temp2], %[temp2], %[ithresh] \n\t" + "subu %[p3], %[p1], %[p0] \n\t" + "bgtz %[temp2], 0f \n\t" + " absq_s.w %[p3], %[p3] \n\t" + "subu %[temp4], %[q3], %[q2] \n\t" + "subu %[pTemp0], %[p], %[hstride] \n\t" + "absq_s.w %[temp4], %[temp4] \n\t" + "subu %[temp2], %[p3], %[ithresh] \n\t" + "sll %[step1], %[temp1], 1 \n\t" + "bgtz %[temp2], 0f \n\t" + " subu %[temp4], %[temp4], %[ithresh] \n\t" + "subu %[temp2], %[q2], %[q1] \n\t" + "bgtz %[temp4], 0f \n\t" + " absq_s.w %[temp2], %[temp2] \n\t" + "subu %[q3], %[q1], %[q0] \n\t" + "absq_s.w %[q3], %[q3] \n\t" + "subu %[temp2], %[temp2], %[ithresh] \n\t" + "addu %[temp1], %[temp1], %[step1] \n\t" + "bgtz %[temp2], 0f \n\t" + " subu %[temp4], %[q3], %[ithresh] \n\t" + "slt %[p3], %[hev_thresh], %[p3] \n\t" + "bgtz %[temp4], 0f \n\t" + " slt %[q3], %[hev_thresh], %[q3] \n\t" + "or %[q3], %[q3], %[p3] \n\t" + "bgtz %[q3], 1f \n\t" + " shra_r.w %[temp2], %[temp1], 3 \n\t" + "addiu %[temp1], %[temp1], 3 \n\t" + "sra %[temp1], %[temp1], 3 \n\t" + "shll_s.w %[temp2], %[temp2], 27 \n\t" + "shll_s.w %[temp1], %[temp1], 27 \n\t" + "addu %[pTemp1], %[p], %[hstride] \n\t" + "sra %[temp2], %[temp2], 27 \n\t" + "sra %[temp1], %[temp1], 27 \n\t" + "addiu %[step1], %[temp2], 1 \n\t" + "sra %[step1], %[step1], 1 \n\t" + "addu %[p0], %[p0], %[temp1] \n\t" + "addu %[p1], %[p1], %[step1] \n\t" + "subu %[q0], %[q0], %[temp2] \n\t" + "subu %[q1], %[q1], %[step1] \n\t" + "lbux %[temp2], %[p0](%[VP8kclip1]) \n\t" + "lbux %[temp3], %[q0](%[VP8kclip1]) \n\t" + "lbux %[temp4], %[q1](%[VP8kclip1]) \n\t" + "sb %[temp2], 0(%[pTemp0]) \n\t" + "lbux %[temp1], %[p1](%[VP8kclip1]) \n\t" + "subu %[pTemp0], %[pTemp0], %[hstride] \n\t" + "sb %[temp3], 0(%[p]) \n\t" + "sb %[temp4], 0(%[pTemp1]) \n\t" + "j 0f \n\t" + " sb %[temp1], 0(%[pTemp0]) \n\t" + "1: \n\t" + "shll_s.w %[temp3], %[temp3], 24 \n\t" + "sra %[temp3], %[temp3], 24 \n\t" + "addu %[temp1], %[temp1], %[temp3] \n\t" + "shra_r.w %[temp2], %[temp1], 3 \n\t" + "addiu %[temp1], %[temp1], 3 \n\t" + "shll_s.w %[temp2], %[temp2], 27 \n\t" + "sra %[temp1], %[temp1], 3 \n\t" + "shll_s.w %[temp1], %[temp1], 27 \n\t" + "sra %[temp2], %[temp2], 27 \n\t" + "sra %[temp1], %[temp1], 27 \n\t" + "addu %[p0], %[p0], %[temp1] \n\t" + "subu %[q0], %[q0], %[temp2] \n\t" + "lbux %[temp1], %[p0](%[VP8kclip1]) \n\t" + "lbux %[temp2], %[q0](%[VP8kclip1]) \n\t" + "sb %[temp2], 0(%[p]) \n\t" + "sb %[temp1], 0(%[pTemp0]) \n\t" + "0: \n\t" + "subu %[size], %[size], 1 \n\t" + "bgtz %[size], 2b \n\t" + " addu %[p], %[p], %[vstride] \n\t" + "3: \n\t" + ".set pop \n\t" + : [p0]"=&r"(p0), [q0]"=&r"(q0), [p1]"=&r"(p1), [q1]"=&r"(q1), + [p2]"=&r"(p2), [q2]"=&r"(q2), [p3]"=&r"(p3), [q3]"=&r"(q3), + [step2]"=&r"(step2), [step1]"=&r"(step1), [temp1]"=&r"(temp1), + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), + [pTemp0]"=&r"(pTemp0), [pTemp1]"=&r"(pTemp1), [p]"+&r"(p), + [size]"+&r"(size) + : [vstride]"r"(vstride), [ithresh]"r"(ithresh), + [hev_thresh]"r"(hev_thresh), [hstride]"r"(hstride), + [VP8kclip1]"r"(VP8kclip1), [thresh2]"r"(thresh2) + : "memory" + ); +} + +// on macroblock edges +static void VFilter16(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26(p, stride, 1, 16, thresh, ithresh, hev_thresh); +} + +static void HFilter16(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26(p, 1, stride, 16, thresh, ithresh, hev_thresh); +} + +// 8-pixels wide variant, for chroma filtering +static void VFilter8(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26(u, stride, 1, 8, thresh, ithresh, hev_thresh); + FilterLoop26(v, stride, 1, 8, thresh, ithresh, hev_thresh); +} + +static void HFilter8(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop26(u, 1, stride, 8, thresh, ithresh, hev_thresh); + FilterLoop26(v, 1, stride, 8, thresh, ithresh, hev_thresh); +} + +// on three inner edges +static void VFilter16i(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4 * stride; + FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh); + } +} + +static void HFilter16i(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4; + FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh); + } +} + +static void VFilter8i(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); + FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); +} + +static void HFilter8i(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh); + FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh); +} + +#undef MUL + +//------------------------------------------------------------------------------ +// Simple In-loop filtering (Paragraph 15.2) + +static void SimpleVFilter16(uint8_t* p, int stride, int thresh) { + int i; + const int thresh2 = 2 * thresh + 1; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; + uint8_t* p1 = p - stride; + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "li %[i], 16 \n\t" + "0: \n\t" + "negu %[temp4], %[stride] \n\t" + "sll %[temp5], %[temp4], 1 \n\t" + "lbu %[temp2], 0(%[p]) \n\t" + "lbux %[temp3], %[stride](%[p]) \n\t" + "lbux %[temp1], %[temp4](%[p]) \n\t" + "lbux %[temp0], %[temp5](%[p]) \n\t" + "subu %[temp7], %[temp1], %[temp2] \n\t" + "subu %[temp6], %[temp0], %[temp3] \n\t" + "absq_s.w %[temp4], %[temp7] \n\t" + "absq_s.w %[temp5], %[temp6] \n\t" + "sll %[temp4], %[temp4], 2 \n\t" + "subu %[temp5], %[temp5], %[thresh2] \n\t" + "addu %[temp5], %[temp4], %[temp5] \n\t" + "negu %[temp8], %[temp7] \n\t" + "bgtz %[temp5], 1f \n\t" + " addiu %[i], %[i], -1 \n\t" + "sll %[temp4], %[temp8], 1 \n\t" + "shll_s.w %[temp5], %[temp6], 24 \n\t" + "addu %[temp3], %[temp4], %[temp8] \n\t" + "sra %[temp5], %[temp5], 24 \n\t" + "addu %[temp3], %[temp3], %[temp5] \n\t" + "addiu %[temp7], %[temp3], 3 \n\t" + "sra %[temp7], %[temp7], 3 \n\t" + "shra_r.w %[temp8], %[temp3], 3 \n\t" + "shll_s.w %[temp0], %[temp7], 27 \n\t" + "shll_s.w %[temp4], %[temp8], 27 \n\t" + "sra %[temp0], %[temp0], 27 \n\t" + "sra %[temp4], %[temp4], 27 \n\t" + "addu %[temp7], %[temp1], %[temp0] \n\t" + "subu %[temp2], %[temp2], %[temp4] \n\t" + "lbux %[temp3], %[temp7](%[VP8kclip1]) \n\t" + "lbux %[temp4], %[temp2](%[VP8kclip1]) \n\t" + "sb %[temp3], 0(%[p1]) \n\t" + "sb %[temp4], 0(%[p]) \n\t" + "1: \n\t" + "addiu %[p1], %[p1], 1 \n\t" + "bgtz %[i], 0b \n\t" + " addiu %[p], %[p], 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), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [p]"+&r"(p), [i]"=&r"(i), [p1]"+&r"(p1) + : [stride]"r"(stride), [VP8kclip1]"r"(VP8kclip1), [thresh2]"r"(thresh2) + : "memory" + ); +} + +// TEMP0 = SRC[A + A1 * BPS] +// TEMP1 = SRC[B + B1 * BPS] +// TEMP2 = SRC[C + C1 * BPS] +// TEMP3 = SRC[D + D1 * BPS] +#define LOAD_4_BYTES(TEMP0, TEMP1, TEMP2, TEMP3, \ + A, A1, B, B1, C, C1, D, D1, SRC) \ + "lbu %[" #TEMP0 "], " #A "+" #A1 "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \ + "lbu %[" #TEMP1 "], " #B "+" #B1 "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \ + "lbu %[" #TEMP2 "], " #C "+" #C1 "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \ + "lbu %[" #TEMP3 "], " #D "+" #D1 "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \ + +static void SimpleHFilter16(uint8_t* p, int stride, int thresh) { + int i; + const int thresh2 = 2 * thresh + 1; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "li %[i], 16 \n\t" + "0: \n\t" + LOAD_4_BYTES(temp0, temp1, temp2, temp3, -2, 0, -1, 0, 0, 0, 1, 0, p) + "subu %[temp7], %[temp1], %[temp2] \n\t" + "subu %[temp6], %[temp0], %[temp3] \n\t" + "absq_s.w %[temp4], %[temp7] \n\t" + "absq_s.w %[temp5], %[temp6] \n\t" + "sll %[temp4], %[temp4], 2 \n\t" + "addu %[temp5], %[temp4], %[temp5] \n\t" + "subu %[temp5], %[temp5], %[thresh2] \n\t" + "negu %[temp8], %[temp7] \n\t" + "bgtz %[temp5], 1f \n\t" + " addiu %[i], %[i], -1 \n\t" + "sll %[temp4], %[temp8], 1 \n\t" + "shll_s.w %[temp5], %[temp6], 24 \n\t" + "addu %[temp3], %[temp4], %[temp8] \n\t" + "sra %[temp5], %[temp5], 24 \n\t" + "addu %[temp3], %[temp3], %[temp5] \n\t" + "addiu %[temp7], %[temp3], 3 \n\t" + "sra %[temp7], %[temp7], 3 \n\t" + "shra_r.w %[temp8], %[temp3], 3 \n\t" + "shll_s.w %[temp0], %[temp7], 27 \n\t" + "shll_s.w %[temp4], %[temp8], 27 \n\t" + "sra %[temp0], %[temp0], 27 \n\t" + "sra %[temp4], %[temp4], 27 \n\t" + "addu %[temp7], %[temp1], %[temp0] \n\t" + "subu %[temp2], %[temp2], %[temp4] \n\t" + "lbux %[temp3], %[temp7](%[VP8kclip1]) \n\t" + "lbux %[temp4], %[temp2](%[VP8kclip1]) \n\t" + "sb %[temp3], -1(%[p]) \n\t" + "sb %[temp4], 0(%[p]) \n\t" + "1: \n\t" + "bgtz %[i], 0b \n\t" + " addu %[p], %[p], %[stride] \n\t" + ".set pop \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [p]"+&r"(p), [i]"=&r"(i) + : [stride]"r"(stride), [VP8kclip1]"r"(VP8kclip1), [thresh2]"r"(thresh2) + : "memory" + ); +} + +static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4 * stride; + SimpleVFilter16(p, stride, thresh); + } +} + +static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4; + SimpleHFilter16(p, stride, thresh); + } +} + +// DST[A * BPS] = TEMP0 +// DST[B + C * BPS] = TEMP1 +#define STORE_8_BYTES(TEMP0, TEMP1, A, B, C, DST) \ + "usw %[" #TEMP0 "], " #A "*" XSTR(BPS) "(%[" #DST "]) \n\t" \ + "usw %[" #TEMP1 "], " #B "+" #C "*" XSTR(BPS) "(%[" #DST "]) \n\t" + +static void VE4(uint8_t* dst) { // vertical + const uint8_t* top = dst - BPS; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6; + __asm__ volatile ( + "ulw %[temp0], -1(%[top]) \n\t" + "ulh %[temp1], 3(%[top]) \n\t" + "preceu.ph.qbr %[temp2], %[temp0] \n\t" + "preceu.ph.qbl %[temp3], %[temp0] \n\t" + "preceu.ph.qbr %[temp4], %[temp1] \n\t" + "packrl.ph %[temp5], %[temp3], %[temp2] \n\t" + "packrl.ph %[temp6], %[temp4], %[temp3] \n\t" + "shll.ph %[temp5], %[temp5], 1 \n\t" + "shll.ph %[temp6], %[temp6], 1 \n\t" + "addq.ph %[temp2], %[temp5], %[temp2] \n\t" + "addq.ph %[temp6], %[temp6], %[temp4] \n\t" + "addq.ph %[temp2], %[temp2], %[temp3] \n\t" + "addq.ph %[temp6], %[temp6], %[temp3] \n\t" + "shra_r.ph %[temp2], %[temp2], 2 \n\t" + "shra_r.ph %[temp6], %[temp6], 2 \n\t" + "precr.qb.ph %[temp4], %[temp6], %[temp2] \n\t" + STORE_8_BYTES(temp4, temp4, 0, 0, 1, dst) + STORE_8_BYTES(temp4, temp4, 2, 0, 3, dst) + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void DC4(uint8_t* dst) { // DC + int temp0, temp1, temp2, temp3, temp4; + __asm__ volatile ( + "ulw %[temp0], -1*" XSTR(BPS) "(%[dst]) \n\t" + LOAD_4_BYTES(temp1, temp2, temp3, temp4, -1, 0, -1, 1, -1, 2, -1, 3, dst) + "ins %[temp1], %[temp2], 8, 8 \n\t" + "ins %[temp1], %[temp3], 16, 8 \n\t" + "ins %[temp1], %[temp4], 24, 8 \n\t" + "raddu.w.qb %[temp0], %[temp0] \n\t" + "raddu.w.qb %[temp1], %[temp1] \n\t" + "addu %[temp0], %[temp0], %[temp1] \n\t" + "shra_r.w %[temp0], %[temp0], 3 \n\t" + "replv.qb %[temp0], %[temp0] \n\t" + STORE_8_BYTES(temp0, temp0, 0, 0, 1, dst) + STORE_8_BYTES(temp0, temp0, 2, 0, 3, dst) + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4) + : [dst]"r"(dst) + : "memory" + ); +} + +static void RD4(uint8_t* dst) { // Down-right + int temp0, temp1, temp2, temp3, temp4; + int temp5, temp6, temp7, temp8; + __asm__ volatile ( + LOAD_4_BYTES(temp0, temp1, temp2, temp3, -1, 0, -1, 1, -1, 2, -1, 3, dst) + "ulw %[temp7], -1-" XSTR(BPS) "(%[dst]) \n\t" + "ins %[temp1], %[temp0], 16, 16 \n\t" + "preceu.ph.qbr %[temp5], %[temp7] \n\t" + "ins %[temp2], %[temp1], 16, 16 \n\t" + "preceu.ph.qbl %[temp4], %[temp7] \n\t" + "ins %[temp3], %[temp2], 16, 16 \n\t" + "shll.ph %[temp2], %[temp2], 1 \n\t" + "addq.ph %[temp3], %[temp3], %[temp1] \n\t" + "packrl.ph %[temp6], %[temp5], %[temp1] \n\t" + "addq.ph %[temp3], %[temp3], %[temp2] \n\t" + "addq.ph %[temp1], %[temp1], %[temp5] \n\t" + "shll.ph %[temp6], %[temp6], 1 \n\t" + "addq.ph %[temp1], %[temp1], %[temp6] \n\t" + "packrl.ph %[temp0], %[temp4], %[temp5] \n\t" + "addq.ph %[temp8], %[temp5], %[temp4] \n\t" + "shra_r.ph %[temp3], %[temp3], 2 \n\t" + "shll.ph %[temp0], %[temp0], 1 \n\t" + "shra_r.ph %[temp1], %[temp1], 2 \n\t" + "addq.ph %[temp8], %[temp0], %[temp8] \n\t" + "lbu %[temp5], 3-" XSTR(BPS) "(%[dst]) \n\t" + "precrq.ph.w %[temp7], %[temp7], %[temp7] \n\t" + "shra_r.ph %[temp8], %[temp8], 2 \n\t" + "ins %[temp7], %[temp5], 0, 8 \n\t" + "precr.qb.ph %[temp2], %[temp1], %[temp3] \n\t" + "raddu.w.qb %[temp4], %[temp7] \n\t" + "precr.qb.ph %[temp6], %[temp8], %[temp1] \n\t" + "shra_r.w %[temp4], %[temp4], 2 \n\t" + STORE_8_BYTES(temp2, temp6, 3, 0, 1, dst) + "prepend %[temp2], %[temp8], 8 \n\t" + "prepend %[temp6], %[temp4], 8 \n\t" + STORE_8_BYTES(temp2, temp6, 2, 0, 0, dst) + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8) + : [dst]"r"(dst) + : "memory" + ); +} + +// TEMP0 = SRC[A * BPS] +// TEMP1 = SRC[B + C * BPS] +#define LOAD_8_BYTES(TEMP0, TEMP1, A, B, C, SRC) \ + "ulw %[" #TEMP0 "], " #A "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \ + "ulw %[" #TEMP1 "], " #B "+" #C "*" XSTR(BPS) "(%[" #SRC "]) \n\t" + +static void LD4(uint8_t* dst) { // Down-Left + int temp0, temp1, temp2, temp3, temp4; + int temp5, temp6, temp7, temp8, temp9; + __asm__ volatile ( + LOAD_8_BYTES(temp0, temp1, -1, 4, -1, dst) + "preceu.ph.qbl %[temp2], %[temp0] \n\t" + "preceu.ph.qbr %[temp3], %[temp0] \n\t" + "preceu.ph.qbr %[temp4], %[temp1] \n\t" + "preceu.ph.qbl %[temp5], %[temp1] \n\t" + "packrl.ph %[temp6], %[temp2], %[temp3] \n\t" + "packrl.ph %[temp7], %[temp4], %[temp2] \n\t" + "packrl.ph %[temp8], %[temp5], %[temp4] \n\t" + "shll.ph %[temp6], %[temp6], 1 \n\t" + "addq.ph %[temp9], %[temp2], %[temp6] \n\t" + "shll.ph %[temp7], %[temp7], 1 \n\t" + "addq.ph %[temp9], %[temp9], %[temp3] \n\t" + "shll.ph %[temp8], %[temp8], 1 \n\t" + "shra_r.ph %[temp9], %[temp9], 2 \n\t" + "addq.ph %[temp3], %[temp4], %[temp7] \n\t" + "addq.ph %[temp0], %[temp5], %[temp8] \n\t" + "addq.ph %[temp3], %[temp3], %[temp2] \n\t" + "addq.ph %[temp0], %[temp0], %[temp4] \n\t" + "shra_r.ph %[temp3], %[temp3], 2 \n\t" + "shra_r.ph %[temp0], %[temp0], 2 \n\t" + "srl %[temp1], %[temp1], 24 \n\t" + "sll %[temp1], %[temp1], 1 \n\t" + "raddu.w.qb %[temp5], %[temp5] \n\t" + "precr.qb.ph %[temp9], %[temp3], %[temp9] \n\t" + "precr.qb.ph %[temp3], %[temp0], %[temp3] \n\t" + "addu %[temp1], %[temp1], %[temp5] \n\t" + "shra_r.w %[temp1], %[temp1], 2 \n\t" + STORE_8_BYTES(temp9, temp3, 0, 0, 2, dst) + "prepend %[temp9], %[temp0], 8 \n\t" + "prepend %[temp3], %[temp1], 8 \n\t" + STORE_8_BYTES(temp9, temp3, 1, 0, 3, dst) + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9) + : [dst]"r"(dst) + : "memory" + ); +} + +//------------------------------------------------------------------------------ +// Chroma + +static void DC8uv(uint8_t* dst) { // DC + int temp0, temp1, temp2, temp3, temp4; + int temp5, temp6, temp7, temp8, temp9; + __asm__ volatile ( + LOAD_8_BYTES(temp0, temp1, -1, 4, -1, dst) + LOAD_4_BYTES(temp2, temp3, temp4, temp5, -1, 0, -1, 1, -1, 2, -1, 3, dst) + LOAD_4_BYTES(temp6, temp7, temp8, temp9, -1, 4, -1, 5, -1, 6, -1, 7, dst) + "raddu.w.qb %[temp0], %[temp0] \n\t" + "raddu.w.qb %[temp1], %[temp1] \n\t" + "addu %[temp2], %[temp2], %[temp3] \n\t" + "addu %[temp4], %[temp4], %[temp5] \n\t" + "addu %[temp6], %[temp6], %[temp7] \n\t" + "addu %[temp8], %[temp8], %[temp9] \n\t" + "addu %[temp0], %[temp0], %[temp1] \n\t" + "addu %[temp2], %[temp2], %[temp4] \n\t" + "addu %[temp6], %[temp6], %[temp8] \n\t" + "addu %[temp0], %[temp0], %[temp2] \n\t" + "addu %[temp0], %[temp0], %[temp6] \n\t" + "shra_r.w %[temp0], %[temp0], 4 \n\t" + "replv.qb %[temp0], %[temp0] \n\t" + STORE_8_BYTES(temp0, temp0, 0, 4, 0, dst) + STORE_8_BYTES(temp0, temp0, 1, 4, 1, dst) + STORE_8_BYTES(temp0, temp0, 2, 4, 2, dst) + STORE_8_BYTES(temp0, temp0, 3, 4, 3, dst) + STORE_8_BYTES(temp0, temp0, 4, 4, 4, dst) + STORE_8_BYTES(temp0, temp0, 5, 4, 5, dst) + STORE_8_BYTES(temp0, temp0, 6, 4, 6, dst) + STORE_8_BYTES(temp0, temp0, 7, 4, 7, dst) + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9) + : [dst]"r"(dst) + : "memory" + ); +} + +static void DC8uvNoLeft(uint8_t* dst) { // DC with no left samples + int temp0, temp1; + __asm__ volatile ( + LOAD_8_BYTES(temp0, temp1, -1, 4, -1, dst) + "raddu.w.qb %[temp0], %[temp0] \n\t" + "raddu.w.qb %[temp1], %[temp1] \n\t" + "addu %[temp0], %[temp0], %[temp1] \n\t" + "shra_r.w %[temp0], %[temp0], 3 \n\t" + "replv.qb %[temp0], %[temp0] \n\t" + STORE_8_BYTES(temp0, temp0, 0, 4, 0, dst) + STORE_8_BYTES(temp0, temp0, 1, 4, 1, dst) + STORE_8_BYTES(temp0, temp0, 2, 4, 2, dst) + STORE_8_BYTES(temp0, temp0, 3, 4, 3, dst) + STORE_8_BYTES(temp0, temp0, 4, 4, 4, dst) + STORE_8_BYTES(temp0, temp0, 5, 4, 5, dst) + STORE_8_BYTES(temp0, temp0, 6, 4, 6, dst) + STORE_8_BYTES(temp0, temp0, 7, 4, 7, dst) + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1) + : [dst]"r"(dst) + : "memory" + ); +} + +static void DC8uvNoTop(uint8_t* dst) { // DC with no top samples + int temp0, temp1, temp2, temp3, temp4; + int temp5, temp6, temp7, temp8; + __asm__ volatile ( + LOAD_4_BYTES(temp2, temp3, temp4, temp5, -1, 0, -1, 1, -1, 2, -1, 3, dst) + LOAD_4_BYTES(temp6, temp7, temp8, temp1, -1, 4, -1, 5, -1, 6, -1, 7, dst) + "addu %[temp2], %[temp2], %[temp3] \n\t" + "addu %[temp4], %[temp4], %[temp5] \n\t" + "addu %[temp6], %[temp6], %[temp7] \n\t" + "addu %[temp8], %[temp8], %[temp1] \n\t" + "addu %[temp2], %[temp2], %[temp4] \n\t" + "addu %[temp6], %[temp6], %[temp8] \n\t" + "addu %[temp0], %[temp6], %[temp2] \n\t" + "shra_r.w %[temp0], %[temp0], 3 \n\t" + "replv.qb %[temp0], %[temp0] \n\t" + STORE_8_BYTES(temp0, temp0, 0, 4, 0, dst) + STORE_8_BYTES(temp0, temp0, 1, 4, 1, dst) + STORE_8_BYTES(temp0, temp0, 2, 4, 2, dst) + STORE_8_BYTES(temp0, temp0, 3, 4, 3, dst) + STORE_8_BYTES(temp0, temp0, 4, 4, 4, dst) + STORE_8_BYTES(temp0, temp0, 5, 4, 5, dst) + STORE_8_BYTES(temp0, temp0, 6, 4, 6, dst) + STORE_8_BYTES(temp0, temp0, 7, 4, 7, dst) + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8) + : [dst]"r"(dst) + : "memory" + ); +} + +#undef LOAD_8_BYTES +#undef STORE_8_BYTES +#undef LOAD_4_BYTES + +#define CLIPPING(SIZE) \ + "preceu.ph.qbl %[temp2], %[temp0] \n\t" \ + "preceu.ph.qbr %[temp0], %[temp0] \n\t" \ +".if " #SIZE " == 8 \n\t" \ + "preceu.ph.qbl %[temp3], %[temp1] \n\t" \ + "preceu.ph.qbr %[temp1], %[temp1] \n\t" \ +".endif \n\t" \ + "addu.ph %[temp2], %[temp2], %[dst_1] \n\t" \ + "addu.ph %[temp0], %[temp0], %[dst_1] \n\t" \ +".if " #SIZE " == 8 \n\t" \ + "addu.ph %[temp3], %[temp3], %[dst_1] \n\t" \ + "addu.ph %[temp1], %[temp1], %[dst_1] \n\t" \ +".endif \n\t" \ + "shll_s.ph %[temp2], %[temp2], 7 \n\t" \ + "shll_s.ph %[temp0], %[temp0], 7 \n\t" \ +".if " #SIZE " == 8 \n\t" \ + "shll_s.ph %[temp3], %[temp3], 7 \n\t" \ + "shll_s.ph %[temp1], %[temp1], 7 \n\t" \ +".endif \n\t" \ + "precrqu_s.qb.ph %[temp0], %[temp2], %[temp0] \n\t" \ +".if " #SIZE " == 8 \n\t" \ + "precrqu_s.qb.ph %[temp1], %[temp3], %[temp1] \n\t" \ +".endif \n\t" + + +#define CLIP_8B_TO_DST(DST, TOP, SIZE) do { \ + int dst_1 = ((int)(DST)[-1] << 16) + (DST)[-1]; \ + int temp0, temp1, temp2, temp3; \ + __asm__ volatile ( \ + ".if " #SIZE " < 8 \n\t" \ + "ulw %[temp0], 0(%[top]) \n\t" \ + "subu.ph %[dst_1], %[dst_1], %[top_1] \n\t" \ + CLIPPING(4) \ + "usw %[temp0], 0(%[dst]) \n\t" \ + ".else \n\t" \ + "ulw %[temp0], 0(%[top]) \n\t" \ + "ulw %[temp1], 4(%[top]) \n\t" \ + "subu.ph %[dst_1], %[dst_1], %[top_1] \n\t" \ + CLIPPING(8) \ + "usw %[temp0], 0(%[dst]) \n\t" \ + "usw %[temp1], 4(%[dst]) \n\t" \ + ".if " #SIZE " == 16 \n\t" \ + "ulw %[temp0], 8(%[top]) \n\t" \ + "ulw %[temp1], 12(%[top]) \n\t" \ + CLIPPING(8) \ + "usw %[temp0], 8(%[dst]) \n\t" \ + "usw %[temp1], 12(%[dst]) \n\t" \ + ".endif \n\t" \ + ".endif \n\t" \ + : [dst_1]"+&r"(dst_1), [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), \ + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3) \ + : [top_1]"r"(top_1), [top]"r"((TOP)), [dst]"r"((DST)) \ + : "memory" \ + ); \ +} while (0) + +#define CLIP_TO_DST(DST, SIZE) do { \ + int y; \ + const uint8_t* top = (DST) - BPS; \ + const int top_1 = ((int)top[-1] << 16) + top[-1]; \ + for (y = 0; y < (SIZE); ++y) { \ + CLIP_8B_TO_DST((DST), top, (SIZE)); \ + (DST) += BPS; \ + } \ +} while (0) + +#define TRUE_MOTION(DST, SIZE) \ +static void TrueMotion##SIZE(uint8_t* (DST)) { \ + CLIP_TO_DST((DST), (SIZE)); \ +} + +TRUE_MOTION(dst, 4) +TRUE_MOTION(dst, 8) +TRUE_MOTION(dst, 16) + +#undef TRUE_MOTION +#undef CLIP_TO_DST +#undef CLIP_8B_TO_DST +#undef CLIPPING + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8DspInitMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitMIPSdspR2(void) { + VP8TransformDC = TransformDC; + VP8TransformAC3 = TransformAC3; + VP8Transform = TransformTwo; + + VP8VFilter16 = VFilter16; + VP8HFilter16 = HFilter16; + VP8VFilter8 = VFilter8; + VP8HFilter8 = HFilter8; + VP8VFilter16i = VFilter16i; + VP8HFilter16i = HFilter16i; + VP8VFilter8i = VFilter8i; + VP8HFilter8i = HFilter8i; + VP8SimpleVFilter16 = SimpleVFilter16; + VP8SimpleHFilter16 = SimpleHFilter16; + VP8SimpleVFilter16i = SimpleVFilter16i; + VP8SimpleHFilter16i = SimpleHFilter16i; + + VP8PredLuma4[0] = DC4; + VP8PredLuma4[1] = TrueMotion4; + VP8PredLuma4[2] = VE4; + VP8PredLuma4[4] = RD4; + VP8PredLuma4[6] = LD4; + + VP8PredChroma8[0] = DC8uv; + VP8PredChroma8[1] = TrueMotion8; + VP8PredChroma8[4] = DC8uvNoTop; + VP8PredChroma8[5] = DC8uvNoLeft; + + VP8PredLuma16[1] = TrueMotion16; +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(VP8DspInitMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 diff --git a/internal/libwebp-1.2.1/src/dsp/dec_msa.c b/internal/libwebp-1.2.1/src/dsp/dec_msa.c new file mode 100644 index 0000000..8090622 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/dec_msa.c @@ -0,0 +1,1020 @@ +// 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 dsp functions +// +// Author(s): Prashant Patil (prashant.patil@imgtec.com) + + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "src/dsp/msa_macro.h" + +//------------------------------------------------------------------------------ +// Transforms + +#define IDCT_1D_W(in0, in1, in2, in3, out0, out1, out2, out3) { \ + v4i32 a1_m, b1_m, c1_m, d1_m; \ + v4i32 c_tmp1_m, c_tmp2_m, d_tmp1_m, d_tmp2_m; \ + const v4i32 cospi8sqrt2minus1 = __msa_fill_w(20091); \ + const v4i32 sinpi8sqrt2 = __msa_fill_w(35468); \ + \ + a1_m = in0 + in2; \ + b1_m = in0 - in2; \ + c_tmp1_m = (in1 * sinpi8sqrt2) >> 16; \ + c_tmp2_m = in3 + ((in3 * cospi8sqrt2minus1) >> 16); \ + c1_m = c_tmp1_m - c_tmp2_m; \ + d_tmp1_m = in1 + ((in1 * cospi8sqrt2minus1) >> 16); \ + d_tmp2_m = (in3 * sinpi8sqrt2) >> 16; \ + d1_m = d_tmp1_m + d_tmp2_m; \ + BUTTERFLY_4(a1_m, b1_m, c1_m, d1_m, out0, out1, out2, out3); \ +} +#define MULT1(a) ((((a) * 20091) >> 16) + (a)) +#define MULT2(a) (((a) * 35468) >> 16) + +static void TransformOne(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; + const v16i8 zero = { 0 }; + v16i8 dest0, dest1, dest2, dest3; + + 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(dst, 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 TransformTwo(const int16_t* in, uint8_t* dst, int do_two) { + TransformOne(in, dst); + if (do_two) { + TransformOne(in + 16, dst + 4); + } +} + +static void TransformWHT(const int16_t* in, int16_t* out) { + v8i16 input0, input1; + 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 }; + v8i16 tmp0, tmp1, tmp2, tmp3; + v8i16 out0, out1; + + LD_SH2(in, 8, input0, input1); + input1 = SLDI_SH(input1, input1, 8); + tmp0 = input0 + input1; + tmp1 = input0 - input1; + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3); + out0 = tmp2 + tmp3; + out1 = tmp2 - tmp3; + VSHF_H2_SH(out0, out1, out0, out1, mask2, mask3, input0, input1); + tmp0 = input0 + input1; + tmp1 = input0 - input1; + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3); + tmp0 = tmp2 + tmp3; + tmp1 = tmp2 - tmp3; + ADDVI_H2_SH(tmp0, 3, tmp1, 3, out0, out1); + SRAI_H2_SH(out0, out1, 3); + out[0] = __msa_copy_s_h(out0, 0); + out[16] = __msa_copy_s_h(out0, 4); + out[32] = __msa_copy_s_h(out1, 0); + out[48] = __msa_copy_s_h(out1, 4); + out[64] = __msa_copy_s_h(out0, 1); + out[80] = __msa_copy_s_h(out0, 5); + out[96] = __msa_copy_s_h(out1, 1); + out[112] = __msa_copy_s_h(out1, 5); + out[128] = __msa_copy_s_h(out0, 2); + out[144] = __msa_copy_s_h(out0, 6); + out[160] = __msa_copy_s_h(out1, 2); + out[176] = __msa_copy_s_h(out1, 6); + out[192] = __msa_copy_s_h(out0, 3); + out[208] = __msa_copy_s_h(out0, 7); + out[224] = __msa_copy_s_h(out1, 3); + out[240] = __msa_copy_s_h(out1, 7); +} + +static void TransformDC(const int16_t* in, uint8_t* dst) { + const int DC = (in[0] + 4) >> 3; + const v8i16 tmp0 = __msa_fill_h(DC); + ADDBLK_ST4x4_UB(tmp0, tmp0, tmp0, tmp0, dst, BPS); +} + +static void TransformAC3(const int16_t* in, uint8_t* dst) { + const int a = in[0] + 4; + const int c4 = MULT2(in[4]); + const int d4 = MULT1(in[4]); + const int in2 = MULT2(in[1]); + const int in3 = MULT1(in[1]); + v4i32 tmp0 = { 0 }; + v4i32 out0 = __msa_fill_w(a + d4); + v4i32 out1 = __msa_fill_w(a + c4); + v4i32 out2 = __msa_fill_w(a - c4); + v4i32 out3 = __msa_fill_w(a - d4); + v4i32 res0, res1, res2, res3; + const v4i32 zero = { 0 }; + v16u8 dest0, dest1, dest2, dest3; + + INSERT_W4_SW(in3, in2, -in2, -in3, tmp0); + ADD4(out0, tmp0, out1, tmp0, out2, tmp0, out3, tmp0, + out0, out1, out2, out3); + SRAI_W4_SW(out0, out1, out2, out3, 3); + LD_UB4(dst, 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, out0, res1, out1, res2, out2, res3, out3, res0, res1, res2, res3); + CLIP_SW4_0_255(res0, res1, res2, res3); + PCKEV_B2_SW(res0, res1, res2, res3, out0, out1); + res0 = (v4i32)__msa_pckev_b((v16i8)out0, (v16i8)out1); + 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); \ + const v8i16 cnst63h = __msa_ldi_h(63); \ + \ + 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 = temp0 + cnst63h; \ + temp2 = filt_l * cnst9h; \ + temp3 = temp2 + cnst63h; \ + 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 = { 0 }, 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 = { 0 }; + + 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 = { 0 }, 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 + +extern void VP8DspInitMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitMSA(void) { + VP8TransformWHT = TransformWHT; + 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 + +WEBP_DSP_INIT_STUB(VP8DspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/internal/libwebp-1.2.1/src/dsp/dec_neon.c b/internal/libwebp-1.2.1/src/dsp/dec_neon.c new file mode 100644 index 0000000..fa85170 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/dec_neon.c @@ -0,0 +1,1663 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// ARM NEON version of dsp functions and loop filtering. +// +// Authors: Somnath Banerjee (somnath@google.com) +// Johann Koenig (johannkoenig@google.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_NEON) + +#include "src/dsp/neon.h" +#include "src/dec/vp8i_dec.h" + +//------------------------------------------------------------------------------ +// NxM Loading functions + +#if !defined(WORK_AROUND_GCC) + +// This intrinsics version makes gcc-4.6.3 crash during Load4x??() compilation +// (register alloc, probably). The variants somewhat mitigate the problem, but +// not quite. HFilter16i() remains problematic. +static WEBP_INLINE uint8x8x4_t Load4x8_NEON(const uint8_t* const src, + int stride) { + const uint8x8_t zero = vdup_n_u8(0); + uint8x8x4_t out; + INIT_VECTOR4(out, zero, zero, zero, zero); + out = vld4_lane_u8(src + 0 * stride, out, 0); + out = vld4_lane_u8(src + 1 * stride, out, 1); + out = vld4_lane_u8(src + 2 * stride, out, 2); + out = vld4_lane_u8(src + 3 * stride, out, 3); + out = vld4_lane_u8(src + 4 * stride, out, 4); + out = vld4_lane_u8(src + 5 * stride, out, 5); + out = vld4_lane_u8(src + 6 * stride, out, 6); + out = vld4_lane_u8(src + 7 * stride, out, 7); + return out; +} + +static WEBP_INLINE void Load4x16_NEON(const uint8_t* const src, int stride, + uint8x16_t* const p1, + uint8x16_t* const p0, + uint8x16_t* const q0, + uint8x16_t* const q1) { + // row0 = p1[0..7]|p0[0..7]|q0[0..7]|q1[0..7] + // row8 = p1[8..15]|p0[8..15]|q0[8..15]|q1[8..15] + const uint8x8x4_t row0 = Load4x8_NEON(src - 2 + 0 * stride, stride); + const uint8x8x4_t row8 = Load4x8_NEON(src - 2 + 8 * stride, stride); + *p1 = vcombine_u8(row0.val[0], row8.val[0]); + *p0 = vcombine_u8(row0.val[1], row8.val[1]); + *q0 = vcombine_u8(row0.val[2], row8.val[2]); + *q1 = vcombine_u8(row0.val[3], row8.val[3]); +} + +#else // WORK_AROUND_GCC + +#define LOADQ_LANE_32b(VALUE, LANE) do { \ + (VALUE) = vld1q_lane_u32((const uint32_t*)src, (VALUE), (LANE)); \ + src += stride; \ +} while (0) + +static WEBP_INLINE void Load4x16_NEON(const uint8_t* src, int stride, + uint8x16_t* const p1, + uint8x16_t* const p0, + uint8x16_t* const q0, + uint8x16_t* const q1) { + const uint32x4_t zero = vdupq_n_u32(0); + uint32x4x4_t in; + INIT_VECTOR4(in, zero, zero, zero, zero); + src -= 2; + LOADQ_LANE_32b(in.val[0], 0); + LOADQ_LANE_32b(in.val[1], 0); + LOADQ_LANE_32b(in.val[2], 0); + LOADQ_LANE_32b(in.val[3], 0); + LOADQ_LANE_32b(in.val[0], 1); + LOADQ_LANE_32b(in.val[1], 1); + LOADQ_LANE_32b(in.val[2], 1); + LOADQ_LANE_32b(in.val[3], 1); + LOADQ_LANE_32b(in.val[0], 2); + LOADQ_LANE_32b(in.val[1], 2); + LOADQ_LANE_32b(in.val[2], 2); + LOADQ_LANE_32b(in.val[3], 2); + LOADQ_LANE_32b(in.val[0], 3); + LOADQ_LANE_32b(in.val[1], 3); + LOADQ_LANE_32b(in.val[2], 3); + LOADQ_LANE_32b(in.val[3], 3); + // Transpose four 4x4 parts: + { + const uint8x16x2_t row01 = vtrnq_u8(vreinterpretq_u8_u32(in.val[0]), + vreinterpretq_u8_u32(in.val[1])); + const uint8x16x2_t row23 = vtrnq_u8(vreinterpretq_u8_u32(in.val[2]), + vreinterpretq_u8_u32(in.val[3])); + const uint16x8x2_t row02 = vtrnq_u16(vreinterpretq_u16_u8(row01.val[0]), + vreinterpretq_u16_u8(row23.val[0])); + const uint16x8x2_t row13 = vtrnq_u16(vreinterpretq_u16_u8(row01.val[1]), + vreinterpretq_u16_u8(row23.val[1])); + *p1 = vreinterpretq_u8_u16(row02.val[0]); + *p0 = vreinterpretq_u8_u16(row13.val[0]); + *q0 = vreinterpretq_u8_u16(row02.val[1]); + *q1 = vreinterpretq_u8_u16(row13.val[1]); + } +} +#undef LOADQ_LANE_32b + +#endif // !WORK_AROUND_GCC + +static WEBP_INLINE void Load8x16_NEON( + const uint8_t* const src, int stride, + uint8x16_t* const p3, uint8x16_t* const p2, uint8x16_t* const p1, + uint8x16_t* const p0, uint8x16_t* const q0, uint8x16_t* const q1, + uint8x16_t* const q2, uint8x16_t* const q3) { + Load4x16_NEON(src - 2, stride, p3, p2, p1, p0); + Load4x16_NEON(src + 2, stride, q0, q1, q2, q3); +} + +static WEBP_INLINE void Load16x4_NEON(const uint8_t* const src, int stride, + uint8x16_t* const p1, + uint8x16_t* const p0, + uint8x16_t* const q0, + uint8x16_t* const q1) { + *p1 = vld1q_u8(src - 2 * stride); + *p0 = vld1q_u8(src - 1 * stride); + *q0 = vld1q_u8(src + 0 * stride); + *q1 = vld1q_u8(src + 1 * stride); +} + +static WEBP_INLINE void Load16x8_NEON( + const uint8_t* const src, int stride, + uint8x16_t* const p3, uint8x16_t* const p2, uint8x16_t* const p1, + uint8x16_t* const p0, uint8x16_t* const q0, uint8x16_t* const q1, + uint8x16_t* const q2, uint8x16_t* const q3) { + Load16x4_NEON(src - 2 * stride, stride, p3, p2, p1, p0); + Load16x4_NEON(src + 2 * stride, stride, q0, q1, q2, q3); +} + +static WEBP_INLINE void Load8x8x2_NEON( + const uint8_t* const u, const uint8_t* const v, int stride, + uint8x16_t* const p3, uint8x16_t* const p2, uint8x16_t* const p1, + uint8x16_t* const p0, uint8x16_t* const q0, uint8x16_t* const q1, + uint8x16_t* const q2, uint8x16_t* const q3) { + // We pack the 8x8 u-samples in the lower half of the uint8x16_t destination + // and the v-samples on the higher half. + *p3 = vcombine_u8(vld1_u8(u - 4 * stride), vld1_u8(v - 4 * stride)); + *p2 = vcombine_u8(vld1_u8(u - 3 * stride), vld1_u8(v - 3 * stride)); + *p1 = vcombine_u8(vld1_u8(u - 2 * stride), vld1_u8(v - 2 * stride)); + *p0 = vcombine_u8(vld1_u8(u - 1 * stride), vld1_u8(v - 1 * stride)); + *q0 = vcombine_u8(vld1_u8(u + 0 * stride), vld1_u8(v + 0 * stride)); + *q1 = vcombine_u8(vld1_u8(u + 1 * stride), vld1_u8(v + 1 * stride)); + *q2 = vcombine_u8(vld1_u8(u + 2 * stride), vld1_u8(v + 2 * stride)); + *q3 = vcombine_u8(vld1_u8(u + 3 * stride), vld1_u8(v + 3 * stride)); +} + +#if !defined(WORK_AROUND_GCC) + +#define LOAD_UV_8(ROW) \ + vcombine_u8(vld1_u8(u - 4 + (ROW) * stride), vld1_u8(v - 4 + (ROW) * stride)) + +static WEBP_INLINE void Load8x8x2T_NEON( + const uint8_t* const u, const uint8_t* const v, int stride, + uint8x16_t* const p3, uint8x16_t* const p2, uint8x16_t* const p1, + uint8x16_t* const p0, uint8x16_t* const q0, uint8x16_t* const q1, + uint8x16_t* const q2, uint8x16_t* const q3) { + // We pack the 8x8 u-samples in the lower half of the uint8x16_t destination + // and the v-samples on the higher half. + const uint8x16_t row0 = LOAD_UV_8(0); + const uint8x16_t row1 = LOAD_UV_8(1); + const uint8x16_t row2 = LOAD_UV_8(2); + const uint8x16_t row3 = LOAD_UV_8(3); + const uint8x16_t row4 = LOAD_UV_8(4); + const uint8x16_t row5 = LOAD_UV_8(5); + const uint8x16_t row6 = LOAD_UV_8(6); + const uint8x16_t row7 = LOAD_UV_8(7); + // Perform two side-by-side 8x8 transposes + // u00 u01 u02 u03 u04 u05 u06 u07 | v00 v01 v02 v03 v04 v05 v06 v07 + // u10 u11 u12 u13 u14 u15 u16 u17 | v10 v11 v12 ... + // u20 u21 u22 u23 u24 u25 u26 u27 | v20 v21 ... + // u30 u31 u32 u33 u34 u35 u36 u37 | ... + // u40 u41 u42 u43 u44 u45 u46 u47 | ... + // u50 u51 u52 u53 u54 u55 u56 u57 | ... + // u60 u61 u62 u63 u64 u65 u66 u67 | v60 ... + // u70 u71 u72 u73 u74 u75 u76 u77 | v70 v71 v72 ... + const uint8x16x2_t row01 = vtrnq_u8(row0, row1); // u00 u10 u02 u12 ... + // u01 u11 u03 u13 ... + const uint8x16x2_t row23 = vtrnq_u8(row2, row3); // u20 u30 u22 u32 ... + // u21 u31 u23 u33 ... + const uint8x16x2_t row45 = vtrnq_u8(row4, row5); // ... + const uint8x16x2_t row67 = vtrnq_u8(row6, row7); // ... + const uint16x8x2_t row02 = vtrnq_u16(vreinterpretq_u16_u8(row01.val[0]), + vreinterpretq_u16_u8(row23.val[0])); + const uint16x8x2_t row13 = vtrnq_u16(vreinterpretq_u16_u8(row01.val[1]), + vreinterpretq_u16_u8(row23.val[1])); + const uint16x8x2_t row46 = vtrnq_u16(vreinterpretq_u16_u8(row45.val[0]), + vreinterpretq_u16_u8(row67.val[0])); + const uint16x8x2_t row57 = vtrnq_u16(vreinterpretq_u16_u8(row45.val[1]), + vreinterpretq_u16_u8(row67.val[1])); + const uint32x4x2_t row04 = vtrnq_u32(vreinterpretq_u32_u16(row02.val[0]), + vreinterpretq_u32_u16(row46.val[0])); + const uint32x4x2_t row26 = vtrnq_u32(vreinterpretq_u32_u16(row02.val[1]), + vreinterpretq_u32_u16(row46.val[1])); + const uint32x4x2_t row15 = vtrnq_u32(vreinterpretq_u32_u16(row13.val[0]), + vreinterpretq_u32_u16(row57.val[0])); + const uint32x4x2_t row37 = vtrnq_u32(vreinterpretq_u32_u16(row13.val[1]), + vreinterpretq_u32_u16(row57.val[1])); + *p3 = vreinterpretq_u8_u32(row04.val[0]); + *p2 = vreinterpretq_u8_u32(row15.val[0]); + *p1 = vreinterpretq_u8_u32(row26.val[0]); + *p0 = vreinterpretq_u8_u32(row37.val[0]); + *q0 = vreinterpretq_u8_u32(row04.val[1]); + *q1 = vreinterpretq_u8_u32(row15.val[1]); + *q2 = vreinterpretq_u8_u32(row26.val[1]); + *q3 = vreinterpretq_u8_u32(row37.val[1]); +} +#undef LOAD_UV_8 + +#endif // !WORK_AROUND_GCC + +static WEBP_INLINE void Store2x8_NEON(const uint8x8x2_t v, + uint8_t* const dst, int stride) { + vst2_lane_u8(dst + 0 * stride, v, 0); + vst2_lane_u8(dst + 1 * stride, v, 1); + vst2_lane_u8(dst + 2 * stride, v, 2); + vst2_lane_u8(dst + 3 * stride, v, 3); + vst2_lane_u8(dst + 4 * stride, v, 4); + vst2_lane_u8(dst + 5 * stride, v, 5); + vst2_lane_u8(dst + 6 * stride, v, 6); + vst2_lane_u8(dst + 7 * stride, v, 7); +} + +static WEBP_INLINE void Store2x16_NEON(const uint8x16_t p0, const uint8x16_t q0, + uint8_t* const dst, int stride) { + uint8x8x2_t lo, hi; + lo.val[0] = vget_low_u8(p0); + lo.val[1] = vget_low_u8(q0); + hi.val[0] = vget_high_u8(p0); + hi.val[1] = vget_high_u8(q0); + Store2x8_NEON(lo, dst - 1 + 0 * stride, stride); + Store2x8_NEON(hi, dst - 1 + 8 * stride, stride); +} + +#if !defined(WORK_AROUND_GCC) +static WEBP_INLINE void Store4x8_NEON(const uint8x8x4_t v, + uint8_t* const dst, int stride) { + vst4_lane_u8(dst + 0 * stride, v, 0); + vst4_lane_u8(dst + 1 * stride, v, 1); + vst4_lane_u8(dst + 2 * stride, v, 2); + vst4_lane_u8(dst + 3 * stride, v, 3); + vst4_lane_u8(dst + 4 * stride, v, 4); + vst4_lane_u8(dst + 5 * stride, v, 5); + vst4_lane_u8(dst + 6 * stride, v, 6); + vst4_lane_u8(dst + 7 * stride, v, 7); +} + +static WEBP_INLINE void Store4x16_NEON(const uint8x16_t p1, const uint8x16_t p0, + const uint8x16_t q0, const uint8x16_t q1, + uint8_t* const dst, int stride) { + uint8x8x4_t lo, hi; + INIT_VECTOR4(lo, + vget_low_u8(p1), vget_low_u8(p0), + vget_low_u8(q0), vget_low_u8(q1)); + INIT_VECTOR4(hi, + vget_high_u8(p1), vget_high_u8(p0), + vget_high_u8(q0), vget_high_u8(q1)); + Store4x8_NEON(lo, dst - 2 + 0 * stride, stride); + Store4x8_NEON(hi, dst - 2 + 8 * stride, stride); +} +#endif // !WORK_AROUND_GCC + +static WEBP_INLINE void Store16x2_NEON(const uint8x16_t p0, const uint8x16_t q0, + uint8_t* const dst, int stride) { + vst1q_u8(dst - stride, p0); + vst1q_u8(dst, q0); +} + +static WEBP_INLINE void Store16x4_NEON(const uint8x16_t p1, const uint8x16_t p0, + const uint8x16_t q0, const uint8x16_t q1, + uint8_t* const dst, int stride) { + Store16x2_NEON(p1, p0, dst - stride, stride); + Store16x2_NEON(q0, q1, dst + stride, stride); +} + +static WEBP_INLINE void Store8x2x2_NEON(const uint8x16_t p0, + const uint8x16_t q0, + uint8_t* const u, uint8_t* const v, + int stride) { + // p0 and q0 contain the u+v samples packed in low/high halves. + vst1_u8(u - stride, vget_low_u8(p0)); + vst1_u8(u, vget_low_u8(q0)); + vst1_u8(v - stride, vget_high_u8(p0)); + vst1_u8(v, vget_high_u8(q0)); +} + +static WEBP_INLINE void Store8x4x2_NEON(const uint8x16_t p1, + const uint8x16_t p0, + const uint8x16_t q0, + const uint8x16_t q1, + uint8_t* const u, uint8_t* const v, + int stride) { + // The p1...q1 registers contain the u+v samples packed in low/high halves. + Store8x2x2_NEON(p1, p0, u - stride, v - stride, stride); + Store8x2x2_NEON(q0, q1, u + stride, v + stride, stride); +} + +#if !defined(WORK_AROUND_GCC) + +#define STORE6_LANE(DST, VAL0, VAL1, LANE) do { \ + vst3_lane_u8((DST) - 3, (VAL0), (LANE)); \ + vst3_lane_u8((DST) + 0, (VAL1), (LANE)); \ + (DST) += stride; \ +} while (0) + +static WEBP_INLINE void Store6x8x2_NEON( + const uint8x16_t p2, const uint8x16_t p1, const uint8x16_t p0, + const uint8x16_t q0, const uint8x16_t q1, const uint8x16_t q2, + uint8_t* u, uint8_t* v, int stride) { + uint8x8x3_t u0, u1, v0, v1; + INIT_VECTOR3(u0, vget_low_u8(p2), vget_low_u8(p1), vget_low_u8(p0)); + INIT_VECTOR3(u1, vget_low_u8(q0), vget_low_u8(q1), vget_low_u8(q2)); + INIT_VECTOR3(v0, vget_high_u8(p2), vget_high_u8(p1), vget_high_u8(p0)); + INIT_VECTOR3(v1, vget_high_u8(q0), vget_high_u8(q1), vget_high_u8(q2)); + STORE6_LANE(u, u0, u1, 0); + STORE6_LANE(u, u0, u1, 1); + STORE6_LANE(u, u0, u1, 2); + STORE6_LANE(u, u0, u1, 3); + STORE6_LANE(u, u0, u1, 4); + STORE6_LANE(u, u0, u1, 5); + STORE6_LANE(u, u0, u1, 6); + STORE6_LANE(u, u0, u1, 7); + STORE6_LANE(v, v0, v1, 0); + STORE6_LANE(v, v0, v1, 1); + STORE6_LANE(v, v0, v1, 2); + STORE6_LANE(v, v0, v1, 3); + STORE6_LANE(v, v0, v1, 4); + STORE6_LANE(v, v0, v1, 5); + STORE6_LANE(v, v0, v1, 6); + STORE6_LANE(v, v0, v1, 7); +} +#undef STORE6_LANE + +static WEBP_INLINE void Store4x8x2_NEON(const uint8x16_t p1, + const uint8x16_t p0, + const uint8x16_t q0, + const uint8x16_t q1, + uint8_t* const u, uint8_t* const v, + int stride) { + uint8x8x4_t u0, v0; + INIT_VECTOR4(u0, + vget_low_u8(p1), vget_low_u8(p0), + vget_low_u8(q0), vget_low_u8(q1)); + INIT_VECTOR4(v0, + vget_high_u8(p1), vget_high_u8(p0), + vget_high_u8(q0), vget_high_u8(q1)); + vst4_lane_u8(u - 2 + 0 * stride, u0, 0); + vst4_lane_u8(u - 2 + 1 * stride, u0, 1); + vst4_lane_u8(u - 2 + 2 * stride, u0, 2); + vst4_lane_u8(u - 2 + 3 * stride, u0, 3); + vst4_lane_u8(u - 2 + 4 * stride, u0, 4); + vst4_lane_u8(u - 2 + 5 * stride, u0, 5); + vst4_lane_u8(u - 2 + 6 * stride, u0, 6); + vst4_lane_u8(u - 2 + 7 * stride, u0, 7); + vst4_lane_u8(v - 2 + 0 * stride, v0, 0); + vst4_lane_u8(v - 2 + 1 * stride, v0, 1); + vst4_lane_u8(v - 2 + 2 * stride, v0, 2); + vst4_lane_u8(v - 2 + 3 * stride, v0, 3); + vst4_lane_u8(v - 2 + 4 * stride, v0, 4); + vst4_lane_u8(v - 2 + 5 * stride, v0, 5); + vst4_lane_u8(v - 2 + 6 * stride, v0, 6); + vst4_lane_u8(v - 2 + 7 * stride, v0, 7); +} + +#endif // !WORK_AROUND_GCC + +// Zero extend 'v' to an int16x8_t. +static WEBP_INLINE int16x8_t ConvertU8ToS16_NEON(uint8x8_t v) { + return vreinterpretq_s16_u16(vmovl_u8(v)); +} + +// Performs unsigned 8b saturation on 'dst01' and 'dst23' storing the result +// to the corresponding rows of 'dst'. +static WEBP_INLINE void SaturateAndStore4x4_NEON(uint8_t* const dst, + const int16x8_t dst01, + const int16x8_t dst23) { + // Unsigned saturate to 8b. + const uint8x8_t dst01_u8 = vqmovun_s16(dst01); + const uint8x8_t dst23_u8 = vqmovun_s16(dst23); + + // Store the results. + vst1_lane_u32((uint32_t*)(dst + 0 * BPS), vreinterpret_u32_u8(dst01_u8), 0); + vst1_lane_u32((uint32_t*)(dst + 1 * BPS), vreinterpret_u32_u8(dst01_u8), 1); + vst1_lane_u32((uint32_t*)(dst + 2 * BPS), vreinterpret_u32_u8(dst23_u8), 0); + vst1_lane_u32((uint32_t*)(dst + 3 * BPS), vreinterpret_u32_u8(dst23_u8), 1); +} + +static WEBP_INLINE void Add4x4_NEON(const int16x8_t row01, + const int16x8_t row23, + uint8_t* const dst) { + uint32x2_t dst01 = vdup_n_u32(0); + uint32x2_t dst23 = vdup_n_u32(0); + + // Load the source pixels. + dst01 = vld1_lane_u32((uint32_t*)(dst + 0 * BPS), dst01, 0); + dst23 = vld1_lane_u32((uint32_t*)(dst + 2 * BPS), dst23, 0); + dst01 = vld1_lane_u32((uint32_t*)(dst + 1 * BPS), dst01, 1); + dst23 = vld1_lane_u32((uint32_t*)(dst + 3 * BPS), dst23, 1); + + { + // Convert to 16b. + const int16x8_t dst01_s16 = ConvertU8ToS16_NEON(vreinterpret_u8_u32(dst01)); + const int16x8_t dst23_s16 = ConvertU8ToS16_NEON(vreinterpret_u8_u32(dst23)); + + // Descale with rounding. + const int16x8_t out01 = vrsraq_n_s16(dst01_s16, row01, 3); + const int16x8_t out23 = vrsraq_n_s16(dst23_s16, row23, 3); + // Add the inverse transform. + SaturateAndStore4x4_NEON(dst, out01, out23); + } +} + +//----------------------------------------------------------------------------- +// Simple In-loop filtering (Paragraph 15.2) + +static uint8x16_t NeedsFilter_NEON(const uint8x16_t p1, const uint8x16_t p0, + const uint8x16_t q0, const uint8x16_t q1, + int thresh) { + const uint8x16_t thresh_v = vdupq_n_u8((uint8_t)thresh); + const uint8x16_t a_p0_q0 = vabdq_u8(p0, q0); // abs(p0-q0) + const uint8x16_t a_p1_q1 = vabdq_u8(p1, q1); // abs(p1-q1) + const uint8x16_t a_p0_q0_2 = vqaddq_u8(a_p0_q0, a_p0_q0); // 2 * abs(p0-q0) + const uint8x16_t a_p1_q1_2 = vshrq_n_u8(a_p1_q1, 1); // abs(p1-q1) / 2 + const uint8x16_t sum = vqaddq_u8(a_p0_q0_2, a_p1_q1_2); + const uint8x16_t mask = vcgeq_u8(thresh_v, sum); + return mask; +} + +static int8x16_t FlipSign_NEON(const uint8x16_t v) { + const uint8x16_t sign_bit = vdupq_n_u8(0x80); + return vreinterpretq_s8_u8(veorq_u8(v, sign_bit)); +} + +static uint8x16_t FlipSignBack_NEON(const int8x16_t v) { + const int8x16_t sign_bit = vdupq_n_s8(0x80); + return vreinterpretq_u8_s8(veorq_s8(v, sign_bit)); +} + +static int8x16_t GetBaseDelta_NEON(const int8x16_t p1, const int8x16_t p0, + const int8x16_t q0, const int8x16_t q1) { + const int8x16_t q0_p0 = vqsubq_s8(q0, p0); // (q0-p0) + const int8x16_t p1_q1 = vqsubq_s8(p1, q1); // (p1-q1) + const int8x16_t s1 = vqaddq_s8(p1_q1, q0_p0); // (p1-q1) + 1 * (q0 - p0) + const int8x16_t s2 = vqaddq_s8(q0_p0, s1); // (p1-q1) + 2 * (q0 - p0) + const int8x16_t s3 = vqaddq_s8(q0_p0, s2); // (p1-q1) + 3 * (q0 - p0) + return s3; +} + +static int8x16_t GetBaseDelta0_NEON(const int8x16_t p0, const int8x16_t q0) { + const int8x16_t q0_p0 = vqsubq_s8(q0, p0); // (q0-p0) + const int8x16_t s1 = vqaddq_s8(q0_p0, q0_p0); // 2 * (q0 - p0) + const int8x16_t s2 = vqaddq_s8(q0_p0, s1); // 3 * (q0 - p0) + return s2; +} + +//------------------------------------------------------------------------------ + +static void ApplyFilter2NoFlip_NEON(const int8x16_t p0s, const int8x16_t q0s, + const int8x16_t delta, + int8x16_t* const op0, + int8x16_t* const oq0) { + const int8x16_t kCst3 = vdupq_n_s8(0x03); + const int8x16_t kCst4 = vdupq_n_s8(0x04); + const int8x16_t delta_p3 = vqaddq_s8(delta, kCst3); + const int8x16_t delta_p4 = vqaddq_s8(delta, kCst4); + const int8x16_t delta3 = vshrq_n_s8(delta_p3, 3); + const int8x16_t delta4 = vshrq_n_s8(delta_p4, 3); + *op0 = vqaddq_s8(p0s, delta3); + *oq0 = vqsubq_s8(q0s, delta4); +} + +#if defined(WEBP_USE_INTRINSICS) + +static void ApplyFilter2_NEON(const int8x16_t p0s, const int8x16_t q0s, + const int8x16_t delta, + uint8x16_t* const op0, uint8x16_t* const oq0) { + const int8x16_t kCst3 = vdupq_n_s8(0x03); + const int8x16_t kCst4 = vdupq_n_s8(0x04); + const int8x16_t delta_p3 = vqaddq_s8(delta, kCst3); + const int8x16_t delta_p4 = vqaddq_s8(delta, kCst4); + const int8x16_t delta3 = vshrq_n_s8(delta_p3, 3); + const int8x16_t delta4 = vshrq_n_s8(delta_p4, 3); + const int8x16_t sp0 = vqaddq_s8(p0s, delta3); + const int8x16_t sq0 = vqsubq_s8(q0s, delta4); + *op0 = FlipSignBack_NEON(sp0); + *oq0 = FlipSignBack_NEON(sq0); +} + +static void DoFilter2_NEON(const uint8x16_t p1, const uint8x16_t p0, + const uint8x16_t q0, const uint8x16_t q1, + const uint8x16_t mask, + uint8x16_t* const op0, uint8x16_t* const oq0) { + const int8x16_t p1s = FlipSign_NEON(p1); + const int8x16_t p0s = FlipSign_NEON(p0); + const int8x16_t q0s = FlipSign_NEON(q0); + const int8x16_t q1s = FlipSign_NEON(q1); + const int8x16_t delta0 = GetBaseDelta_NEON(p1s, p0s, q0s, q1s); + const int8x16_t delta1 = vandq_s8(delta0, vreinterpretq_s8_u8(mask)); + ApplyFilter2_NEON(p0s, q0s, delta1, op0, oq0); +} + +static void SimpleVFilter16_NEON(uint8_t* p, int stride, int thresh) { + uint8x16_t p1, p0, q0, q1, op0, oq0; + Load16x4_NEON(p, stride, &p1, &p0, &q0, &q1); + { + const uint8x16_t mask = NeedsFilter_NEON(p1, p0, q0, q1, thresh); + DoFilter2_NEON(p1, p0, q0, q1, mask, &op0, &oq0); + } + Store16x2_NEON(op0, oq0, p, stride); +} + +static void SimpleHFilter16_NEON(uint8_t* p, int stride, int thresh) { + uint8x16_t p1, p0, q0, q1, oq0, op0; + Load4x16_NEON(p, stride, &p1, &p0, &q0, &q1); + { + const uint8x16_t mask = NeedsFilter_NEON(p1, p0, q0, q1, thresh); + DoFilter2_NEON(p1, p0, q0, q1, mask, &op0, &oq0); + } + Store2x16_NEON(op0, oq0, p, stride); +} + +#else + +// Load/Store vertical edge +#define LOAD8x4(c1, c2, c3, c4, b1, b2, stride) \ + "vld4.8 {" #c1 "[0]," #c2 "[0]," #c3 "[0]," #c4 "[0]}," #b1 "," #stride "\n" \ + "vld4.8 {" #c1 "[1]," #c2 "[1]," #c3 "[1]," #c4 "[1]}," #b2 "," #stride "\n" \ + "vld4.8 {" #c1 "[2]," #c2 "[2]," #c3 "[2]," #c4 "[2]}," #b1 "," #stride "\n" \ + "vld4.8 {" #c1 "[3]," #c2 "[3]," #c3 "[3]," #c4 "[3]}," #b2 "," #stride "\n" \ + "vld4.8 {" #c1 "[4]," #c2 "[4]," #c3 "[4]," #c4 "[4]}," #b1 "," #stride "\n" \ + "vld4.8 {" #c1 "[5]," #c2 "[5]," #c3 "[5]," #c4 "[5]}," #b2 "," #stride "\n" \ + "vld4.8 {" #c1 "[6]," #c2 "[6]," #c3 "[6]," #c4 "[6]}," #b1 "," #stride "\n" \ + "vld4.8 {" #c1 "[7]," #c2 "[7]," #c3 "[7]," #c4 "[7]}," #b2 "," #stride "\n" + +#define STORE8x2(c1, c2, p, stride) \ + "vst2.8 {" #c1 "[0], " #c2 "[0]}," #p "," #stride " \n" \ + "vst2.8 {" #c1 "[1], " #c2 "[1]}," #p "," #stride " \n" \ + "vst2.8 {" #c1 "[2], " #c2 "[2]}," #p "," #stride " \n" \ + "vst2.8 {" #c1 "[3], " #c2 "[3]}," #p "," #stride " \n" \ + "vst2.8 {" #c1 "[4], " #c2 "[4]}," #p "," #stride " \n" \ + "vst2.8 {" #c1 "[5], " #c2 "[5]}," #p "," #stride " \n" \ + "vst2.8 {" #c1 "[6], " #c2 "[6]}," #p "," #stride " \n" \ + "vst2.8 {" #c1 "[7], " #c2 "[7]}," #p "," #stride " \n" + +#define QRegs "q0", "q1", "q2", "q3", \ + "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" + +#define FLIP_SIGN_BIT2(a, b, s) \ + "veor " #a "," #a "," #s " \n" \ + "veor " #b "," #b "," #s " \n" \ + +#define FLIP_SIGN_BIT4(a, b, c, d, s) \ + FLIP_SIGN_BIT2(a, b, s) \ + FLIP_SIGN_BIT2(c, d, s) \ + +#define NEEDS_FILTER(p1, p0, q0, q1, thresh, mask) \ + "vabd.u8 q15," #p0 "," #q0 " \n" /* abs(p0 - q0) */ \ + "vabd.u8 q14," #p1 "," #q1 " \n" /* abs(p1 - q1) */ \ + "vqadd.u8 q15, q15, q15 \n" /* abs(p0 - q0) * 2 */ \ + "vshr.u8 q14, q14, #1 \n" /* abs(p1 - q1) / 2 */ \ + "vqadd.u8 q15, q15, q14 \n" /* abs(p0 - q0) * 2 + abs(p1 - q1) / 2 */ \ + "vdup.8 q14, " #thresh " \n" \ + "vcge.u8 " #mask ", q14, q15 \n" /* mask <= thresh */ + +#define GET_BASE_DELTA(p1, p0, q0, q1, o) \ + "vqsub.s8 q15," #q0 "," #p0 " \n" /* (q0 - p0) */ \ + "vqsub.s8 " #o "," #p1 "," #q1 " \n" /* (p1 - q1) */ \ + "vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 1 * (p0 - q0) */ \ + "vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 2 * (p0 - q0) */ \ + "vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 3 * (p0 - q0) */ + +#define DO_SIMPLE_FILTER(p0, q0, fl) \ + "vmov.i8 q15, #0x03 \n" \ + "vqadd.s8 q15, q15, " #fl " \n" /* filter1 = filter + 3 */ \ + "vshr.s8 q15, q15, #3 \n" /* filter1 >> 3 */ \ + "vqadd.s8 " #p0 "," #p0 ", q15 \n" /* p0 += filter1 */ \ + \ + "vmov.i8 q15, #0x04 \n" \ + "vqadd.s8 q15, q15, " #fl " \n" /* filter1 = filter + 4 */ \ + "vshr.s8 q15, q15, #3 \n" /* filter2 >> 3 */ \ + "vqsub.s8 " #q0 "," #q0 ", q15 \n" /* q0 -= filter2 */ + +// Applies filter on 2 pixels (p0 and q0) +#define DO_FILTER2(p1, p0, q0, q1, thresh) \ + NEEDS_FILTER(p1, p0, q0, q1, thresh, q9) /* filter mask in q9 */ \ + "vmov.i8 q10, #0x80 \n" /* sign bit */ \ + FLIP_SIGN_BIT4(p1, p0, q0, q1, q10) /* convert to signed value */ \ + GET_BASE_DELTA(p1, p0, q0, q1, q11) /* get filter level */ \ + "vand q9, q9, q11 \n" /* apply filter mask */ \ + DO_SIMPLE_FILTER(p0, q0, q9) /* apply filter */ \ + FLIP_SIGN_BIT2(p0, q0, q10) + +static void SimpleVFilter16_NEON(uint8_t* p, int stride, int thresh) { + __asm__ volatile ( + "sub %[p], %[p], %[stride], lsl #1 \n" // p -= 2 * stride + + "vld1.u8 {q1}, [%[p]], %[stride] \n" // p1 + "vld1.u8 {q2}, [%[p]], %[stride] \n" // p0 + "vld1.u8 {q3}, [%[p]], %[stride] \n" // q0 + "vld1.u8 {q12}, [%[p]] \n" // q1 + + DO_FILTER2(q1, q2, q3, q12, %[thresh]) + + "sub %[p], %[p], %[stride], lsl #1 \n" // p -= 2 * stride + + "vst1.u8 {q2}, [%[p]], %[stride] \n" // store op0 + "vst1.u8 {q3}, [%[p]] \n" // store oq0 + : [p] "+r"(p) + : [stride] "r"(stride), [thresh] "r"(thresh) + : "memory", QRegs + ); +} + +static void SimpleHFilter16_NEON(uint8_t* p, int stride, int thresh) { + __asm__ volatile ( + "sub r4, %[p], #2 \n" // base1 = p - 2 + "lsl r6, %[stride], #1 \n" // r6 = 2 * stride + "add r5, r4, %[stride] \n" // base2 = base1 + stride + + LOAD8x4(d2, d3, d4, d5, [r4], [r5], r6) + LOAD8x4(d24, d25, d26, d27, [r4], [r5], r6) + "vswp d3, d24 \n" // p1:q1 p0:q3 + "vswp d5, d26 \n" // q0:q2 q1:q4 + "vswp q2, q12 \n" // p1:q1 p0:q2 q0:q3 q1:q4 + + DO_FILTER2(q1, q2, q12, q13, %[thresh]) + + "sub %[p], %[p], #1 \n" // p - 1 + + "vswp d5, d24 \n" + STORE8x2(d4, d5, [%[p]], %[stride]) + STORE8x2(d24, d25, [%[p]], %[stride]) + + : [p] "+r"(p) + : [stride] "r"(stride), [thresh] "r"(thresh) + : "memory", "r4", "r5", "r6", QRegs + ); +} + +#undef LOAD8x4 +#undef STORE8x2 + +#endif // WEBP_USE_INTRINSICS + +static void SimpleVFilter16i_NEON(uint8_t* p, int stride, int thresh) { + uint32_t k; + for (k = 3; k != 0; --k) { + p += 4 * stride; + SimpleVFilter16_NEON(p, stride, thresh); + } +} + +static void SimpleHFilter16i_NEON(uint8_t* p, int stride, int thresh) { + uint32_t k; + for (k = 3; k != 0; --k) { + p += 4; + SimpleHFilter16_NEON(p, stride, thresh); + } +} + +//------------------------------------------------------------------------------ +// Complex In-loop filtering (Paragraph 15.3) + +static uint8x16_t NeedsHev_NEON(const uint8x16_t p1, const uint8x16_t p0, + const uint8x16_t q0, const uint8x16_t q1, + int hev_thresh) { + 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 a_max = vmaxq_u8(a_p1_p0, a_q1_q0); + const uint8x16_t mask = vcgtq_u8(a_max, hev_thresh_v); + return mask; +} + +static uint8x16_t NeedsFilter2_NEON(const uint8x16_t p3, const uint8x16_t p2, + const uint8x16_t p1, const uint8x16_t p0, + const uint8x16_t q0, const uint8x16_t q1, + const uint8x16_t q2, const uint8x16_t q3, + int ithresh, int thresh) { + const uint8x16_t ithresh_v = vdupq_n_u8((uint8_t)ithresh); + const uint8x16_t a_p3_p2 = vabdq_u8(p3, p2); // abs(p3 - p2) + const uint8x16_t a_p2_p1 = vabdq_u8(p2, p1); // abs(p2 - p1) + const uint8x16_t a_p1_p0 = vabdq_u8(p1, p0); // abs(p1 - p0) + const uint8x16_t a_q3_q2 = vabdq_u8(q3, q2); // abs(q3 - q2) + const uint8x16_t a_q2_q1 = vabdq_u8(q2, q1); // abs(q2 - q1) + const uint8x16_t a_q1_q0 = vabdq_u8(q1, q0); // abs(q1 - q0) + const uint8x16_t max1 = vmaxq_u8(a_p3_p2, a_p2_p1); + const uint8x16_t max2 = vmaxq_u8(a_p1_p0, a_q3_q2); + const uint8x16_t max3 = vmaxq_u8(a_q2_q1, a_q1_q0); + const uint8x16_t max12 = vmaxq_u8(max1, max2); + const uint8x16_t max123 = vmaxq_u8(max12, max3); + const uint8x16_t mask2 = vcgeq_u8(ithresh_v, max123); + const uint8x16_t mask1 = NeedsFilter_NEON(p1, p0, q0, q1, thresh); + const uint8x16_t mask = vandq_u8(mask1, mask2); + return mask; +} + +// 4-points filter + +static void ApplyFilter4_NEON( + const int8x16_t p1, const int8x16_t p0, + const int8x16_t q0, const int8x16_t q1, + const int8x16_t delta0, + uint8x16_t* const op1, uint8x16_t* const op0, + uint8x16_t* const oq0, uint8x16_t* const oq1) { + const int8x16_t kCst3 = vdupq_n_s8(0x03); + const int8x16_t kCst4 = vdupq_n_s8(0x04); + const int8x16_t delta1 = vqaddq_s8(delta0, kCst4); + const int8x16_t delta2 = vqaddq_s8(delta0, kCst3); + const int8x16_t a1 = vshrq_n_s8(delta1, 3); + const int8x16_t a2 = vshrq_n_s8(delta2, 3); + const int8x16_t a3 = vrshrq_n_s8(a1, 1); // a3 = (a1 + 1) >> 1 + *op0 = FlipSignBack_NEON(vqaddq_s8(p0, a2)); // clip(p0 + a2) + *oq0 = FlipSignBack_NEON(vqsubq_s8(q0, a1)); // clip(q0 - a1) + *op1 = FlipSignBack_NEON(vqaddq_s8(p1, a3)); // clip(p1 + a3) + *oq1 = FlipSignBack_NEON(vqsubq_s8(q1, a3)); // clip(q1 - a3) +} + +static void DoFilter4_NEON( + const uint8x16_t p1, const uint8x16_t p0, + const uint8x16_t q0, const uint8x16_t q1, + const uint8x16_t mask, const uint8x16_t hev_mask, + uint8x16_t* const op1, uint8x16_t* const op0, + uint8x16_t* const oq0, uint8x16_t* const oq1) { + // This is a fused version of DoFilter2() calling ApplyFilter2 directly + const int8x16_t p1s = FlipSign_NEON(p1); + int8x16_t p0s = FlipSign_NEON(p0); + int8x16_t q0s = FlipSign_NEON(q0); + const int8x16_t q1s = FlipSign_NEON(q1); + const uint8x16_t simple_lf_mask = vandq_u8(mask, hev_mask); + + // do_filter2 part (simple loopfilter on pixels with hev) + { + const int8x16_t delta = GetBaseDelta_NEON(p1s, p0s, q0s, q1s); + const int8x16_t simple_lf_delta = + vandq_s8(delta, vreinterpretq_s8_u8(simple_lf_mask)); + ApplyFilter2NoFlip_NEON(p0s, q0s, simple_lf_delta, &p0s, &q0s); + } + + // do_filter4 part (complex loopfilter on pixels without hev) + { + const int8x16_t delta0 = GetBaseDelta0_NEON(p0s, q0s); + // we use: (mask & hev_mask) ^ mask = mask & !hev_mask + const uint8x16_t complex_lf_mask = veorq_u8(simple_lf_mask, mask); + const int8x16_t complex_lf_delta = + vandq_s8(delta0, vreinterpretq_s8_u8(complex_lf_mask)); + ApplyFilter4_NEON(p1s, p0s, q0s, q1s, complex_lf_delta, op1, op0, oq0, oq1); + } +} + +// 6-points filter + +static void ApplyFilter6_NEON( + const int8x16_t p2, const int8x16_t p1, const int8x16_t p0, + const int8x16_t q0, const int8x16_t q1, const int8x16_t q2, + 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) { + // 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 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); + + *op0 = FlipSignBack_NEON(vqaddq_s8(p0, a1)); // clip(p0 + a1) + *oq0 = FlipSignBack_NEON(vqsubq_s8(q0, a1)); // clip(q0 - q1) + *oq1 = FlipSignBack_NEON(vqsubq_s8(q1, a2)); // clip(q1 - a2) + *op1 = FlipSignBack_NEON(vqaddq_s8(p1, a2)); // clip(p1 + a2) + *oq2 = FlipSignBack_NEON(vqsubq_s8(q2, a3)); // clip(q2 - a3) + *op2 = FlipSignBack_NEON(vqaddq_s8(p2, a3)); // clip(p2 + a3) +} + +static void DoFilter6_NEON( + const uint8x16_t p2, const uint8x16_t p1, const uint8x16_t p0, + const uint8x16_t q0, const uint8x16_t q1, const uint8x16_t q2, + const uint8x16_t mask, const uint8x16_t hev_mask, + uint8x16_t* const op2, uint8x16_t* const op1, uint8x16_t* const op0, + uint8x16_t* const oq0, uint8x16_t* const oq1, uint8x16_t* const oq2) { + // This is a fused version of DoFilter2() calling ApplyFilter2 directly + const int8x16_t p2s = FlipSign_NEON(p2); + const int8x16_t p1s = FlipSign_NEON(p1); + int8x16_t p0s = FlipSign_NEON(p0); + int8x16_t q0s = FlipSign_NEON(q0); + const int8x16_t q1s = FlipSign_NEON(q1); + const int8x16_t q2s = FlipSign_NEON(q2); + const uint8x16_t simple_lf_mask = vandq_u8(mask, hev_mask); + const int8x16_t delta0 = GetBaseDelta_NEON(p1s, p0s, q0s, q1s); + + // do_filter2 part (simple loopfilter on pixels with hev) + { + const int8x16_t simple_lf_delta = + vandq_s8(delta0, vreinterpretq_s8_u8(simple_lf_mask)); + ApplyFilter2NoFlip_NEON(p0s, q0s, simple_lf_delta, &p0s, &q0s); + } + + // do_filter6 part (complex loopfilter on pixels without hev) + { + // we use: (mask & hev_mask) ^ mask = mask & !hev_mask + const uint8x16_t complex_lf_mask = veorq_u8(simple_lf_mask, mask); + const int8x16_t complex_lf_delta = + vandq_s8(delta0, vreinterpretq_s8_u8(complex_lf_mask)); + ApplyFilter6_NEON(p2s, p1s, p0s, q0s, q1s, q2s, complex_lf_delta, + op2, op1, op0, oq0, oq1, oq2); + } +} + +// on macroblock edges + +static void VFilter16_NEON(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3; + Load16x8_NEON(p, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3); + { + const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, + ithresh, thresh); + const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh); + uint8x16_t op2, op1, op0, oq0, oq1, oq2; + DoFilter6_NEON(p2, p1, p0, q0, q1, q2, mask, hev_mask, + &op2, &op1, &op0, &oq0, &oq1, &oq2); + Store16x2_NEON(op2, op1, p - 2 * stride, stride); + Store16x2_NEON(op0, oq0, p + 0 * stride, stride); + Store16x2_NEON(oq1, oq2, p + 2 * stride, stride); + } +} + +static void HFilter16_NEON(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3; + Load8x16_NEON(p, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3); + { + const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, + ithresh, thresh); + const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh); + uint8x16_t op2, op1, op0, oq0, oq1, oq2; + DoFilter6_NEON(p2, p1, p0, q0, q1, q2, mask, hev_mask, + &op2, &op1, &op0, &oq0, &oq1, &oq2); + Store2x16_NEON(op2, op1, p - 2, stride); + Store2x16_NEON(op0, oq0, p + 0, stride); + Store2x16_NEON(oq1, oq2, p + 2, stride); + } +} + +// on three inner edges +static void VFilter16i_NEON(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + uint32_t k; + uint8x16_t p3, p2, p1, p0; + Load16x4_NEON(p + 2 * stride, stride, &p3, &p2, &p1, &p0); + for (k = 3; k != 0; --k) { + uint8x16_t q0, q1, q2, q3; + p += 4 * stride; + Load16x4_NEON(p + 2 * stride, stride, &q0, &q1, &q2, &q3); + { + const uint8x16_t mask = + NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, ithresh, thresh); + const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh); + // p3 and p2 are not just temporary variables here: they will be + // re-used for next span. And q2/q3 will become p1/p0 accordingly. + DoFilter4_NEON(p1, p0, q0, q1, mask, hev_mask, &p1, &p0, &p3, &p2); + Store16x4_NEON(p1, p0, p3, p2, p, stride); + p1 = q2; + p0 = q3; + } + } +} + +#if !defined(WORK_AROUND_GCC) +static void HFilter16i_NEON(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + uint32_t k; + uint8x16_t p3, p2, p1, p0; + Load4x16_NEON(p + 2, stride, &p3, &p2, &p1, &p0); + for (k = 3; k != 0; --k) { + uint8x16_t q0, q1, q2, q3; + p += 4; + Load4x16_NEON(p + 2, stride, &q0, &q1, &q2, &q3); + { + const uint8x16_t mask = + NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, ithresh, thresh); + const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh); + DoFilter4_NEON(p1, p0, q0, q1, mask, hev_mask, &p1, &p0, &p3, &p2); + Store4x16_NEON(p1, p0, p3, p2, p, stride); + p1 = q2; + p0 = q3; + } + } +} +#endif // !WORK_AROUND_GCC + +// 8-pixels wide variant, for chroma filtering +static void VFilter8_NEON(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3; + Load8x8x2_NEON(u, v, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3); + { + const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, + ithresh, thresh); + const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh); + uint8x16_t op2, op1, op0, oq0, oq1, oq2; + DoFilter6_NEON(p2, p1, p0, q0, q1, q2, mask, hev_mask, + &op2, &op1, &op0, &oq0, &oq1, &oq2); + Store8x2x2_NEON(op2, op1, u - 2 * stride, v - 2 * stride, stride); + Store8x2x2_NEON(op0, oq0, u + 0 * stride, v + 0 * stride, stride); + Store8x2x2_NEON(oq1, oq2, u + 2 * stride, v + 2 * stride, stride); + } +} +static void VFilter8i_NEON(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3; + u += 4 * stride; + v += 4 * stride; + Load8x8x2_NEON(u, v, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3); + { + const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, + ithresh, thresh); + const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh); + uint8x16_t op1, op0, oq0, oq1; + DoFilter4_NEON(p1, p0, q0, q1, mask, hev_mask, &op1, &op0, &oq0, &oq1); + Store8x4x2_NEON(op1, op0, oq0, oq1, u, v, stride); + } +} + +#if !defined(WORK_AROUND_GCC) +static void HFilter8_NEON(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3; + Load8x8x2T_NEON(u, v, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3); + { + const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, + ithresh, thresh); + const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh); + uint8x16_t op2, op1, op0, oq0, oq1, oq2; + DoFilter6_NEON(p2, p1, p0, q0, q1, q2, mask, hev_mask, + &op2, &op1, &op0, &oq0, &oq1, &oq2); + Store6x8x2_NEON(op2, op1, op0, oq0, oq1, oq2, u, v, stride); + } +} + +static void HFilter8i_NEON(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3; + u += 4; + v += 4; + Load8x8x2T_NEON(u, v, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3); + { + const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, + ithresh, thresh); + const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh); + uint8x16_t op1, op0, oq0, oq1; + DoFilter4_NEON(p1, p0, q0, q1, mask, hev_mask, &op1, &op0, &oq0, &oq1); + Store4x8x2_NEON(op1, op0, oq0, oq1, u, v, stride); + } +} +#endif // !WORK_AROUND_GCC + +//----------------------------------------------------------------------------- +// Inverse transforms (Paragraph 14.4) + +// Technically these are unsigned but vqdmulh is only available in signed. +// vqdmulh returns high half (effectively >> 16) but also doubles the value, +// changing the >> 16 to >> 15 and requiring an additional >> 1. +// We use this to our advantage with kC2. The canonical value is 35468. +// However, the high bit is set so treating it as signed will give incorrect +// results. We avoid this by down shifting by 1 here to clear the highest bit. +// Combined with the doubling effect of vqdmulh we get >> 16. +// This can not be applied to kC1 because the lowest bit is set. Down shifting +// the constant would reduce precision. + +// libwebp uses a trick to avoid some extra addition that libvpx does. +// Instead of: +// temp2 = ip[12] + ((ip[12] * cospi8sqrt2minus1) >> 16); +// libwebp adds 1 << 16 to cospi8sqrt2minus1 (kC1). However, this causes the +// same issue with kC1 and vqdmulh that we work around by down shifting kC2 + +static const int16_t kC1 = 20091; +static const int16_t kC2 = 17734; // half of kC2, actually. See comment above. + +#if defined(WEBP_USE_INTRINSICS) +static WEBP_INLINE void Transpose8x2_NEON(const int16x8_t in0, + const int16x8_t in1, + int16x8x2_t* const out) { + // a0 a1 a2 a3 | b0 b1 b2 b3 => a0 b0 c0 d0 | a1 b1 c1 d1 + // c0 c1 c2 c3 | d0 d1 d2 d3 a2 b2 c2 d2 | a3 b3 c3 d3 + const int16x8x2_t tmp0 = vzipq_s16(in0, in1); // a0 c0 a1 c1 a2 c2 ... + // b0 d0 b1 d1 b2 d2 ... + *out = vzipq_s16(tmp0.val[0], tmp0.val[1]); +} + +static WEBP_INLINE void TransformPass_NEON(int16x8x2_t* const rows) { + // {rows} = in0 | in4 + // in8 | in12 + // B1 = in4 | in12 + const int16x8_t B1 = + vcombine_s16(vget_high_s16(rows->val[0]), vget_high_s16(rows->val[1])); + // C0 = kC1 * in4 | kC1 * in12 + // C1 = kC2 * in4 | kC2 * in12 + const int16x8_t C0 = vsraq_n_s16(B1, vqdmulhq_n_s16(B1, kC1), 1); + const int16x8_t C1 = vqdmulhq_n_s16(B1, kC2); + const int16x4_t a = vqadd_s16(vget_low_s16(rows->val[0]), + vget_low_s16(rows->val[1])); // in0 + in8 + const int16x4_t b = vqsub_s16(vget_low_s16(rows->val[0]), + vget_low_s16(rows->val[1])); // in0 - in8 + // c = kC2 * in4 - kC1 * in12 + // d = kC1 * in4 + kC2 * in12 + const int16x4_t c = vqsub_s16(vget_low_s16(C1), vget_high_s16(C0)); + const int16x4_t d = vqadd_s16(vget_low_s16(C0), vget_high_s16(C1)); + const int16x8_t D0 = vcombine_s16(a, b); // D0 = a | b + const int16x8_t D1 = vcombine_s16(d, c); // D1 = d | c + const int16x8_t E0 = vqaddq_s16(D0, D1); // a+d | b+c + const int16x8_t E_tmp = vqsubq_s16(D0, D1); // a-d | b-c + const int16x8_t E1 = vcombine_s16(vget_high_s16(E_tmp), vget_low_s16(E_tmp)); + Transpose8x2_NEON(E0, E1, rows); +} + +static void TransformOne_NEON(const int16_t* in, uint8_t* dst) { + int16x8x2_t rows; + INIT_VECTOR2(rows, vld1q_s16(in + 0), vld1q_s16(in + 8)); + TransformPass_NEON(&rows); + TransformPass_NEON(&rows); + Add4x4_NEON(rows.val[0], rows.val[1], dst); +} + +#else + +static void TransformOne_NEON(const int16_t* in, uint8_t* dst) { + const int kBPS = BPS; + // kC1, kC2. Padded because vld1.16 loads 8 bytes + const int16_t constants[4] = { kC1, kC2, 0, 0 }; + /* Adapted from libvpx: vp8/common/arm/neon/shortidct4x4llm_neon.asm */ + __asm__ volatile ( + "vld1.16 {q1, q2}, [%[in]] \n" + "vld1.16 {d0}, [%[constants]] \n" + + /* d2: in[0] + * d3: in[8] + * d4: in[4] + * d5: in[12] + */ + "vswp d3, d4 \n" + + /* q8 = {in[4], in[12]} * kC1 * 2 >> 16 + * q9 = {in[4], in[12]} * kC2 >> 16 + */ + "vqdmulh.s16 q8, q2, d0[0] \n" + "vqdmulh.s16 q9, q2, d0[1] \n" + + /* d22 = a = in[0] + in[8] + * d23 = b = in[0] - in[8] + */ + "vqadd.s16 d22, d2, d3 \n" + "vqsub.s16 d23, d2, d3 \n" + + /* The multiplication should be x * kC1 >> 16 + * However, with vqdmulh we get x * kC1 * 2 >> 16 + * (multiply, double, return high half) + * We avoided this in kC2 by pre-shifting the constant. + * q8 = in[4]/[12] * kC1 >> 16 + */ + "vshr.s16 q8, q8, #1 \n" + + /* Add {in[4], in[12]} back after the multiplication. This is handled by + * adding 1 << 16 to kC1 in the libwebp C code. + */ + "vqadd.s16 q8, q2, q8 \n" + + /* d20 = c = in[4]*kC2 - in[12]*kC1 + * d21 = d = in[4]*kC1 + in[12]*kC2 + */ + "vqsub.s16 d20, d18, d17 \n" + "vqadd.s16 d21, d19, d16 \n" + + /* d2 = tmp[0] = a + d + * d3 = tmp[1] = b + c + * d4 = tmp[2] = b - c + * d5 = tmp[3] = a - d + */ + "vqadd.s16 d2, d22, d21 \n" + "vqadd.s16 d3, d23, d20 \n" + "vqsub.s16 d4, d23, d20 \n" + "vqsub.s16 d5, d22, d21 \n" + + "vzip.16 q1, q2 \n" + "vzip.16 q1, q2 \n" + + "vswp d3, d4 \n" + + /* q8 = {tmp[4], tmp[12]} * kC1 * 2 >> 16 + * q9 = {tmp[4], tmp[12]} * kC2 >> 16 + */ + "vqdmulh.s16 q8, q2, d0[0] \n" + "vqdmulh.s16 q9, q2, d0[1] \n" + + /* d22 = a = tmp[0] + tmp[8] + * d23 = b = tmp[0] - tmp[8] + */ + "vqadd.s16 d22, d2, d3 \n" + "vqsub.s16 d23, d2, d3 \n" + + /* See long winded explanations prior */ + "vshr.s16 q8, q8, #1 \n" + "vqadd.s16 q8, q2, q8 \n" + + /* d20 = c = in[4]*kC2 - in[12]*kC1 + * d21 = d = in[4]*kC1 + in[12]*kC2 + */ + "vqsub.s16 d20, d18, d17 \n" + "vqadd.s16 d21, d19, d16 \n" + + /* d2 = tmp[0] = a + d + * d3 = tmp[1] = b + c + * d4 = tmp[2] = b - c + * d5 = tmp[3] = a - d + */ + "vqadd.s16 d2, d22, d21 \n" + "vqadd.s16 d3, d23, d20 \n" + "vqsub.s16 d4, d23, d20 \n" + "vqsub.s16 d5, d22, d21 \n" + + "vld1.32 d6[0], [%[dst]], %[kBPS] \n" + "vld1.32 d6[1], [%[dst]], %[kBPS] \n" + "vld1.32 d7[0], [%[dst]], %[kBPS] \n" + "vld1.32 d7[1], [%[dst]], %[kBPS] \n" + + "sub %[dst], %[dst], %[kBPS], lsl #2 \n" + + /* (val) + 4 >> 3 */ + "vrshr.s16 d2, d2, #3 \n" + "vrshr.s16 d3, d3, #3 \n" + "vrshr.s16 d4, d4, #3 \n" + "vrshr.s16 d5, d5, #3 \n" + + "vzip.16 q1, q2 \n" + "vzip.16 q1, q2 \n" + + /* Must accumulate before saturating */ + "vmovl.u8 q8, d6 \n" + "vmovl.u8 q9, d7 \n" + + "vqadd.s16 q1, q1, q8 \n" + "vqadd.s16 q2, q2, q9 \n" + + "vqmovun.s16 d0, q1 \n" + "vqmovun.s16 d1, q2 \n" + + "vst1.32 d0[0], [%[dst]], %[kBPS] \n" + "vst1.32 d0[1], [%[dst]], %[kBPS] \n" + "vst1.32 d1[0], [%[dst]], %[kBPS] \n" + "vst1.32 d1[1], [%[dst]] \n" + + : [in] "+r"(in), [dst] "+r"(dst) /* modified registers */ + : [kBPS] "r"(kBPS), [constants] "r"(constants) /* constants */ + : "memory", "q0", "q1", "q2", "q8", "q9", "q10", "q11" /* clobbered */ + ); +} + +#endif // WEBP_USE_INTRINSICS + +static void TransformTwo_NEON(const int16_t* in, uint8_t* dst, int do_two) { + TransformOne_NEON(in, dst); + if (do_two) { + TransformOne_NEON(in + 16, dst + 4); + } +} + +static void TransformDC_NEON(const int16_t* in, uint8_t* dst) { + const int16x8_t DC = vdupq_n_s16(in[0]); + Add4x4_NEON(DC, DC, dst); +} + +//------------------------------------------------------------------------------ + +#define STORE_WHT(dst, col, rows) do { \ + *dst = vgetq_lane_s32(rows.val[0], col); (dst) += 16; \ + *dst = vgetq_lane_s32(rows.val[1], col); (dst) += 16; \ + *dst = vgetq_lane_s32(rows.val[2], col); (dst) += 16; \ + *dst = vgetq_lane_s32(rows.val[3], col); (dst) += 16; \ +} while (0) + +static void TransformWHT_NEON(const int16_t* in, int16_t* out) { + int32x4x4_t tmp; + + { + // Load the source. + const int16x4_t in00_03 = vld1_s16(in + 0); + const int16x4_t in04_07 = vld1_s16(in + 4); + const int16x4_t in08_11 = vld1_s16(in + 8); + const int16x4_t in12_15 = vld1_s16(in + 12); + const int32x4_t a0 = vaddl_s16(in00_03, in12_15); // in[0..3] + in[12..15] + const int32x4_t a1 = vaddl_s16(in04_07, in08_11); // in[4..7] + in[8..11] + const int32x4_t a2 = vsubl_s16(in04_07, in08_11); // in[4..7] - in[8..11] + const int32x4_t a3 = vsubl_s16(in00_03, in12_15); // in[0..3] - in[12..15] + tmp.val[0] = vaddq_s32(a0, a1); + tmp.val[1] = vaddq_s32(a3, a2); + tmp.val[2] = vsubq_s32(a0, a1); + tmp.val[3] = vsubq_s32(a3, a2); + // Arrange the temporary results column-wise. + tmp = Transpose4x4_NEON(tmp); + } + + { + const int32x4_t kCst3 = vdupq_n_s32(3); + const int32x4_t dc = vaddq_s32(tmp.val[0], kCst3); // add rounder + const int32x4_t a0 = vaddq_s32(dc, tmp.val[3]); + const int32x4_t a1 = vaddq_s32(tmp.val[1], tmp.val[2]); + const int32x4_t a2 = vsubq_s32(tmp.val[1], tmp.val[2]); + const int32x4_t a3 = vsubq_s32(dc, tmp.val[3]); + + tmp.val[0] = vaddq_s32(a0, a1); + tmp.val[1] = vaddq_s32(a3, a2); + tmp.val[2] = vsubq_s32(a0, a1); + tmp.val[3] = vsubq_s32(a3, a2); + + // right shift the results by 3. + tmp.val[0] = vshrq_n_s32(tmp.val[0], 3); + tmp.val[1] = vshrq_n_s32(tmp.val[1], 3); + tmp.val[2] = vshrq_n_s32(tmp.val[2], 3); + tmp.val[3] = vshrq_n_s32(tmp.val[3], 3); + + STORE_WHT(out, 0, tmp); + STORE_WHT(out, 1, tmp); + STORE_WHT(out, 2, tmp); + STORE_WHT(out, 3, tmp); + } +} + +#undef STORE_WHT + +//------------------------------------------------------------------------------ + +#define MUL(a, b) (((a) * (b)) >> 16) +static void TransformAC3_NEON(const int16_t* in, uint8_t* dst) { + static const int kC1_full = 20091 + (1 << 16); + static const int kC2_full = 35468; + const int16x4_t A = vld1_dup_s16(in); + const int16x4_t c4 = vdup_n_s16(MUL(in[4], kC2_full)); + const int16x4_t d4 = vdup_n_s16(MUL(in[4], kC1_full)); + const int c1 = MUL(in[1], kC2_full); + const int d1 = MUL(in[1], kC1_full); + const uint64_t cd = (uint64_t)( d1 & 0xffff) << 0 | + (uint64_t)( c1 & 0xffff) << 16 | + (uint64_t)(-c1 & 0xffff) << 32 | + (uint64_t)(-d1 & 0xffff) << 48; + const int16x4_t CD = vcreate_s16(cd); + const int16x4_t B = vqadd_s16(A, CD); + const int16x8_t m0_m1 = vcombine_s16(vqadd_s16(B, d4), vqadd_s16(B, c4)); + const int16x8_t m2_m3 = vcombine_s16(vqsub_s16(B, c4), vqsub_s16(B, d4)); + Add4x4_NEON(m0_m1, m2_m3, dst); +} +#undef MUL + +//------------------------------------------------------------------------------ +// 4x4 + +static void DC4_NEON(uint8_t* dst) { // DC + const uint8x8_t A = vld1_u8(dst - BPS); // top row + const uint16x4_t p0 = vpaddl_u8(A); // cascading summation of the top + const uint16x4_t p1 = vpadd_u16(p0, p0); + const uint8x8_t L0 = vld1_u8(dst + 0 * BPS - 1); + const uint8x8_t L1 = vld1_u8(dst + 1 * BPS - 1); + const uint8x8_t L2 = vld1_u8(dst + 2 * BPS - 1); + const uint8x8_t L3 = vld1_u8(dst + 3 * BPS - 1); + const uint16x8_t s0 = vaddl_u8(L0, L1); + const uint16x8_t s1 = vaddl_u8(L2, L3); + const uint16x8_t s01 = vaddq_u16(s0, s1); + const uint16x8_t sum = vaddq_u16(s01, vcombine_u16(p1, p1)); + const uint8x8_t dc0 = vrshrn_n_u16(sum, 3); // (sum + 4) >> 3 + const uint8x8_t dc = vdup_lane_u8(dc0, 0); + int i; + for (i = 0; i < 4; ++i) { + vst1_lane_u32((uint32_t*)(dst + i * BPS), vreinterpret_u32_u8(dc), 0); + } +} + +// TrueMotion (4x4 + 8x8) +static WEBP_INLINE void TrueMotion_NEON(uint8_t* dst, int size) { + const uint8x8_t TL = vld1_dup_u8(dst - BPS - 1); // top-left pixel 'A[-1]' + const uint8x8_t T = vld1_u8(dst - BPS); // top row 'A[0..3]' + const int16x8_t d = vreinterpretq_s16_u16(vsubl_u8(T, TL)); // A[c] - A[-1] + int y; + for (y = 0; y < size; y += 4) { + // left edge + const int16x8_t L0 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 0 * BPS - 1)); + const int16x8_t L1 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 1 * BPS - 1)); + const int16x8_t L2 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 2 * BPS - 1)); + const int16x8_t L3 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 3 * BPS - 1)); + const int16x8_t r0 = vaddq_s16(L0, d); // L[r] + A[c] - A[-1] + const int16x8_t r1 = vaddq_s16(L1, d); + const int16x8_t r2 = vaddq_s16(L2, d); + const int16x8_t r3 = vaddq_s16(L3, d); + // Saturate and store the result. + const uint32x2_t r0_u32 = vreinterpret_u32_u8(vqmovun_s16(r0)); + const uint32x2_t r1_u32 = vreinterpret_u32_u8(vqmovun_s16(r1)); + const uint32x2_t r2_u32 = vreinterpret_u32_u8(vqmovun_s16(r2)); + const uint32x2_t r3_u32 = vreinterpret_u32_u8(vqmovun_s16(r3)); + if (size == 4) { + vst1_lane_u32((uint32_t*)(dst + 0 * BPS), r0_u32, 0); + vst1_lane_u32((uint32_t*)(dst + 1 * BPS), r1_u32, 0); + vst1_lane_u32((uint32_t*)(dst + 2 * BPS), r2_u32, 0); + vst1_lane_u32((uint32_t*)(dst + 3 * BPS), r3_u32, 0); + } else { + vst1_u32((uint32_t*)(dst + 0 * BPS), r0_u32); + vst1_u32((uint32_t*)(dst + 1 * BPS), r1_u32); + vst1_u32((uint32_t*)(dst + 2 * BPS), r2_u32); + vst1_u32((uint32_t*)(dst + 3 * BPS), r3_u32); + } + dst += 4 * BPS; + } +} + +static void TM4_NEON(uint8_t* dst) { TrueMotion_NEON(dst, 4); } + +static void VE4_NEON(uint8_t* dst) { // vertical + // NB: avoid vld1_u64 here as an alignment hint may be added -> SIGBUS. + const uint64x1_t A0 = vreinterpret_u64_u8(vld1_u8(dst - BPS - 1)); // top row + const uint64x1_t A1 = vshr_n_u64(A0, 8); + const uint64x1_t A2 = vshr_n_u64(A0, 16); + const uint8x8_t ABCDEFGH = vreinterpret_u8_u64(A0); + const uint8x8_t BCDEFGH0 = vreinterpret_u8_u64(A1); + const uint8x8_t CDEFGH00 = vreinterpret_u8_u64(A2); + const uint8x8_t b = vhadd_u8(ABCDEFGH, CDEFGH00); + const uint8x8_t avg = vrhadd_u8(b, BCDEFGH0); + int i; + for (i = 0; i < 4; ++i) { + vst1_lane_u32((uint32_t*)(dst + i * BPS), vreinterpret_u32_u8(avg), 0); + } +} + +static void RD4_NEON(uint8_t* dst) { // Down-right + const uint8x8_t XABCD_u8 = vld1_u8(dst - BPS - 1); + const uint64x1_t XABCD = vreinterpret_u64_u8(XABCD_u8); + const uint64x1_t ____XABC = vshl_n_u64(XABCD, 32); + const uint32_t I = dst[-1 + 0 * BPS]; + const uint32_t J = dst[-1 + 1 * BPS]; + const uint32_t K = dst[-1 + 2 * BPS]; + const uint32_t L = dst[-1 + 3 * BPS]; + const uint64x1_t LKJI____ = + vcreate_u64((uint64_t)L | (K << 8) | (J << 16) | (I << 24)); + const uint64x1_t LKJIXABC = vorr_u64(LKJI____, ____XABC); + const uint8x8_t KJIXABC_ = vreinterpret_u8_u64(vshr_n_u64(LKJIXABC, 8)); + const uint8x8_t JIXABC__ = vreinterpret_u8_u64(vshr_n_u64(LKJIXABC, 16)); + const uint8_t D = vget_lane_u8(XABCD_u8, 4); + const uint8x8_t JIXABCD_ = vset_lane_u8(D, JIXABC__, 6); + const uint8x8_t LKJIXABC_u8 = vreinterpret_u8_u64(LKJIXABC); + const uint8x8_t avg1 = vhadd_u8(JIXABCD_, LKJIXABC_u8); + const uint8x8_t avg2 = vrhadd_u8(avg1, KJIXABC_); + const uint64x1_t avg2_u64 = vreinterpret_u64_u8(avg2); + const uint32x2_t r3 = vreinterpret_u32_u8(avg2); + const uint32x2_t r2 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 8)); + const uint32x2_t r1 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 16)); + const uint32x2_t r0 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 24)); + vst1_lane_u32((uint32_t*)(dst + 0 * BPS), r0, 0); + vst1_lane_u32((uint32_t*)(dst + 1 * BPS), r1, 0); + vst1_lane_u32((uint32_t*)(dst + 2 * BPS), r2, 0); + vst1_lane_u32((uint32_t*)(dst + 3 * BPS), r3, 0); +} + +static void LD4_NEON(uint8_t* dst) { // Down-left + // Note using the same shift trick as VE4() is slower here. + const uint8x8_t ABCDEFGH = vld1_u8(dst - BPS + 0); + const uint8x8_t BCDEFGH0 = vld1_u8(dst - BPS + 1); + const uint8x8_t CDEFGH00 = vld1_u8(dst - BPS + 2); + const uint8x8_t CDEFGHH0 = vset_lane_u8(dst[-BPS + 7], CDEFGH00, 6); + const uint8x8_t avg1 = vhadd_u8(ABCDEFGH, CDEFGHH0); + const uint8x8_t avg2 = vrhadd_u8(avg1, BCDEFGH0); + const uint64x1_t avg2_u64 = vreinterpret_u64_u8(avg2); + const uint32x2_t r0 = vreinterpret_u32_u8(avg2); + const uint32x2_t r1 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 8)); + const uint32x2_t r2 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 16)); + const uint32x2_t r3 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 24)); + vst1_lane_u32((uint32_t*)(dst + 0 * BPS), r0, 0); + vst1_lane_u32((uint32_t*)(dst + 1 * BPS), r1, 0); + vst1_lane_u32((uint32_t*)(dst + 2 * BPS), r2, 0); + vst1_lane_u32((uint32_t*)(dst + 3 * BPS), r3, 0); +} + +//------------------------------------------------------------------------------ +// Chroma + +static void VE8uv_NEON(uint8_t* dst) { // vertical + const uint8x8_t top = vld1_u8(dst - BPS); + int j; + for (j = 0; j < 8; ++j) { + vst1_u8(dst + j * BPS, top); + } +} + +static void HE8uv_NEON(uint8_t* dst) { // horizontal + int j; + for (j = 0; j < 8; ++j) { + const uint8x8_t left = vld1_dup_u8(dst - 1); + vst1_u8(dst, left); + dst += BPS; + } +} + +static WEBP_INLINE void DC8_NEON(uint8_t* dst, int do_top, int do_left) { + uint16x8_t sum_top; + uint16x8_t sum_left; + uint8x8_t dc0; + + if (do_top) { + const uint8x8_t A = vld1_u8(dst - BPS); // top row +#if defined(__aarch64__) + const uint16_t p2 = vaddlv_u8(A); + sum_top = vdupq_n_u16(p2); +#else + const uint16x4_t p0 = vpaddl_u8(A); // cascading summation of the top + const uint16x4_t p1 = vpadd_u16(p0, p0); + const uint16x4_t p2 = vpadd_u16(p1, p1); + sum_top = vcombine_u16(p2, p2); +#endif + } + + if (do_left) { + const uint8x8_t L0 = vld1_u8(dst + 0 * BPS - 1); + const uint8x8_t L1 = vld1_u8(dst + 1 * BPS - 1); + const uint8x8_t L2 = vld1_u8(dst + 2 * BPS - 1); + const uint8x8_t L3 = vld1_u8(dst + 3 * BPS - 1); + const uint8x8_t L4 = vld1_u8(dst + 4 * BPS - 1); + const uint8x8_t L5 = vld1_u8(dst + 5 * BPS - 1); + const uint8x8_t L6 = vld1_u8(dst + 6 * BPS - 1); + const uint8x8_t L7 = vld1_u8(dst + 7 * BPS - 1); + const uint16x8_t s0 = vaddl_u8(L0, L1); + const uint16x8_t s1 = vaddl_u8(L2, L3); + const uint16x8_t s2 = vaddl_u8(L4, L5); + const uint16x8_t s3 = vaddl_u8(L6, L7); + const uint16x8_t s01 = vaddq_u16(s0, s1); + const uint16x8_t s23 = vaddq_u16(s2, s3); + sum_left = vaddq_u16(s01, s23); + } + + if (do_top && do_left) { + const uint16x8_t sum = vaddq_u16(sum_left, sum_top); + dc0 = vrshrn_n_u16(sum, 4); + } else if (do_top) { + dc0 = vrshrn_n_u16(sum_top, 3); + } else if (do_left) { + dc0 = vrshrn_n_u16(sum_left, 3); + } else { + dc0 = vdup_n_u8(0x80); + } + + { + const uint8x8_t dc = vdup_lane_u8(dc0, 0); + int i; + for (i = 0; i < 8; ++i) { + vst1_u32((uint32_t*)(dst + i * BPS), vreinterpret_u32_u8(dc)); + } + } +} + +static void DC8uv_NEON(uint8_t* dst) { DC8_NEON(dst, 1, 1); } +static void DC8uvNoTop_NEON(uint8_t* dst) { DC8_NEON(dst, 0, 1); } +static void DC8uvNoLeft_NEON(uint8_t* dst) { DC8_NEON(dst, 1, 0); } +static void DC8uvNoTopLeft_NEON(uint8_t* dst) { DC8_NEON(dst, 0, 0); } + +static void TM8uv_NEON(uint8_t* dst) { TrueMotion_NEON(dst, 8); } + +//------------------------------------------------------------------------------ +// 16x16 + +static void VE16_NEON(uint8_t* dst) { // vertical + const uint8x16_t top = vld1q_u8(dst - BPS); + int j; + for (j = 0; j < 16; ++j) { + vst1q_u8(dst + j * BPS, top); + } +} + +static void HE16_NEON(uint8_t* dst) { // horizontal + int j; + for (j = 0; j < 16; ++j) { + const uint8x16_t left = vld1q_dup_u8(dst - 1); + vst1q_u8(dst, left); + dst += BPS; + } +} + +static WEBP_INLINE void DC16_NEON(uint8_t* dst, int do_top, int do_left) { + uint16x8_t sum_top; + uint16x8_t sum_left; + uint8x8_t dc0; + + if (do_top) { + const uint8x16_t A = vld1q_u8(dst - BPS); // top row +#if defined(__aarch64__) + const uint16_t p3 = vaddlvq_u8(A); + sum_top = vdupq_n_u16(p3); +#else + const uint16x8_t p0 = vpaddlq_u8(A); // cascading summation of the top + const uint16x4_t p1 = vadd_u16(vget_low_u16(p0), vget_high_u16(p0)); + const uint16x4_t p2 = vpadd_u16(p1, p1); + const uint16x4_t p3 = vpadd_u16(p2, p2); + sum_top = vcombine_u16(p3, p3); +#endif + } + + if (do_left) { + int i; + sum_left = vdupq_n_u16(0); + for (i = 0; i < 16; i += 8) { + const uint8x8_t L0 = vld1_u8(dst + (i + 0) * BPS - 1); + const uint8x8_t L1 = vld1_u8(dst + (i + 1) * BPS - 1); + const uint8x8_t L2 = vld1_u8(dst + (i + 2) * BPS - 1); + const uint8x8_t L3 = vld1_u8(dst + (i + 3) * BPS - 1); + const uint8x8_t L4 = vld1_u8(dst + (i + 4) * BPS - 1); + const uint8x8_t L5 = vld1_u8(dst + (i + 5) * BPS - 1); + const uint8x8_t L6 = vld1_u8(dst + (i + 6) * BPS - 1); + const uint8x8_t L7 = vld1_u8(dst + (i + 7) * BPS - 1); + const uint16x8_t s0 = vaddl_u8(L0, L1); + const uint16x8_t s1 = vaddl_u8(L2, L3); + const uint16x8_t s2 = vaddl_u8(L4, L5); + const uint16x8_t s3 = vaddl_u8(L6, L7); + const uint16x8_t s01 = vaddq_u16(s0, s1); + const uint16x8_t s23 = vaddq_u16(s2, s3); + const uint16x8_t sum = vaddq_u16(s01, s23); + sum_left = vaddq_u16(sum_left, sum); + } + } + + if (do_top && do_left) { + const uint16x8_t sum = vaddq_u16(sum_left, sum_top); + dc0 = vrshrn_n_u16(sum, 5); + } else if (do_top) { + dc0 = vrshrn_n_u16(sum_top, 4); + } else if (do_left) { + dc0 = vrshrn_n_u16(sum_left, 4); + } else { + dc0 = vdup_n_u8(0x80); + } + + { + const uint8x16_t dc = vdupq_lane_u8(dc0, 0); + int i; + for (i = 0; i < 16; ++i) { + vst1q_u8(dst + i * BPS, dc); + } + } +} + +static void DC16TopLeft_NEON(uint8_t* dst) { DC16_NEON(dst, 1, 1); } +static void DC16NoTop_NEON(uint8_t* dst) { DC16_NEON(dst, 0, 1); } +static void DC16NoLeft_NEON(uint8_t* dst) { DC16_NEON(dst, 1, 0); } +static void DC16NoTopLeft_NEON(uint8_t* dst) { DC16_NEON(dst, 0, 0); } + +static void TM16_NEON(uint8_t* dst) { + const uint8x8_t TL = vld1_dup_u8(dst - BPS - 1); // top-left pixel 'A[-1]' + const uint8x16_t T = vld1q_u8(dst - BPS); // top row 'A[0..15]' + // A[c] - A[-1] + const int16x8_t d_lo = vreinterpretq_s16_u16(vsubl_u8(vget_low_u8(T), TL)); + const int16x8_t d_hi = vreinterpretq_s16_u16(vsubl_u8(vget_high_u8(T), TL)); + int y; + for (y = 0; y < 16; y += 4) { + // left edge + const int16x8_t L0 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 0 * BPS - 1)); + const int16x8_t L1 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 1 * BPS - 1)); + const int16x8_t L2 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 2 * BPS - 1)); + const int16x8_t L3 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 3 * BPS - 1)); + const int16x8_t r0_lo = vaddq_s16(L0, d_lo); // L[r] + A[c] - A[-1] + const int16x8_t r1_lo = vaddq_s16(L1, d_lo); + const int16x8_t r2_lo = vaddq_s16(L2, d_lo); + const int16x8_t r3_lo = vaddq_s16(L3, d_lo); + const int16x8_t r0_hi = vaddq_s16(L0, d_hi); + const int16x8_t r1_hi = vaddq_s16(L1, d_hi); + const int16x8_t r2_hi = vaddq_s16(L2, d_hi); + const int16x8_t r3_hi = vaddq_s16(L3, d_hi); + // Saturate and store the result. + const uint8x16_t row0 = vcombine_u8(vqmovun_s16(r0_lo), vqmovun_s16(r0_hi)); + const uint8x16_t row1 = vcombine_u8(vqmovun_s16(r1_lo), vqmovun_s16(r1_hi)); + const uint8x16_t row2 = vcombine_u8(vqmovun_s16(r2_lo), vqmovun_s16(r2_hi)); + const uint8x16_t row3 = vcombine_u8(vqmovun_s16(r3_lo), vqmovun_s16(r3_hi)); + vst1q_u8(dst + 0 * BPS, row0); + vst1q_u8(dst + 1 * BPS, row1); + vst1q_u8(dst + 2 * BPS, row2); + vst1q_u8(dst + 3 * BPS, row3); + dst += 4 * BPS; + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8DspInitNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitNEON(void) { + VP8Transform = TransformTwo_NEON; + VP8TransformAC3 = TransformAC3_NEON; + VP8TransformDC = TransformDC_NEON; + VP8TransformWHT = TransformWHT_NEON; + + VP8VFilter16 = VFilter16_NEON; + VP8VFilter16i = VFilter16i_NEON; + VP8HFilter16 = HFilter16_NEON; +#if !defined(WORK_AROUND_GCC) + VP8HFilter16i = HFilter16i_NEON; +#endif + VP8VFilter8 = VFilter8_NEON; + VP8VFilter8i = VFilter8i_NEON; +#if !defined(WORK_AROUND_GCC) + VP8HFilter8 = HFilter8_NEON; + VP8HFilter8i = HFilter8i_NEON; +#endif + VP8SimpleVFilter16 = SimpleVFilter16_NEON; + VP8SimpleHFilter16 = SimpleHFilter16_NEON; + VP8SimpleVFilter16i = SimpleVFilter16i_NEON; + VP8SimpleHFilter16i = SimpleHFilter16i_NEON; + + VP8PredLuma4[0] = DC4_NEON; + VP8PredLuma4[1] = TM4_NEON; + VP8PredLuma4[2] = VE4_NEON; + VP8PredLuma4[4] = RD4_NEON; + VP8PredLuma4[6] = LD4_NEON; + + VP8PredLuma16[0] = DC16TopLeft_NEON; + VP8PredLuma16[1] = TM16_NEON; + VP8PredLuma16[2] = VE16_NEON; + VP8PredLuma16[3] = HE16_NEON; + VP8PredLuma16[4] = DC16NoTop_NEON; + VP8PredLuma16[5] = DC16NoLeft_NEON; + VP8PredLuma16[6] = DC16NoTopLeft_NEON; + + VP8PredChroma8[0] = DC8uv_NEON; + VP8PredChroma8[1] = TM8uv_NEON; + VP8PredChroma8[2] = VE8uv_NEON; + VP8PredChroma8[3] = HE8uv_NEON; + VP8PredChroma8[4] = DC8uvNoTop_NEON; + VP8PredChroma8[5] = DC8uvNoLeft_NEON; + VP8PredChroma8[6] = DC8uvNoTopLeft_NEON; +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(VP8DspInitNEON) + +#endif // WEBP_USE_NEON diff --git a/internal/libwebp-1.2.1/src/dsp/dec_sse2.c b/internal/libwebp-1.2.1/src/dsp/dec_sse2.c new file mode 100644 index 0000000..873aa59 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/dec_sse2.c @@ -0,0 +1,1227 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE2 version of some decoding functions (idct, loop filtering). +// +// Author: somnath@google.com (Somnath Banerjee) +// cduvivier@google.com (Christian Duvivier) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) + +// The 3-coeff sparse transform in SSE2 is not really faster than the plain-C +// one it seems => disable it by default. Uncomment the following to enable: +#if !defined(USE_TRANSFORM_AC3) +#define USE_TRANSFORM_AC3 0 // ALTERNATE_CODE +#endif + +#include +#include "src/dsp/common_sse2.h" +#include "src/dec/vp8i_dec.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ +// Transforms (Paragraph 14.4) + +static void Transform_SSE2(const int16_t* in, uint8_t* dst, int do_two) { + // This implementation makes use of 16-bit fixed point versions of two + // multiply constants: + // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16 + // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16 + // + // To be able to use signed 16-bit integers, we use the following trick to + // have constants within range: + // - Associated constants are obtained by subtracting the 16-bit fixed point + // version of one: + // k = K - (1 << 16) => K = k + (1 << 16) + // K1 = 85267 => k1 = 20091 + // K2 = 35468 => k2 = -30068 + // - The multiplication of a variable by a constant become the sum of the + // variable and the multiplication of that variable by the associated + // constant: + // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x + const __m128i k1 = _mm_set1_epi16(20091); + const __m128i k2 = _mm_set1_epi16(-30068); + __m128i T0, T1, T2, T3; + + // Load and concatenate the transform coefficients (we'll do two transforms + // in parallel). In the case of only one transform, the second half of the + // vectors will just contain random value we'll never use nor store. + __m128i in0, in1, in2, in3; + { + in0 = _mm_loadl_epi64((const __m128i*)&in[0]); + in1 = _mm_loadl_epi64((const __m128i*)&in[4]); + in2 = _mm_loadl_epi64((const __m128i*)&in[8]); + in3 = _mm_loadl_epi64((const __m128i*)&in[12]); + // a00 a10 a20 a30 x x x x + // a01 a11 a21 a31 x x x x + // a02 a12 a22 a32 x x x x + // a03 a13 a23 a33 x x x x + if (do_two) { + const __m128i inB0 = _mm_loadl_epi64((const __m128i*)&in[16]); + const __m128i inB1 = _mm_loadl_epi64((const __m128i*)&in[20]); + const __m128i inB2 = _mm_loadl_epi64((const __m128i*)&in[24]); + const __m128i inB3 = _mm_loadl_epi64((const __m128i*)&in[28]); + in0 = _mm_unpacklo_epi64(in0, inB0); + in1 = _mm_unpacklo_epi64(in1, inB1); + in2 = _mm_unpacklo_epi64(in2, inB2); + in3 = _mm_unpacklo_epi64(in3, inB3); + // a00 a10 a20 a30 b00 b10 b20 b30 + // a01 a11 a21 a31 b01 b11 b21 b31 + // a02 a12 a22 a32 b02 b12 b22 b32 + // a03 a13 a23 a33 b03 b13 b23 b33 + } + } + + // Vertical pass and subsequent transpose. + { + // First pass, c and d calculations are longer because of the "trick" + // multiplications. + const __m128i a = _mm_add_epi16(in0, in2); + const __m128i b = _mm_sub_epi16(in0, in2); + // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3 + const __m128i c1 = _mm_mulhi_epi16(in1, k2); + const __m128i c2 = _mm_mulhi_epi16(in3, k1); + const __m128i c3 = _mm_sub_epi16(in1, in3); + const __m128i c4 = _mm_sub_epi16(c1, c2); + const __m128i c = _mm_add_epi16(c3, c4); + // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3 + const __m128i d1 = _mm_mulhi_epi16(in1, k1); + const __m128i d2 = _mm_mulhi_epi16(in3, k2); + const __m128i d3 = _mm_add_epi16(in1, in3); + const __m128i d4 = _mm_add_epi16(d1, d2); + const __m128i d = _mm_add_epi16(d3, d4); + + // Second pass. + const __m128i tmp0 = _mm_add_epi16(a, d); + const __m128i tmp1 = _mm_add_epi16(b, c); + const __m128i tmp2 = _mm_sub_epi16(b, c); + const __m128i tmp3 = _mm_sub_epi16(a, d); + + // Transpose the two 4x4. + VP8Transpose_2_4x4_16b(&tmp0, &tmp1, &tmp2, &tmp3, &T0, &T1, &T2, &T3); + } + + // Horizontal pass and subsequent transpose. + { + // First pass, c and d calculations are longer because of the "trick" + // multiplications. + const __m128i four = _mm_set1_epi16(4); + const __m128i dc = _mm_add_epi16(T0, four); + const __m128i a = _mm_add_epi16(dc, T2); + const __m128i b = _mm_sub_epi16(dc, T2); + // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3 + const __m128i c1 = _mm_mulhi_epi16(T1, k2); + const __m128i c2 = _mm_mulhi_epi16(T3, k1); + const __m128i c3 = _mm_sub_epi16(T1, T3); + const __m128i c4 = _mm_sub_epi16(c1, c2); + const __m128i c = _mm_add_epi16(c3, c4); + // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3 + const __m128i d1 = _mm_mulhi_epi16(T1, k1); + const __m128i d2 = _mm_mulhi_epi16(T3, k2); + const __m128i d3 = _mm_add_epi16(T1, T3); + const __m128i d4 = _mm_add_epi16(d1, d2); + const __m128i d = _mm_add_epi16(d3, d4); + + // Second pass. + const __m128i tmp0 = _mm_add_epi16(a, d); + const __m128i tmp1 = _mm_add_epi16(b, c); + const __m128i tmp2 = _mm_sub_epi16(b, c); + const __m128i tmp3 = _mm_sub_epi16(a, d); + const __m128i shifted0 = _mm_srai_epi16(tmp0, 3); + const __m128i shifted1 = _mm_srai_epi16(tmp1, 3); + const __m128i shifted2 = _mm_srai_epi16(tmp2, 3); + const __m128i shifted3 = _mm_srai_epi16(tmp3, 3); + + // Transpose the two 4x4. + VP8Transpose_2_4x4_16b(&shifted0, &shifted1, &shifted2, &shifted3, &T0, &T1, + &T2, &T3); + } + + // Add inverse transform to 'dst' and store. + { + const __m128i zero = _mm_setzero_si128(); + // Load the reference(s). + __m128i dst0, dst1, dst2, dst3; + if (do_two) { + // Load eight bytes/pixels per line. + dst0 = _mm_loadl_epi64((__m128i*)(dst + 0 * BPS)); + dst1 = _mm_loadl_epi64((__m128i*)(dst + 1 * BPS)); + dst2 = _mm_loadl_epi64((__m128i*)(dst + 2 * BPS)); + dst3 = _mm_loadl_epi64((__m128i*)(dst + 3 * BPS)); + } else { + // Load four bytes/pixels per line. + dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS)); + dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS)); + dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS)); + dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS)); + } + // Convert to 16b. + dst0 = _mm_unpacklo_epi8(dst0, zero); + dst1 = _mm_unpacklo_epi8(dst1, zero); + dst2 = _mm_unpacklo_epi8(dst2, zero); + dst3 = _mm_unpacklo_epi8(dst3, zero); + // Add the inverse transform(s). + dst0 = _mm_add_epi16(dst0, T0); + dst1 = _mm_add_epi16(dst1, T1); + dst2 = _mm_add_epi16(dst2, T2); + dst3 = _mm_add_epi16(dst3, T3); + // Unsigned saturate to 8b. + dst0 = _mm_packus_epi16(dst0, dst0); + dst1 = _mm_packus_epi16(dst1, dst1); + dst2 = _mm_packus_epi16(dst2, dst2); + dst3 = _mm_packus_epi16(dst3, dst3); + // Store the results. + if (do_two) { + // Store eight bytes/pixels per line. + _mm_storel_epi64((__m128i*)(dst + 0 * BPS), dst0); + _mm_storel_epi64((__m128i*)(dst + 1 * BPS), dst1); + _mm_storel_epi64((__m128i*)(dst + 2 * BPS), dst2); + _mm_storel_epi64((__m128i*)(dst + 3 * BPS), dst3); + } else { + // Store four bytes/pixels per line. + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0)); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1)); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2)); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3)); + } + } +} + +#if (USE_TRANSFORM_AC3 == 1) +#define MUL(a, b) (((a) * (b)) >> 16) +static void TransformAC3(const int16_t* in, uint8_t* dst) { + static const int kC1 = 20091 + (1 << 16); + static const int kC2 = 35468; + const __m128i A = _mm_set1_epi16(in[0] + 4); + const __m128i c4 = _mm_set1_epi16(MUL(in[4], kC2)); + const __m128i d4 = _mm_set1_epi16(MUL(in[4], kC1)); + const int c1 = MUL(in[1], kC2); + const int d1 = MUL(in[1], kC1); + const __m128i CD = _mm_set_epi16(0, 0, 0, 0, -d1, -c1, c1, d1); + const __m128i B = _mm_adds_epi16(A, CD); + const __m128i m0 = _mm_adds_epi16(B, d4); + const __m128i m1 = _mm_adds_epi16(B, c4); + const __m128i m2 = _mm_subs_epi16(B, c4); + const __m128i m3 = _mm_subs_epi16(B, d4); + const __m128i zero = _mm_setzero_si128(); + // Load the source pixels. + __m128i dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS)); + __m128i dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS)); + __m128i dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS)); + __m128i dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS)); + // Convert to 16b. + dst0 = _mm_unpacklo_epi8(dst0, zero); + dst1 = _mm_unpacklo_epi8(dst1, zero); + dst2 = _mm_unpacklo_epi8(dst2, zero); + dst3 = _mm_unpacklo_epi8(dst3, zero); + // Add the inverse transform. + dst0 = _mm_adds_epi16(dst0, _mm_srai_epi16(m0, 3)); + dst1 = _mm_adds_epi16(dst1, _mm_srai_epi16(m1, 3)); + dst2 = _mm_adds_epi16(dst2, _mm_srai_epi16(m2, 3)); + dst3 = _mm_adds_epi16(dst3, _mm_srai_epi16(m3, 3)); + // Unsigned saturate to 8b. + dst0 = _mm_packus_epi16(dst0, dst0); + dst1 = _mm_packus_epi16(dst1, dst1); + dst2 = _mm_packus_epi16(dst2, dst2); + dst3 = _mm_packus_epi16(dst3, dst3); + // Store the results. + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0)); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1)); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2)); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3)); +} +#undef MUL +#endif // USE_TRANSFORM_AC3 + +//------------------------------------------------------------------------------ +// Loop Filter (Paragraph 15) + +// Compute abs(p - q) = subs(p - q) OR subs(q - p) +#define MM_ABS(p, q) _mm_or_si128( \ + _mm_subs_epu8((q), (p)), \ + _mm_subs_epu8((p), (q))) + +// Shift each byte of "x" by 3 bits while preserving by the sign bit. +static WEBP_INLINE void SignedShift8b_SSE2(__m128i* const x) { + const __m128i zero = _mm_setzero_si128(); + const __m128i lo_0 = _mm_unpacklo_epi8(zero, *x); + const __m128i hi_0 = _mm_unpackhi_epi8(zero, *x); + const __m128i lo_1 = _mm_srai_epi16(lo_0, 3 + 8); + const __m128i hi_1 = _mm_srai_epi16(hi_0, 3 + 8); + *x = _mm_packs_epi16(lo_1, hi_1); +} + +#define FLIP_SIGN_BIT2(a, b) { \ + (a) = _mm_xor_si128(a, sign_bit); \ + (b) = _mm_xor_si128(b, sign_bit); \ +} + +#define FLIP_SIGN_BIT4(a, b, c, d) { \ + FLIP_SIGN_BIT2(a, b); \ + FLIP_SIGN_BIT2(c, d); \ +} + +// input/output is uint8_t +static WEBP_INLINE void GetNotHEV_SSE2(const __m128i* const p1, + const __m128i* const p0, + const __m128i* const q0, + const __m128i* const q1, + int hev_thresh, __m128i* const not_hev) { + const __m128i zero = _mm_setzero_si128(); + const __m128i t_1 = MM_ABS(*p1, *p0); + const __m128i t_2 = MM_ABS(*q1, *q0); + + const __m128i h = _mm_set1_epi8(hev_thresh); + const __m128i t_max = _mm_max_epu8(t_1, t_2); + + const __m128i t_max_h = _mm_subs_epu8(t_max, h); + *not_hev = _mm_cmpeq_epi8(t_max_h, zero); // not_hev <= t1 && not_hev <= t2 +} + +// input pixels are int8_t +static WEBP_INLINE void GetBaseDelta_SSE2(const __m128i* const p1, + const __m128i* const p0, + const __m128i* const q0, + const __m128i* const q1, + __m128i* const delta) { + // beware of addition order, for saturation! + const __m128i p1_q1 = _mm_subs_epi8(*p1, *q1); // p1 - q1 + const __m128i q0_p0 = _mm_subs_epi8(*q0, *p0); // q0 - p0 + const __m128i s1 = _mm_adds_epi8(p1_q1, q0_p0); // p1 - q1 + 1 * (q0 - p0) + const __m128i s2 = _mm_adds_epi8(q0_p0, s1); // p1 - q1 + 2 * (q0 - p0) + const __m128i s3 = _mm_adds_epi8(q0_p0, s2); // p1 - q1 + 3 * (q0 - p0) + *delta = s3; +} + +// input and output are int8_t +static WEBP_INLINE void DoSimpleFilter_SSE2(__m128i* const p0, + __m128i* const q0, + const __m128i* const fl) { + const __m128i k3 = _mm_set1_epi8(3); + const __m128i k4 = _mm_set1_epi8(4); + __m128i v3 = _mm_adds_epi8(*fl, k3); + __m128i v4 = _mm_adds_epi8(*fl, k4); + + SignedShift8b_SSE2(&v4); // v4 >> 3 + SignedShift8b_SSE2(&v3); // v3 >> 3 + *q0 = _mm_subs_epi8(*q0, v4); // q0 -= v4 + *p0 = _mm_adds_epi8(*p0, v3); // p0 += v3 +} + +// Updates values of 2 pixels at MB edge during complex filtering. +// Update operations: +// q = q - delta and p = p + delta; where delta = [(a_hi >> 7), (a_lo >> 7)] +// Pixels 'pi' and 'qi' are int8_t on input, uint8_t on output (sign flip). +static WEBP_INLINE void Update2Pixels_SSE2(__m128i* const pi, __m128i* const qi, + const __m128i* const a0_lo, + const __m128i* const a0_hi) { + const __m128i a1_lo = _mm_srai_epi16(*a0_lo, 7); + const __m128i a1_hi = _mm_srai_epi16(*a0_hi, 7); + const __m128i delta = _mm_packs_epi16(a1_lo, a1_hi); + const __m128i sign_bit = _mm_set1_epi8((char)0x80); + *pi = _mm_adds_epi8(*pi, delta); + *qi = _mm_subs_epi8(*qi, delta); + FLIP_SIGN_BIT2(*pi, *qi); +} + +// input pixels are uint8_t +static WEBP_INLINE void NeedsFilter_SSE2(const __m128i* const p1, + const __m128i* const p0, + const __m128i* const q0, + const __m128i* const q1, + int thresh, __m128i* const mask) { + const __m128i m_thresh = _mm_set1_epi8((char)thresh); + const __m128i t1 = MM_ABS(*p1, *q1); // abs(p1 - q1) + const __m128i kFE = _mm_set1_epi8((char)0xFE); + const __m128i t2 = _mm_and_si128(t1, kFE); // set lsb of each byte to zero + const __m128i t3 = _mm_srli_epi16(t2, 1); // abs(p1 - q1) / 2 + + const __m128i t4 = MM_ABS(*p0, *q0); // abs(p0 - q0) + const __m128i t5 = _mm_adds_epu8(t4, t4); // abs(p0 - q0) * 2 + const __m128i t6 = _mm_adds_epu8(t5, t3); // abs(p0-q0)*2 + abs(p1-q1)/2 + + const __m128i t7 = _mm_subs_epu8(t6, m_thresh); // mask <= m_thresh + *mask = _mm_cmpeq_epi8(t7, _mm_setzero_si128()); +} + +//------------------------------------------------------------------------------ +// Edge filtering functions + +// Applies filter on 2 pixels (p0 and q0) +static WEBP_INLINE void DoFilter2_SSE2(__m128i* const p1, __m128i* const p0, + __m128i* const q0, __m128i* const q1, + int thresh) { + __m128i a, mask; + const __m128i sign_bit = _mm_set1_epi8((char)0x80); + // convert p1/q1 to int8_t (for GetBaseDelta_SSE2) + const __m128i p1s = _mm_xor_si128(*p1, sign_bit); + const __m128i q1s = _mm_xor_si128(*q1, sign_bit); + + NeedsFilter_SSE2(p1, p0, q0, q1, thresh, &mask); + + FLIP_SIGN_BIT2(*p0, *q0); + GetBaseDelta_SSE2(&p1s, p0, q0, &q1s, &a); + a = _mm_and_si128(a, mask); // mask filter values we don't care about + DoSimpleFilter_SSE2(p0, q0, &a); + FLIP_SIGN_BIT2(*p0, *q0); +} + +// Applies filter on 4 pixels (p1, p0, q0 and q1) +static WEBP_INLINE void DoFilter4_SSE2(__m128i* const p1, __m128i* const p0, + __m128i* const q0, __m128i* const q1, + const __m128i* const mask, + int hev_thresh) { + const __m128i zero = _mm_setzero_si128(); + const __m128i sign_bit = _mm_set1_epi8((char)0x80); + const __m128i k64 = _mm_set1_epi8(64); + const __m128i k3 = _mm_set1_epi8(3); + const __m128i k4 = _mm_set1_epi8(4); + __m128i not_hev; + __m128i t1, t2, t3; + + // compute hev mask + GetNotHEV_SSE2(p1, p0, q0, q1, hev_thresh, ¬_hev); + + // convert to signed values + FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1); + + t1 = _mm_subs_epi8(*p1, *q1); // p1 - q1 + t1 = _mm_andnot_si128(not_hev, t1); // hev(p1 - q1) + t2 = _mm_subs_epi8(*q0, *p0); // q0 - p0 + t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 1 * (q0 - p0) + t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 2 * (q0 - p0) + t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 3 * (q0 - p0) + t1 = _mm_and_si128(t1, *mask); // mask filter values we don't care about + + t2 = _mm_adds_epi8(t1, k3); // 3 * (q0 - p0) + hev(p1 - q1) + 3 + t3 = _mm_adds_epi8(t1, k4); // 3 * (q0 - p0) + hev(p1 - q1) + 4 + SignedShift8b_SSE2(&t2); // (3 * (q0 - p0) + hev(p1 - q1) + 3) >> 3 + SignedShift8b_SSE2(&t3); // (3 * (q0 - p0) + hev(p1 - q1) + 4) >> 3 + *p0 = _mm_adds_epi8(*p0, t2); // p0 += t2 + *q0 = _mm_subs_epi8(*q0, t3); // q0 -= t3 + FLIP_SIGN_BIT2(*p0, *q0); + + // this is equivalent to signed (a + 1) >> 1 calculation + t2 = _mm_add_epi8(t3, sign_bit); + t3 = _mm_avg_epu8(t2, zero); + t3 = _mm_sub_epi8(t3, k64); + + t3 = _mm_and_si128(not_hev, t3); // if !hev + *q1 = _mm_subs_epi8(*q1, t3); // q1 -= t3 + *p1 = _mm_adds_epi8(*p1, t3); // p1 += t3 + FLIP_SIGN_BIT2(*p1, *q1); +} + +// Applies filter on 6 pixels (p2, p1, p0, q0, q1 and q2) +static WEBP_INLINE void DoFilter6_SSE2(__m128i* const p2, __m128i* const p1, + __m128i* const p0, __m128i* const q0, + __m128i* const q1, __m128i* const q2, + const __m128i* const mask, + int hev_thresh) { + const __m128i zero = _mm_setzero_si128(); + const __m128i sign_bit = _mm_set1_epi8((char)0x80); + __m128i a, not_hev; + + // compute hev mask + GetNotHEV_SSE2(p1, p0, q0, q1, hev_thresh, ¬_hev); + + FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1); + FLIP_SIGN_BIT2(*p2, *q2); + GetBaseDelta_SSE2(p1, p0, q0, q1, &a); + + { // do simple filter on pixels with hev + const __m128i m = _mm_andnot_si128(not_hev, *mask); + const __m128i f = _mm_and_si128(a, m); + DoSimpleFilter_SSE2(p0, q0, &f); + } + + { // do strong filter on pixels with not hev + const __m128i k9 = _mm_set1_epi16(0x0900); + const __m128i k63 = _mm_set1_epi16(63); + + const __m128i m = _mm_and_si128(not_hev, *mask); + const __m128i f = _mm_and_si128(a, m); + + const __m128i f_lo = _mm_unpacklo_epi8(zero, f); + const __m128i f_hi = _mm_unpackhi_epi8(zero, f); + + const __m128i f9_lo = _mm_mulhi_epi16(f_lo, k9); // Filter (lo) * 9 + const __m128i f9_hi = _mm_mulhi_epi16(f_hi, k9); // Filter (hi) * 9 + + const __m128i a2_lo = _mm_add_epi16(f9_lo, k63); // Filter * 9 + 63 + const __m128i a2_hi = _mm_add_epi16(f9_hi, k63); // Filter * 9 + 63 + + const __m128i a1_lo = _mm_add_epi16(a2_lo, f9_lo); // Filter * 18 + 63 + const __m128i a1_hi = _mm_add_epi16(a2_hi, f9_hi); // Filter * 18 + 63 + + const __m128i a0_lo = _mm_add_epi16(a1_lo, f9_lo); // Filter * 27 + 63 + const __m128i a0_hi = _mm_add_epi16(a1_hi, f9_hi); // Filter * 27 + 63 + + Update2Pixels_SSE2(p2, q2, &a2_lo, &a2_hi); + Update2Pixels_SSE2(p1, q1, &a1_lo, &a1_hi); + Update2Pixels_SSE2(p0, q0, &a0_lo, &a0_hi); + } +} + +// reads 8 rows across a vertical edge. +static WEBP_INLINE void Load8x4_SSE2(const uint8_t* const b, int stride, + __m128i* const p, __m128i* const q) { + // A0 = 63 62 61 60 23 22 21 20 43 42 41 40 03 02 01 00 + // A1 = 73 72 71 70 33 32 31 30 53 52 51 50 13 12 11 10 + const __m128i A0 = _mm_set_epi32( + WebPMemToUint32(&b[6 * stride]), WebPMemToUint32(&b[2 * stride]), + WebPMemToUint32(&b[4 * stride]), WebPMemToUint32(&b[0 * stride])); + const __m128i A1 = _mm_set_epi32( + WebPMemToUint32(&b[7 * stride]), WebPMemToUint32(&b[3 * stride]), + WebPMemToUint32(&b[5 * stride]), WebPMemToUint32(&b[1 * stride])); + + // B0 = 53 43 52 42 51 41 50 40 13 03 12 02 11 01 10 00 + // B1 = 73 63 72 62 71 61 70 60 33 23 32 22 31 21 30 20 + const __m128i B0 = _mm_unpacklo_epi8(A0, A1); + const __m128i B1 = _mm_unpackhi_epi8(A0, A1); + + // C0 = 33 23 13 03 32 22 12 02 31 21 11 01 30 20 10 00 + // C1 = 73 63 53 43 72 62 52 42 71 61 51 41 70 60 50 40 + const __m128i C0 = _mm_unpacklo_epi16(B0, B1); + const __m128i C1 = _mm_unpackhi_epi16(B0, B1); + + // *p = 71 61 51 41 31 21 11 01 70 60 50 40 30 20 10 00 + // *q = 73 63 53 43 33 23 13 03 72 62 52 42 32 22 12 02 + *p = _mm_unpacklo_epi32(C0, C1); + *q = _mm_unpackhi_epi32(C0, C1); +} + +static WEBP_INLINE void Load16x4_SSE2(const uint8_t* const r0, + const uint8_t* const r8, + int stride, + __m128i* const p1, __m128i* const p0, + __m128i* const q0, __m128i* const q1) { + // Assume the pixels around the edge (|) are numbered as follows + // 00 01 | 02 03 + // 10 11 | 12 13 + // ... | ... + // e0 e1 | e2 e3 + // f0 f1 | f2 f3 + // + // r0 is pointing to the 0th row (00) + // r8 is pointing to the 8th row (80) + + // Load + // p1 = 71 61 51 41 31 21 11 01 70 60 50 40 30 20 10 00 + // q0 = 73 63 53 43 33 23 13 03 72 62 52 42 32 22 12 02 + // p0 = f1 e1 d1 c1 b1 a1 91 81 f0 e0 d0 c0 b0 a0 90 80 + // q1 = f3 e3 d3 c3 b3 a3 93 83 f2 e2 d2 c2 b2 a2 92 82 + Load8x4_SSE2(r0, stride, p1, q0); + Load8x4_SSE2(r8, stride, p0, q1); + + { + // p1 = f0 e0 d0 c0 b0 a0 90 80 70 60 50 40 30 20 10 00 + // p0 = f1 e1 d1 c1 b1 a1 91 81 71 61 51 41 31 21 11 01 + // q0 = f2 e2 d2 c2 b2 a2 92 82 72 62 52 42 32 22 12 02 + // q1 = f3 e3 d3 c3 b3 a3 93 83 73 63 53 43 33 23 13 03 + const __m128i t1 = *p1; + const __m128i t2 = *q0; + *p1 = _mm_unpacklo_epi64(t1, *p0); + *p0 = _mm_unpackhi_epi64(t1, *p0); + *q0 = _mm_unpacklo_epi64(t2, *q1); + *q1 = _mm_unpackhi_epi64(t2, *q1); + } +} + +static WEBP_INLINE void Store4x4_SSE2(__m128i* const x, + uint8_t* dst, int stride) { + int i; + for (i = 0; i < 4; ++i, dst += stride) { + WebPUint32ToMem(dst, _mm_cvtsi128_si32(*x)); + *x = _mm_srli_si128(*x, 4); + } +} + +// Transpose back and store +static WEBP_INLINE void Store16x4_SSE2(const __m128i* const p1, + const __m128i* const p0, + const __m128i* const q0, + const __m128i* const q1, + uint8_t* r0, uint8_t* r8, + int stride) { + __m128i t1, p1_s, p0_s, q0_s, q1_s; + + // p0 = 71 70 61 60 51 50 41 40 31 30 21 20 11 10 01 00 + // p1 = f1 f0 e1 e0 d1 d0 c1 c0 b1 b0 a1 a0 91 90 81 80 + t1 = *p0; + p0_s = _mm_unpacklo_epi8(*p1, t1); + p1_s = _mm_unpackhi_epi8(*p1, t1); + + // q0 = 73 72 63 62 53 52 43 42 33 32 23 22 13 12 03 02 + // q1 = f3 f2 e3 e2 d3 d2 c3 c2 b3 b2 a3 a2 93 92 83 82 + t1 = *q0; + q0_s = _mm_unpacklo_epi8(t1, *q1); + q1_s = _mm_unpackhi_epi8(t1, *q1); + + // p0 = 33 32 31 30 23 22 21 20 13 12 11 10 03 02 01 00 + // q0 = 73 72 71 70 63 62 61 60 53 52 51 50 43 42 41 40 + t1 = p0_s; + p0_s = _mm_unpacklo_epi16(t1, q0_s); + q0_s = _mm_unpackhi_epi16(t1, q0_s); + + // p1 = b3 b2 b1 b0 a3 a2 a1 a0 93 92 91 90 83 82 81 80 + // q1 = f3 f2 f1 f0 e3 e2 e1 e0 d3 d2 d1 d0 c3 c2 c1 c0 + t1 = p1_s; + p1_s = _mm_unpacklo_epi16(t1, q1_s); + q1_s = _mm_unpackhi_epi16(t1, q1_s); + + Store4x4_SSE2(&p0_s, r0, stride); + r0 += 4 * stride; + Store4x4_SSE2(&q0_s, r0, stride); + + Store4x4_SSE2(&p1_s, r8, stride); + r8 += 4 * stride; + Store4x4_SSE2(&q1_s, r8, stride); +} + +//------------------------------------------------------------------------------ +// Simple In-loop filtering (Paragraph 15.2) + +static void SimpleVFilter16_SSE2(uint8_t* p, int stride, int thresh) { + // Load + __m128i p1 = _mm_loadu_si128((__m128i*)&p[-2 * stride]); + __m128i p0 = _mm_loadu_si128((__m128i*)&p[-stride]); + __m128i q0 = _mm_loadu_si128((__m128i*)&p[0]); + __m128i q1 = _mm_loadu_si128((__m128i*)&p[stride]); + + DoFilter2_SSE2(&p1, &p0, &q0, &q1, thresh); + + // Store + _mm_storeu_si128((__m128i*)&p[-stride], p0); + _mm_storeu_si128((__m128i*)&p[0], q0); +} + +static void SimpleHFilter16_SSE2(uint8_t* p, int stride, int thresh) { + __m128i p1, p0, q0, q1; + + p -= 2; // beginning of p1 + + Load16x4_SSE2(p, p + 8 * stride, stride, &p1, &p0, &q0, &q1); + DoFilter2_SSE2(&p1, &p0, &q0, &q1, thresh); + Store16x4_SSE2(&p1, &p0, &q0, &q1, p, p + 8 * stride, stride); +} + +static void SimpleVFilter16i_SSE2(uint8_t* p, int stride, int thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4 * stride; + SimpleVFilter16_SSE2(p, stride, thresh); + } +} + +static void SimpleHFilter16i_SSE2(uint8_t* p, int stride, int thresh) { + int k; + for (k = 3; k > 0; --k) { + p += 4; + SimpleHFilter16_SSE2(p, stride, thresh); + } +} + +//------------------------------------------------------------------------------ +// Complex In-loop filtering (Paragraph 15.3) + +#define MAX_DIFF1(p3, p2, p1, p0, m) do { \ + (m) = MM_ABS(p1, p0); \ + (m) = _mm_max_epu8(m, MM_ABS(p3, p2)); \ + (m) = _mm_max_epu8(m, MM_ABS(p2, p1)); \ +} while (0) + +#define MAX_DIFF2(p3, p2, p1, p0, m) do { \ + (m) = _mm_max_epu8(m, MM_ABS(p1, p0)); \ + (m) = _mm_max_epu8(m, MM_ABS(p3, p2)); \ + (m) = _mm_max_epu8(m, MM_ABS(p2, p1)); \ +} while (0) + +#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) { \ + (e1) = _mm_loadu_si128((__m128i*)&(p)[0 * (stride)]); \ + (e2) = _mm_loadu_si128((__m128i*)&(p)[1 * (stride)]); \ + (e3) = _mm_loadu_si128((__m128i*)&(p)[2 * (stride)]); \ + (e4) = _mm_loadu_si128((__m128i*)&(p)[3 * (stride)]); \ +} + +#define LOADUV_H_EDGE(p, u, v, stride) do { \ + const __m128i U = _mm_loadl_epi64((__m128i*)&(u)[(stride)]); \ + const __m128i V = _mm_loadl_epi64((__m128i*)&(v)[(stride)]); \ + (p) = _mm_unpacklo_epi64(U, V); \ +} while (0) + +#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) { \ + LOADUV_H_EDGE(e1, u, v, 0 * (stride)); \ + LOADUV_H_EDGE(e2, u, v, 1 * (stride)); \ + LOADUV_H_EDGE(e3, u, v, 2 * (stride)); \ + LOADUV_H_EDGE(e4, u, v, 3 * (stride)); \ +} + +#define STOREUV(p, u, v, stride) { \ + _mm_storel_epi64((__m128i*)&(u)[(stride)], p); \ + (p) = _mm_srli_si128(p, 8); \ + _mm_storel_epi64((__m128i*)&(v)[(stride)], p); \ +} + +static WEBP_INLINE void ComplexMask_SSE2(const __m128i* const p1, + const __m128i* const p0, + const __m128i* const q0, + const __m128i* const q1, + int thresh, int ithresh, + __m128i* const mask) { + const __m128i it = _mm_set1_epi8(ithresh); + const __m128i diff = _mm_subs_epu8(*mask, it); + const __m128i thresh_mask = _mm_cmpeq_epi8(diff, _mm_setzero_si128()); + __m128i filter_mask; + NeedsFilter_SSE2(p1, p0, q0, q1, thresh, &filter_mask); + *mask = _mm_and_si128(thresh_mask, filter_mask); +} + +// on macroblock edges +static void VFilter16_SSE2(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + __m128i t1; + __m128i mask; + __m128i p2, p1, p0, q0, q1, q2; + + // Load p3, p2, p1, p0 + LOAD_H_EDGES4(p - 4 * stride, stride, t1, p2, p1, p0); + MAX_DIFF1(t1, p2, p1, p0, mask); + + // Load q0, q1, q2, q3 + LOAD_H_EDGES4(p, stride, q0, q1, q2, t1); + MAX_DIFF2(t1, q2, q1, q0, mask); + + ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask); + DoFilter6_SSE2(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh); + + // Store + _mm_storeu_si128((__m128i*)&p[-3 * stride], p2); + _mm_storeu_si128((__m128i*)&p[-2 * stride], p1); + _mm_storeu_si128((__m128i*)&p[-1 * stride], p0); + _mm_storeu_si128((__m128i*)&p[+0 * stride], q0); + _mm_storeu_si128((__m128i*)&p[+1 * stride], q1); + _mm_storeu_si128((__m128i*)&p[+2 * stride], q2); +} + +static void HFilter16_SSE2(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + __m128i mask; + __m128i p3, p2, p1, p0, q0, q1, q2, q3; + + uint8_t* const b = p - 4; + Load16x4_SSE2(b, b + 8 * stride, stride, &p3, &p2, &p1, &p0); + MAX_DIFF1(p3, p2, p1, p0, mask); + + Load16x4_SSE2(p, p + 8 * stride, stride, &q0, &q1, &q2, &q3); + MAX_DIFF2(q3, q2, q1, q0, mask); + + ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask); + DoFilter6_SSE2(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh); + + Store16x4_SSE2(&p3, &p2, &p1, &p0, b, b + 8 * stride, stride); + Store16x4_SSE2(&q0, &q1, &q2, &q3, p, p + 8 * stride, stride); +} + +// on three inner edges +static void VFilter16i_SSE2(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + int k; + __m128i p3, p2, p1, p0; // loop invariants + + LOAD_H_EDGES4(p, stride, p3, p2, p1, p0); // prologue + + for (k = 3; k > 0; --k) { + __m128i mask, tmp1, tmp2; + uint8_t* const b = p + 2 * stride; // beginning of p1 + p += 4 * stride; + + MAX_DIFF1(p3, p2, p1, p0, mask); // compute partial mask + LOAD_H_EDGES4(p, stride, p3, p2, tmp1, tmp2); + MAX_DIFF2(p3, p2, tmp1, tmp2, mask); + + // p3 and p2 are not just temporary variables here: they will be + // re-used for next span. And q2/q3 will become p1/p0 accordingly. + ComplexMask_SSE2(&p1, &p0, &p3, &p2, thresh, ithresh, &mask); + DoFilter4_SSE2(&p1, &p0, &p3, &p2, &mask, hev_thresh); + + // Store + _mm_storeu_si128((__m128i*)&b[0 * stride], p1); + _mm_storeu_si128((__m128i*)&b[1 * stride], p0); + _mm_storeu_si128((__m128i*)&b[2 * stride], p3); + _mm_storeu_si128((__m128i*)&b[3 * stride], p2); + + // rotate samples + p1 = tmp1; + p0 = tmp2; + } +} + +static void HFilter16i_SSE2(uint8_t* p, int stride, + int thresh, int ithresh, int hev_thresh) { + int k; + __m128i p3, p2, p1, p0; // loop invariants + + Load16x4_SSE2(p, p + 8 * stride, stride, &p3, &p2, &p1, &p0); // prologue + + for (k = 3; k > 0; --k) { + __m128i mask, tmp1, tmp2; + uint8_t* const b = p + 2; // beginning of p1 + + p += 4; // beginning of q0 (and next span) + + MAX_DIFF1(p3, p2, p1, p0, mask); // compute partial mask + Load16x4_SSE2(p, p + 8 * stride, stride, &p3, &p2, &tmp1, &tmp2); + MAX_DIFF2(p3, p2, tmp1, tmp2, mask); + + ComplexMask_SSE2(&p1, &p0, &p3, &p2, thresh, ithresh, &mask); + DoFilter4_SSE2(&p1, &p0, &p3, &p2, &mask, hev_thresh); + + Store16x4_SSE2(&p1, &p0, &p3, &p2, b, b + 8 * stride, stride); + + // rotate samples + p1 = tmp1; + p0 = tmp2; + } +} + +// 8-pixels wide variant, for chroma filtering +static void VFilter8_SSE2(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + __m128i mask; + __m128i t1, p2, p1, p0, q0, q1, q2; + + // Load p3, p2, p1, p0 + LOADUV_H_EDGES4(u - 4 * stride, v - 4 * stride, stride, t1, p2, p1, p0); + MAX_DIFF1(t1, p2, p1, p0, mask); + + // Load q0, q1, q2, q3 + LOADUV_H_EDGES4(u, v, stride, q0, q1, q2, t1); + MAX_DIFF2(t1, q2, q1, q0, mask); + + ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask); + DoFilter6_SSE2(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh); + + // Store + STOREUV(p2, u, v, -3 * stride); + STOREUV(p1, u, v, -2 * stride); + STOREUV(p0, u, v, -1 * stride); + STOREUV(q0, u, v, 0 * stride); + STOREUV(q1, u, v, 1 * stride); + STOREUV(q2, u, v, 2 * stride); +} + +static void HFilter8_SSE2(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + __m128i mask; + __m128i p3, p2, p1, p0, q0, q1, q2, q3; + + uint8_t* const tu = u - 4; + uint8_t* const tv = v - 4; + Load16x4_SSE2(tu, tv, stride, &p3, &p2, &p1, &p0); + MAX_DIFF1(p3, p2, p1, p0, mask); + + Load16x4_SSE2(u, v, stride, &q0, &q1, &q2, &q3); + MAX_DIFF2(q3, q2, q1, q0, mask); + + ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask); + DoFilter6_SSE2(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh); + + Store16x4_SSE2(&p3, &p2, &p1, &p0, tu, tv, stride); + Store16x4_SSE2(&q0, &q1, &q2, &q3, u, v, stride); +} + +static void VFilter8i_SSE2(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + __m128i mask; + __m128i t1, t2, p1, p0, q0, q1; + + // Load p3, p2, p1, p0 + LOADUV_H_EDGES4(u, v, stride, t2, t1, p1, p0); + MAX_DIFF1(t2, t1, p1, p0, mask); + + u += 4 * stride; + v += 4 * stride; + + // Load q0, q1, q2, q3 + LOADUV_H_EDGES4(u, v, stride, q0, q1, t1, t2); + MAX_DIFF2(t2, t1, q1, q0, mask); + + ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask); + DoFilter4_SSE2(&p1, &p0, &q0, &q1, &mask, hev_thresh); + + // Store + STOREUV(p1, u, v, -2 * stride); + STOREUV(p0, u, v, -1 * stride); + STOREUV(q0, u, v, 0 * stride); + STOREUV(q1, u, v, 1 * stride); +} + +static void HFilter8i_SSE2(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_thresh) { + __m128i mask; + __m128i t1, t2, p1, p0, q0, q1; + Load16x4_SSE2(u, v, stride, &t2, &t1, &p1, &p0); // p3, p2, p1, p0 + MAX_DIFF1(t2, t1, p1, p0, mask); + + u += 4; // beginning of q0 + v += 4; + Load16x4_SSE2(u, v, stride, &q0, &q1, &t1, &t2); // q0, q1, q2, q3 + MAX_DIFF2(t2, t1, q1, q0, mask); + + ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask); + DoFilter4_SSE2(&p1, &p0, &q0, &q1, &mask, hev_thresh); + + u -= 2; // beginning of p1 + v -= 2; + Store16x4_SSE2(&p1, &p0, &q0, &q1, u, v, stride); +} + +//------------------------------------------------------------------------------ +// 4x4 predictions + +#define DST(x, y) dst[(x) + (y) * BPS] +#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2) + +// We use the following 8b-arithmetic tricks: +// (a + 2 * b + c + 2) >> 2 = (AC + b + 1) >> 1 +// where: AC = (a + c) >> 1 = [(a + c + 1) >> 1] - [(a^c) & 1] +// and: +// (a + 2 * b + c + 2) >> 2 = (AB + BC + 1) >> 1 - (ab|bc)&lsb +// where: AC = (a + b + 1) >> 1, BC = (b + c + 1) >> 1 +// and ab = a ^ b, bc = b ^ c, lsb = (AC^BC)&1 + +static void VE4_SSE2(uint8_t* dst) { // vertical + const __m128i one = _mm_set1_epi8(1); + const __m128i ABCDEFGH = _mm_loadl_epi64((__m128i*)(dst - BPS - 1)); + const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1); + const __m128i CDEFGH00 = _mm_srli_si128(ABCDEFGH, 2); + const __m128i a = _mm_avg_epu8(ABCDEFGH, CDEFGH00); + const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGH00), one); + const __m128i b = _mm_subs_epu8(a, lsb); + const __m128i avg = _mm_avg_epu8(b, BCDEFGH0); + const uint32_t vals = _mm_cvtsi128_si32(avg); + int i; + for (i = 0; i < 4; ++i) { + WebPUint32ToMem(dst + i * BPS, vals); + } +} + +static void LD4_SSE2(uint8_t* dst) { // Down-Left + const __m128i one = _mm_set1_epi8(1); + const __m128i ABCDEFGH = _mm_loadl_epi64((__m128i*)(dst - BPS)); + const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1); + const __m128i CDEFGH00 = _mm_srli_si128(ABCDEFGH, 2); + const __m128i CDEFGHH0 = _mm_insert_epi16(CDEFGH00, dst[-BPS + 7], 3); + const __m128i avg1 = _mm_avg_epu8(ABCDEFGH, CDEFGHH0); + const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one); + const __m128i avg2 = _mm_subs_epu8(avg1, lsb); + const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1))); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3))); +} + +static void VR4_SSE2(uint8_t* dst) { // Vertical-Right + const __m128i one = _mm_set1_epi8(1); + const int I = dst[-1 + 0 * BPS]; + const int J = dst[-1 + 1 * BPS]; + const int K = dst[-1 + 2 * BPS]; + const int X = dst[-1 - BPS]; + const __m128i XABCD = _mm_loadl_epi64((__m128i*)(dst - BPS - 1)); + const __m128i ABCD0 = _mm_srli_si128(XABCD, 1); + const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0); + const __m128i _XABCD = _mm_slli_si128(XABCD, 1); + const __m128i IXABCD = _mm_insert_epi16(_XABCD, (short)(I | (X << 8)), 0); + const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0); + const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one); + const __m128i avg2 = _mm_subs_epu8(avg1, lsb); + const __m128i efgh = _mm_avg_epu8(avg2, XABCD); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1))); + + // these two are hard to implement in SSE2, so we keep the C-version: + DST(0, 2) = AVG3(J, I, X); + DST(0, 3) = AVG3(K, J, I); +} + +static void VL4_SSE2(uint8_t* dst) { // Vertical-Left + const __m128i one = _mm_set1_epi8(1); + const __m128i ABCDEFGH = _mm_loadl_epi64((__m128i*)(dst - BPS)); + const __m128i BCDEFGH_ = _mm_srli_si128(ABCDEFGH, 1); + const __m128i CDEFGH__ = _mm_srli_si128(ABCDEFGH, 2); + const __m128i avg1 = _mm_avg_epu8(ABCDEFGH, BCDEFGH_); + const __m128i avg2 = _mm_avg_epu8(CDEFGH__, BCDEFGH_); + const __m128i avg3 = _mm_avg_epu8(avg1, avg2); + const __m128i lsb1 = _mm_and_si128(_mm_xor_si128(avg1, avg2), one); + const __m128i ab = _mm_xor_si128(ABCDEFGH, BCDEFGH_); + const __m128i bc = _mm_xor_si128(CDEFGH__, BCDEFGH_); + const __m128i abbc = _mm_or_si128(ab, bc); + const __m128i lsb2 = _mm_and_si128(abbc, lsb1); + const __m128i avg4 = _mm_subs_epu8(avg3, lsb2); + const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4)); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1))); + + // these two are hard to get and irregular + DST(3, 2) = (extra_out >> 0) & 0xff; + DST(3, 3) = (extra_out >> 8) & 0xff; +} + +static void RD4_SSE2(uint8_t* dst) { // Down-right + const __m128i one = _mm_set1_epi8(1); + const __m128i XABCD = _mm_loadl_epi64((__m128i*)(dst - BPS - 1)); + const __m128i ____XABCD = _mm_slli_si128(XABCD, 4); + const uint32_t I = dst[-1 + 0 * BPS]; + const uint32_t J = dst[-1 + 1 * BPS]; + const uint32_t K = dst[-1 + 2 * BPS]; + const uint32_t L = dst[-1 + 3 * BPS]; + const __m128i LKJI_____ = + _mm_cvtsi32_si128(L | (K << 8) | (J << 16) | (I << 24)); + const __m128i LKJIXABCD = _mm_or_si128(LKJI_____, ____XABCD); + const __m128i KJIXABCD_ = _mm_srli_si128(LKJIXABCD, 1); + const __m128i JIXABCD__ = _mm_srli_si128(LKJIXABCD, 2); + const __m128i avg1 = _mm_avg_epu8(JIXABCD__, LKJIXABCD); + const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one); + const __m128i avg2 = _mm_subs_epu8(avg1, lsb); + const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1))); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2))); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3))); +} + +#undef DST +#undef AVG3 + +//------------------------------------------------------------------------------ +// Luma 16x16 + +static WEBP_INLINE void TrueMotion_SSE2(uint8_t* dst, int size) { + const uint8_t* top = dst - BPS; + const __m128i zero = _mm_setzero_si128(); + int y; + if (size == 4) { + const __m128i top_values = _mm_cvtsi32_si128(WebPMemToUint32(top)); + const __m128i top_base = _mm_unpacklo_epi8(top_values, zero); + for (y = 0; y < 4; ++y, dst += BPS) { + const int val = dst[-1] - top[-1]; + const __m128i base = _mm_set1_epi16(val); + const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero); + WebPUint32ToMem(dst, _mm_cvtsi128_si32(out)); + } + } else if (size == 8) { + const __m128i top_values = _mm_loadl_epi64((const __m128i*)top); + const __m128i top_base = _mm_unpacklo_epi8(top_values, zero); + for (y = 0; y < 8; ++y, dst += BPS) { + const int val = dst[-1] - top[-1]; + const __m128i base = _mm_set1_epi16(val); + const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero); + _mm_storel_epi64((__m128i*)dst, out); + } + } else { + const __m128i top_values = _mm_loadu_si128((const __m128i*)top); + const __m128i top_base_0 = _mm_unpacklo_epi8(top_values, zero); + const __m128i top_base_1 = _mm_unpackhi_epi8(top_values, zero); + for (y = 0; y < 16; ++y, dst += BPS) { + const int val = dst[-1] - top[-1]; + const __m128i base = _mm_set1_epi16(val); + const __m128i out_0 = _mm_add_epi16(base, top_base_0); + const __m128i out_1 = _mm_add_epi16(base, top_base_1); + const __m128i out = _mm_packus_epi16(out_0, out_1); + _mm_storeu_si128((__m128i*)dst, out); + } + } +} + +static void TM4_SSE2(uint8_t* dst) { TrueMotion_SSE2(dst, 4); } +static void TM8uv_SSE2(uint8_t* dst) { TrueMotion_SSE2(dst, 8); } +static void TM16_SSE2(uint8_t* dst) { TrueMotion_SSE2(dst, 16); } + +static void VE16_SSE2(uint8_t* dst) { + const __m128i top = _mm_loadu_si128((const __m128i*)(dst - BPS)); + int j; + for (j = 0; j < 16; ++j) { + _mm_storeu_si128((__m128i*)(dst + j * BPS), top); + } +} + +static void HE16_SSE2(uint8_t* dst) { // horizontal + int j; + for (j = 16; j > 0; --j) { + const __m128i values = _mm_set1_epi8(dst[-1]); + _mm_storeu_si128((__m128i*)dst, values); + dst += BPS; + } +} + +static WEBP_INLINE void Put16_SSE2(uint8_t v, uint8_t* dst) { + int j; + const __m128i values = _mm_set1_epi8(v); + for (j = 0; j < 16; ++j) { + _mm_storeu_si128((__m128i*)(dst + j * BPS), values); + } +} + +static void DC16_SSE2(uint8_t* dst) { // DC + const __m128i zero = _mm_setzero_si128(); + const __m128i top = _mm_loadu_si128((const __m128i*)(dst - BPS)); + const __m128i sad8x2 = _mm_sad_epu8(top, zero); + // sum the two sads: sad8x2[0:1] + sad8x2[8:9] + const __m128i sum = _mm_add_epi16(sad8x2, _mm_shuffle_epi32(sad8x2, 2)); + int left = 0; + int j; + for (j = 0; j < 16; ++j) { + left += dst[-1 + j * BPS]; + } + { + const int DC = _mm_cvtsi128_si32(sum) + left + 16; + Put16_SSE2(DC >> 5, dst); + } +} + +static void DC16NoTop_SSE2(uint8_t* dst) { // DC with top samples unavailable + int DC = 8; + int j; + for (j = 0; j < 16; ++j) { + DC += dst[-1 + j * BPS]; + } + Put16_SSE2(DC >> 4, dst); +} + +static void DC16NoLeft_SSE2(uint8_t* dst) { // DC with left samples unavailable + const __m128i zero = _mm_setzero_si128(); + const __m128i top = _mm_loadu_si128((const __m128i*)(dst - BPS)); + const __m128i sad8x2 = _mm_sad_epu8(top, zero); + // sum the two sads: sad8x2[0:1] + sad8x2[8:9] + const __m128i sum = _mm_add_epi16(sad8x2, _mm_shuffle_epi32(sad8x2, 2)); + const int DC = _mm_cvtsi128_si32(sum) + 8; + Put16_SSE2(DC >> 4, dst); +} + +static void DC16NoTopLeft_SSE2(uint8_t* dst) { // DC with no top & left samples + Put16_SSE2(0x80, dst); +} + +//------------------------------------------------------------------------------ +// Chroma + +static void VE8uv_SSE2(uint8_t* dst) { // vertical + int j; + const __m128i top = _mm_loadl_epi64((const __m128i*)(dst - BPS)); + for (j = 0; j < 8; ++j) { + _mm_storel_epi64((__m128i*)(dst + j * BPS), top); + } +} + +// helper for chroma-DC predictions +static WEBP_INLINE void Put8x8uv_SSE2(uint8_t v, uint8_t* dst) { + int j; + const __m128i values = _mm_set1_epi8(v); + for (j = 0; j < 8; ++j) { + _mm_storel_epi64((__m128i*)(dst + j * BPS), values); + } +} + +static void DC8uv_SSE2(uint8_t* dst) { // DC + const __m128i zero = _mm_setzero_si128(); + const __m128i top = _mm_loadl_epi64((const __m128i*)(dst - BPS)); + const __m128i sum = _mm_sad_epu8(top, zero); + int left = 0; + int j; + for (j = 0; j < 8; ++j) { + left += dst[-1 + j * BPS]; + } + { + const int DC = _mm_cvtsi128_si32(sum) + left + 8; + Put8x8uv_SSE2(DC >> 4, dst); + } +} + +static void DC8uvNoLeft_SSE2(uint8_t* dst) { // DC with no left samples + const __m128i zero = _mm_setzero_si128(); + const __m128i top = _mm_loadl_epi64((const __m128i*)(dst - BPS)); + const __m128i sum = _mm_sad_epu8(top, zero); + const int DC = _mm_cvtsi128_si32(sum) + 4; + Put8x8uv_SSE2(DC >> 3, dst); +} + +static void DC8uvNoTop_SSE2(uint8_t* dst) { // DC with no top samples + int dc0 = 4; + int i; + for (i = 0; i < 8; ++i) { + dc0 += dst[-1 + i * BPS]; + } + Put8x8uv_SSE2(dc0 >> 3, dst); +} + +static void DC8uvNoTopLeft_SSE2(uint8_t* dst) { // DC with nothing + Put8x8uv_SSE2(0x80, dst); +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8DspInitSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitSSE2(void) { + VP8Transform = Transform_SSE2; +#if (USE_TRANSFORM_AC3 == 1) + VP8TransformAC3 = TransformAC3_SSE2; +#endif + + VP8VFilter16 = VFilter16_SSE2; + VP8HFilter16 = HFilter16_SSE2; + VP8VFilter8 = VFilter8_SSE2; + VP8HFilter8 = HFilter8_SSE2; + VP8VFilter16i = VFilter16i_SSE2; + VP8HFilter16i = HFilter16i_SSE2; + VP8VFilter8i = VFilter8i_SSE2; + VP8HFilter8i = HFilter8i_SSE2; + + VP8SimpleVFilter16 = SimpleVFilter16_SSE2; + VP8SimpleHFilter16 = SimpleHFilter16_SSE2; + VP8SimpleVFilter16i = SimpleVFilter16i_SSE2; + VP8SimpleHFilter16i = SimpleHFilter16i_SSE2; + + VP8PredLuma4[1] = TM4_SSE2; + VP8PredLuma4[2] = VE4_SSE2; + VP8PredLuma4[4] = RD4_SSE2; + VP8PredLuma4[5] = VR4_SSE2; + VP8PredLuma4[6] = LD4_SSE2; + VP8PredLuma4[7] = VL4_SSE2; + + VP8PredLuma16[0] = DC16_SSE2; + VP8PredLuma16[1] = TM16_SSE2; + VP8PredLuma16[2] = VE16_SSE2; + VP8PredLuma16[3] = HE16_SSE2; + VP8PredLuma16[4] = DC16NoTop_SSE2; + VP8PredLuma16[5] = DC16NoLeft_SSE2; + VP8PredLuma16[6] = DC16NoTopLeft_SSE2; + + VP8PredChroma8[0] = DC8uv_SSE2; + VP8PredChroma8[1] = TM8uv_SSE2; + VP8PredChroma8[2] = VE8uv_SSE2; + VP8PredChroma8[4] = DC8uvNoTop_SSE2; + VP8PredChroma8[5] = DC8uvNoLeft_SSE2; + VP8PredChroma8[6] = DC8uvNoTopLeft_SSE2; +} + +#else // !WEBP_USE_SSE2 + +WEBP_DSP_INIT_STUB(VP8DspInitSSE2) + +#endif // WEBP_USE_SSE2 diff --git a/internal/libwebp-1.2.1/src/dsp/dec_sse41.c b/internal/libwebp-1.2.1/src/dsp/dec_sse41.c new file mode 100644 index 0000000..8f18506 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/dec_sse41.c @@ -0,0 +1,46 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE4 version of some decoding functions. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE41) + +#include +#include "src/dec/vp8i_dec.h" +#include "src/utils/utils.h" + +static void HE16_SSE41(uint8_t* dst) { // horizontal + int j; + const __m128i kShuffle3 = _mm_set1_epi8(3); + for (j = 16; j > 0; --j) { + const __m128i in = _mm_cvtsi32_si128(WebPMemToUint32(dst - 4)); + const __m128i values = _mm_shuffle_epi8(in, kShuffle3); + _mm_storeu_si128((__m128i*)dst, values); + dst += BPS; + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8DspInitSSE41(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitSSE41(void) { + VP8PredLuma16[3] = HE16_SSE41; +} + +#else // !WEBP_USE_SSE41 + +WEBP_DSP_INIT_STUB(VP8DspInitSSE41) + +#endif // WEBP_USE_SSE41 diff --git a/internal/libwebp-1.2.1/src/dsp/dsp.h b/internal/libwebp-1.2.1/src/dsp/dsp.h new file mode 100644 index 0000000..c4f57e4 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/dsp.h @@ -0,0 +1,731 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Speed-critical functions. +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_DSP_DSP_H_ +#define WEBP_DSP_DSP_H_ + +#ifdef HAVE_CONFIG_H +#include "src/webp/config.h" +#endif + +#include "src/webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BPS 32 // this is the common stride for enc/dec + +//------------------------------------------------------------------------------ +// WEBP_RESTRICT + +// Declares a pointer with the restrict type qualifier if available. +// This allows code to hint to the compiler that only this pointer references a +// particular object or memory region within the scope of the block in which it +// is declared. This may allow for improved optimizations due to the lack of +// pointer aliasing. See also: +// https://en.cppreference.com/w/c/language/restrict +#if defined(__GNUC__) +#define WEBP_RESTRICT __restrict__ +#elif defined(_MSC_VER) +#define WEBP_RESTRICT __restrict +#else +#define WEBP_RESTRICT +#endif + +//------------------------------------------------------------------------------ +// CPU detection + +#if defined(__GNUC__) +# define LOCAL_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__) +# define LOCAL_GCC_PREREQ(maj, min) \ + (LOCAL_GCC_VERSION >= (((maj) << 8) | (min))) +#else +# define LOCAL_GCC_VERSION 0 +# define LOCAL_GCC_PREREQ(maj, min) 0 +#endif + +#if defined(__clang__) +# define LOCAL_CLANG_VERSION ((__clang_major__ << 8) | __clang_minor__) +# define LOCAL_CLANG_PREREQ(maj, min) \ + (LOCAL_CLANG_VERSION >= (((maj) << 8) | (min))) +#else +# define LOCAL_CLANG_VERSION 0 +# define LOCAL_CLANG_PREREQ(maj, min) 0 +#endif + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#if !defined(HAVE_CONFIG_H) +#if defined(_MSC_VER) && _MSC_VER > 1310 && \ + (defined(_M_X64) || defined(_M_IX86)) +#define WEBP_MSC_SSE2 // Visual C++ SSE2 targets +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1500 && \ + (defined(_M_X64) || defined(_M_IX86)) +#define WEBP_MSC_SSE41 // Visual C++ SSE4.1 targets +#endif +#endif + +// WEBP_HAVE_* are used to indicate the presence of the instruction set in dsp +// files without intrinsics, allowing the corresponding Init() to be called. +// Files containing intrinsics will need to be built targeting the instruction +// set so should succeed on one of the earlier tests. +#if (defined(__SSE2__) || defined(WEBP_MSC_SSE2)) && \ + (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE2)) +#define WEBP_USE_SSE2 +#endif + +#if defined(WEBP_USE_SSE2) && !defined(WEBP_HAVE_SSE2) +#define WEBP_HAVE_SSE2 +#endif + +#if (defined(__SSE4_1__) || defined(WEBP_MSC_SSE41)) && \ + (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE41)) +#define WEBP_USE_SSE41 +#endif + +#if defined(WEBP_USE_SSE41) && !defined(WEBP_HAVE_SSE41) +#define WEBP_HAVE_SSE41 +#endif + +#undef WEBP_MSC_SSE41 +#undef WEBP_MSC_SSE2 + +// The intrinsics currently cause compiler errors with arm-nacl-gcc and the +// inline assembly would need to be modified for use with Native Client. +#if ((defined(__ARM_NEON__) || defined(__aarch64__)) && \ + (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_NEON))) && \ + !defined(__native_client__) +#define WEBP_USE_NEON +#endif + +#if !defined(WEBP_USE_NEON) && defined(__ANDROID__) && \ + defined(__ARM_ARCH_7A__) && defined(HAVE_CPU_FEATURES_H) +#define WEBP_ANDROID_NEON // Android targets that may have NEON +#define WEBP_USE_NEON +#endif + +// Note: ARM64 is supported in Visual Studio 2017, but requires the direct +// inclusion of arm64_neon.h; Visual Studio 2019 includes this file in +// arm_neon.h. +#if defined(_MSC_VER) && \ + ((_MSC_VER >= 1700 && defined(_M_ARM)) || \ + (_MSC_VER >= 1920 && defined(_M_ARM64))) +#define WEBP_USE_NEON +#define WEBP_USE_INTRINSICS +#endif + +#if defined(WEBP_USE_NEON) && !defined(WEBP_HAVE_NEON) +#define WEBP_HAVE_NEON +#endif + +#if defined(__mips__) && !defined(__mips64) && \ + defined(__mips_isa_rev) && (__mips_isa_rev >= 1) && (__mips_isa_rev < 6) +#define WEBP_USE_MIPS32 +#if (__mips_isa_rev >= 2) +#define WEBP_USE_MIPS32_R2 +#if defined(__mips_dspr2) || (defined(__mips_dsp_rev) && __mips_dsp_rev >= 2) +#define WEBP_USE_MIPS_DSP_R2 +#endif +#endif +#endif + +#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5) +#define WEBP_USE_MSA +#endif + +#ifndef WEBP_DSP_OMIT_C_CODE +#define WEBP_DSP_OMIT_C_CODE 1 +#endif + +#if defined(WEBP_USE_NEON) && WEBP_DSP_OMIT_C_CODE +#define WEBP_NEON_OMIT_C_CODE 1 +#else +#define WEBP_NEON_OMIT_C_CODE 0 +#endif + +#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__)) +#define WEBP_NEON_WORK_AROUND_GCC 1 +#else +#define WEBP_NEON_WORK_AROUND_GCC 0 +#endif + +// This macro prevents thread_sanitizer from reporting known concurrent writes. +#define WEBP_TSAN_IGNORE_FUNCTION +#if defined(__has_feature) +#if __has_feature(thread_sanitizer) +#undef WEBP_TSAN_IGNORE_FUNCTION +#define WEBP_TSAN_IGNORE_FUNCTION __attribute__((no_sanitize_thread)) +#endif +#endif + +#if defined(WEBP_USE_THREAD) && !defined(_WIN32) +#include // NOLINT + +#define WEBP_DSP_INIT(func) do { \ + static volatile VP8CPUInfo func ## _last_cpuinfo_used = \ + (VP8CPUInfo)&func ## _last_cpuinfo_used; \ + static pthread_mutex_t func ## _lock = PTHREAD_MUTEX_INITIALIZER; \ + if (pthread_mutex_lock(&func ## _lock)) break; \ + if (func ## _last_cpuinfo_used != VP8GetCPUInfo) func(); \ + func ## _last_cpuinfo_used = VP8GetCPUInfo; \ + (void)pthread_mutex_unlock(&func ## _lock); \ +} while (0) +#else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32)) +#define WEBP_DSP_INIT(func) do { \ + static volatile VP8CPUInfo func ## _last_cpuinfo_used = \ + (VP8CPUInfo)&func ## _last_cpuinfo_used; \ + if (func ## _last_cpuinfo_used == VP8GetCPUInfo) break; \ + func(); \ + func ## _last_cpuinfo_used = VP8GetCPUInfo; \ +} while (0) +#endif // defined(WEBP_USE_THREAD) && !defined(_WIN32) + +// Defines an Init + helper function that control multiple initialization of +// function pointers / tables. +/* Usage: + WEBP_DSP_INIT_FUNC(InitFunc) { + ...function body + } +*/ +#define WEBP_DSP_INIT_FUNC(name) \ + static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void); \ + WEBP_TSAN_IGNORE_FUNCTION void name(void) { \ + WEBP_DSP_INIT(name ## _body); \ + } \ + static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void) + +#define WEBP_UBSAN_IGNORE_UNDEF +#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW +#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 +// are known to support them. +#undef WEBP_UBSAN_IGNORE_UNDEF +#define WEBP_UBSAN_IGNORE_UNDEF \ + __attribute__((no_sanitize("undefined"))) + +// This macro prevents the undefined behavior sanitizer from reporting +// failures related to unsigned integer overflows. This is only meant to +// silence cases where this well defined behavior is expected. +#undef WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW +#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW \ + __attribute__((no_sanitize("unsigned-integer-overflow"))) +#endif +#endif + +// If 'ptr' is NULL, returns NULL. Otherwise returns 'ptr + off'. +// Prevents undefined behavior sanitizer nullptr-with-nonzero-offset warning. +#if !defined(WEBP_OFFSET_PTR) +#define WEBP_OFFSET_PTR(ptr, off) (((ptr) == NULL) ? NULL : ((ptr) + (off))) +#endif + +// Regularize the definition of WEBP_SWAP_16BIT_CSP (backward compatibility) +#if !defined(WEBP_SWAP_16BIT_CSP) +#define WEBP_SWAP_16BIT_CSP 0 +#endif + +// 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 + +typedef enum { + kSSE2, + kSSE3, + kSlowSSSE3, // special feature for slow SSSE3 architectures + kSSE4_1, + kAVX, + kAVX2, + kNEON, + kMIPS32, + kMIPSdspR2, + kMSA +} CPUFeature; +// returns true if the CPU supports the feature. +typedef int (*VP8CPUInfo)(CPUFeature feature); +WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo; + +//------------------------------------------------------------------------------ +// Init stub generator + +// Defines an init function stub to ensure each module exposes a symbol, +// avoiding a compiler warning. +#define WEBP_DSP_INIT_STUB(func) \ + extern void func(void); \ + void func(void) {} + +//------------------------------------------------------------------------------ +// Encoding + +// Transforms +// VP8Idct: Does one of two inverse transforms. If do_two is set, the transforms +// will be done for (ref, in, dst) and (ref + 4, in + 16, dst + 4). +typedef void (*VP8Idct)(const uint8_t* ref, const int16_t* in, uint8_t* dst, + int do_two); +typedef void (*VP8Fdct)(const uint8_t* src, const uint8_t* ref, int16_t* out); +typedef void (*VP8WHT)(const int16_t* in, int16_t* out); +extern VP8Idct VP8ITransform; +extern VP8Fdct VP8FTransform; +extern VP8Fdct VP8FTransform2; // performs two transforms at a time +extern VP8WHT VP8FTransformWHT; +// Predictions +// *dst is the destination block. *top and *left can be NULL. +typedef void (*VP8IntraPreds)(uint8_t* dst, const uint8_t* left, + const uint8_t* top); +typedef void (*VP8Intra4Preds)(uint8_t* dst, const uint8_t* top); +extern VP8Intra4Preds VP8EncPredLuma4; +extern VP8IntraPreds VP8EncPredLuma16; +extern VP8IntraPreds VP8EncPredChroma8; + +typedef int (*VP8Metric)(const uint8_t* pix, const uint8_t* ref); +extern VP8Metric VP8SSE16x16, VP8SSE16x8, VP8SSE8x8, VP8SSE4x4; +typedef int (*VP8WMetric)(const uint8_t* pix, const uint8_t* ref, + const uint16_t* const weights); +// The weights for VP8TDisto4x4 and VP8TDisto16x16 contain a row-major +// 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; +// Quantization +struct VP8Matrix; // forward declaration +typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16], + const struct VP8Matrix* const mtx); +// Same as VP8QuantizeBlock, but quantizes two consecutive blocks. +typedef int (*VP8Quantize2Blocks)(int16_t in[32], int16_t out[32], + const struct VP8Matrix* const mtx); + +extern VP8QuantizeBlock VP8EncQuantizeBlock; +extern VP8Quantize2Blocks VP8EncQuantize2Blocks; + +// specific to 2nd transform: +typedef int (*VP8QuantizeBlockWHT)(int16_t in[16], int16_t out[16], + const struct VP8Matrix* const mtx); +extern VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT; + +extern const int VP8DspScan[16 + 4 + 4]; + +// Collect histogram for susceptibility calculation. +#define MAX_COEFF_THRESH 31 // size of histogram used by CollectHistogram. +typedef struct { + // We only need to store max_value and last_non_zero, not the distribution. + int max_value; + int last_non_zero; +} VP8Histogram; +typedef void (*VP8CHisto)(const uint8_t* ref, const uint8_t* pred, + int start_block, int end_block, + VP8Histogram* const histo); +extern VP8CHisto VP8CollectHistogram; +// General-purpose util function to help VP8CollectHistogram(). +void VP8SetHistogramData(const int distribution[MAX_COEFF_THRESH + 1], + VP8Histogram* const histo); + +// must be called before using any of the above +void VP8EncDspInit(void); + +//------------------------------------------------------------------------------ +// cost functions (encoding) + +extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p) +// approximate cost per level: +extern const uint16_t VP8LevelFixedCosts[2047 /*MAX_LEVEL*/ + 1]; +extern const uint8_t VP8EncBands[16 + 1]; + +struct VP8Residual; +typedef void (*VP8SetResidualCoeffsFunc)(const int16_t* const coeffs, + struct VP8Residual* const res); +extern VP8SetResidualCoeffsFunc VP8SetResidualCoeffs; + +// Cost calculation function. +typedef int (*VP8GetResidualCostFunc)(int ctx0, + const struct VP8Residual* const res); +extern VP8GetResidualCostFunc VP8GetResidualCost; + +// must be called before anything using the above +void VP8EncDspCostInit(void); + +//------------------------------------------------------------------------------ +// SSIM / PSNR utils + +// struct for accumulating statistical moments +typedef struct { + 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 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 + +#if !defined(WEBP_REDUCE_SIZE) +// This version is called with the guarantee that you can load 8 bytes and +// 8 rows at offset src1 and src2 +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 +#endif + +#if !defined(WEBP_DISABLE_STATS) +typedef uint32_t (*VP8AccumulateSSEFunc)(const uint8_t* src1, + const uint8_t* src2, int len); +extern VP8AccumulateSSEFunc VP8AccumulateSSE; +#endif + +// must be called before using any of the above directly +void VP8SSIMDspInit(void); + +//------------------------------------------------------------------------------ +// Decoding + +typedef void (*VP8DecIdct)(const int16_t* coeffs, uint8_t* dst); +// when doing two transforms, coeffs is actually int16_t[2][16]. +typedef void (*VP8DecIdct2)(const int16_t* coeffs, uint8_t* dst, int do_two); +extern VP8DecIdct2 VP8Transform; +extern VP8DecIdct VP8TransformAC3; +extern VP8DecIdct VP8TransformUV; +extern VP8DecIdct VP8TransformDC; +extern VP8DecIdct VP8TransformDCUV; +extern VP8WHT VP8TransformWHT; + +// *dst is the destination block, with stride BPS. Boundary samples are +// assumed accessible when needed. +typedef void (*VP8PredFunc)(uint8_t* dst); +extern VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */]; +extern VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */]; +extern VP8PredFunc VP8PredLuma4[/* NUM_BMODES */]; + +// clipping tables (for filtering) +extern const int8_t* const VP8ksclip1; // clips [-1020, 1020] to [-128, 127] +extern const int8_t* const VP8ksclip2; // clips [-112, 112] to [-16, 15] +extern const uint8_t* const VP8kclip1; // clips [-255,511] to [0,255] +extern const uint8_t* const VP8kabs0; // abs(x) for x in [-255,255] +// must be called first +void VP8InitClipTables(void); + +// simple filter (only for luma) +typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh); +extern VP8SimpleFilterFunc VP8SimpleVFilter16; +extern VP8SimpleFilterFunc VP8SimpleHFilter16; +extern VP8SimpleFilterFunc VP8SimpleVFilter16i; // filter 3 inner edges +extern VP8SimpleFilterFunc VP8SimpleHFilter16i; + +// regular filter (on both macroblock edges and inner edges) +typedef void (*VP8LumaFilterFunc)(uint8_t* luma, int stride, + int thresh, int ithresh, int hev_t); +typedef void (*VP8ChromaFilterFunc)(uint8_t* u, uint8_t* v, int stride, + int thresh, int ithresh, int hev_t); +// on outer edge +extern VP8LumaFilterFunc VP8VFilter16; +extern VP8LumaFilterFunc VP8HFilter16; +extern VP8ChromaFilterFunc VP8VFilter8; +extern VP8ChromaFilterFunc VP8HFilter8; + +// on inner edge +extern VP8LumaFilterFunc VP8VFilter16i; // filtering 3 inner edges altogether +extern VP8LumaFilterFunc VP8HFilter16i; +extern VP8ChromaFilterFunc VP8VFilter8i; // filtering u and v altogether +extern VP8ChromaFilterFunc VP8HFilter8i; + +// Dithering. Combines dithering values (centered around 128) with dst[], +// according to: dst[] = clip(dst[] + (((dither[]-128) + 8) >> 4) +#define VP8_DITHER_DESCALE 4 +#define VP8_DITHER_DESCALE_ROUNDER (1 << (VP8_DITHER_DESCALE - 1)) +#define VP8_DITHER_AMP_BITS 7 +#define VP8_DITHER_AMP_CENTER (1 << VP8_DITHER_AMP_BITS) +extern void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst, + int dst_stride); + +// must be called before anything using the above +void VP8DspInit(void); + +//------------------------------------------------------------------------------ +// WebP I/O + +#define FANCY_UPSAMPLING // undefined to remove fancy upsampling support + +// Convert a pair of y/u/v lines together to the output rgb/a colorspace. +// bottom_y can be NULL if only one line of output is needed (at top/bottom). +typedef void (*WebPUpsampleLinePairFunc)( + const uint8_t* top_y, const uint8_t* bottom_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* bottom_dst, int len); + +#ifdef FANCY_UPSAMPLING + +// Fancy upsampling functions to convert YUV to RGB(A) modes +extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; + +#endif // FANCY_UPSAMPLING + +// Per-row point-sampling methods. +typedef void (*WebPSamplerRowFunc)(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len); +// Generic function to apply 'WebPSamplerRowFunc' to the whole plane: +void WebPSamplerProcessPlane(const uint8_t* y, int y_stride, + const uint8_t* u, const uint8_t* v, int uv_stride, + uint8_t* dst, int dst_stride, + int width, int height, WebPSamplerRowFunc func); + +// Sampling functions to convert rows of YUV to RGB(A) +extern WebPSamplerRowFunc WebPSamplers[/* MODE_LAST */]; + +// General function for converting two lines of ARGB or RGBA. +// 'alpha_is_last' should be true if 0xff000000 is stored in memory as +// as 0x00, 0x00, 0x00, 0xff (little endian). +WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last); + +// YUV444->RGB converters +typedef void (*WebPYUV444Converter)(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len); + +extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */]; + +// Must be called before using the WebPUpsamplers[] (and for premultiplied +// colorspaces like rgbA, rgbA4444, etc) +void WebPInitUpsamplers(void); +// Must be called before using WebPSamplers[] +void WebPInitSamplers(void); +// Must be called before using WebPYUV444Converters[] +void WebPInitYUV444Converters(void); + +//------------------------------------------------------------------------------ +// ARGB -> YUV converters + +// Convert ARGB samples to luma Y. +extern void (*WebPConvertARGBToY)(const uint32_t* argb, uint8_t* y, int width); +// Convert ARGB samples to U/V with downsampling. do_store should be '1' for +// even lines and '0' for odd ones. 'src_width' is the original width, not +// the U/V one. +extern void (*WebPConvertARGBToUV)(const uint32_t* argb, uint8_t* u, uint8_t* v, + int src_width, int do_store); + +// Convert a row of accumulated (four-values) of rgba32 toward U/V +extern void (*WebPConvertRGBA32ToUV)(const uint16_t* rgb, + uint8_t* u, uint8_t* v, int width); + +// Convert RGB or BGR to Y +extern void (*WebPConvertRGB24ToY)(const uint8_t* rgb, uint8_t* y, int width); +extern void (*WebPConvertBGR24ToY)(const uint8_t* bgr, uint8_t* y, int width); + +// used for plain-C fallback. +extern void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v, + int src_width, int do_store); +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); + +//------------------------------------------------------------------------------ +// Rescaler + +struct WebPRescaler; + +// Import a row of data and save its contribution in the rescaler. +// 'channel' denotes the channel number to be imported. 'Expand' corresponds to +// the wrk->x_expand case. Otherwise, 'Shrink' is to be used. +typedef void (*WebPRescalerImportRowFunc)(struct WebPRescaler* const wrk, + const uint8_t* src); + +extern WebPRescalerImportRowFunc WebPRescalerImportRowExpand; +extern WebPRescalerImportRowFunc WebPRescalerImportRowShrink; + +// Export one row (starting at x_out position) from rescaler. +// 'Expand' corresponds to the wrk->y_expand case. +// Otherwise 'Shrink' is to be used +typedef void (*WebPRescalerExportRowFunc)(struct WebPRescaler* const wrk); +extern WebPRescalerExportRowFunc WebPRescalerExportRowExpand; +extern WebPRescalerExportRowFunc WebPRescalerExportRowShrink; + +// Plain-C implementation, as fall-back. +extern void WebPRescalerImportRowExpand_C(struct WebPRescaler* const wrk, + const uint8_t* src); +extern void WebPRescalerImportRowShrink_C(struct WebPRescaler* const wrk, + const uint8_t* src); +extern void WebPRescalerExportRowExpand_C(struct WebPRescaler* const wrk); +extern void WebPRescalerExportRowShrink_C(struct WebPRescaler* const wrk); + +// Main entry calls: +extern void WebPRescalerImportRow(struct WebPRescaler* const wrk, + const uint8_t* src); +// Export one row (starting at x_out position) from rescaler. +extern void WebPRescalerExportRow(struct WebPRescaler* const wrk); + +// Must be called first before using the above. +void WebPRescalerDspInit(void); + +//------------------------------------------------------------------------------ +// Utilities for processing transparent channel. + +// Apply alpha pre-multiply on an rgba, bgra or argb plane of size w * h. +// alpha_first should be 0 for argb, 1 for rgba or bgra (where alpha is last). +extern void (*WebPApplyAlphaMultiply)( + uint8_t* rgba, int alpha_first, int w, int h, int stride); + +// Same, buf specifically for RGBA4444 format +extern void (*WebPApplyAlphaMultiply4444)( + uint8_t* rgba4444, int w, int h, int stride); + +// Dispatch the values from alpha[] plane to the ARGB destination 'dst'. +// Returns true if alpha[] plane has non-trivial values different from 0xff. +extern int (*WebPDispatchAlpha)(const uint8_t* WEBP_RESTRICT alpha, + int alpha_stride, int width, int height, + uint8_t* WEBP_RESTRICT dst, int dst_stride); + +// Transfer packed 8b alpha[] values to green channel in dst[], zero'ing the +// A/R/B values. 'dst_stride' is the stride for dst[] in uint32_t units. +extern void (*WebPDispatchAlphaToGreen)(const uint8_t* WEBP_RESTRICT alpha, + int alpha_stride, int width, int height, + uint32_t* WEBP_RESTRICT dst, + int dst_stride); + +// Extract the alpha values from 32b values in argb[] and pack them into alpha[] +// (this is the opposite of WebPDispatchAlpha). +// Returns true if there's only trivial 0xff alpha values. +extern int (*WebPExtractAlpha)(const uint8_t* WEBP_RESTRICT argb, + int argb_stride, int width, int height, + uint8_t* WEBP_RESTRICT 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* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT 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. + +// Pre-Multiply or Un-Multiply (if 'inverse' is true) argb values in a row. +extern void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse); + +// Same a WebPMultARGBRow(), but for several rows. +void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows, + int inverse); + +// Same for a row of single values, with side alpha values. +extern void (*WebPMultRow)(uint8_t* WEBP_RESTRICT const ptr, + const uint8_t* WEBP_RESTRICT const alpha, + int width, int inverse); + +// Same a WebPMultRow(), but for several 'num_rows' rows. +void WebPMultRows(uint8_t* WEBP_RESTRICT ptr, int stride, + const uint8_t* WEBP_RESTRICT alpha, int alpha_stride, + int width, int num_rows, int inverse); + +// Plain-C versions, used as fallback by some implementations. +void WebPMultRow_C(uint8_t* WEBP_RESTRICT const ptr, + const uint8_t* WEBP_RESTRICT const alpha, + int width, int inverse); +void WebPMultARGBRow_C(uint32_t* const ptr, int width, int inverse); + +#ifdef WORDS_BIGENDIAN +// ARGB packing function: a/r/g/b input is rgba or bgra order. +extern void (*WebPPackARGB)(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT r, + const uint8_t* WEBP_RESTRICT g, + const uint8_t* WEBP_RESTRICT b, + int len, uint32_t* WEBP_RESTRICT out); +#endif + +// RGB packing function. 'step' can be 3 or 4. r/g/b input is rgb or bgr order. +extern void (*WebPPackRGB)(const uint8_t* WEBP_RESTRICT r, + const uint8_t* WEBP_RESTRICT g, + const uint8_t* WEBP_RESTRICT b, + int len, int step, uint32_t* WEBP_RESTRICT out); + +// This function returns true if src[i] contains a value different from 0xff. +extern int (*WebPHasAlpha8b)(const uint8_t* src, int length); +// This function returns true if src[4*i] contains a value different from 0xff. +extern int (*WebPHasAlpha32b)(const uint8_t* src, int length); +// replaces transparent values in src[] by 'color'. +extern void (*WebPAlphaReplace)(uint32_t* src, int length, uint32_t color); + +// To be called first before using the above. +void WebPInitAlphaProcessing(void); + +//------------------------------------------------------------------------------ +// Filter functions + +typedef enum { // Filter types. + WEBP_FILTER_NONE = 0, + WEBP_FILTER_HORIZONTAL, + WEBP_FILTER_VERTICAL, + WEBP_FILTER_GRADIENT, + WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker + WEBP_FILTER_BEST, // meta-types + WEBP_FILTER_FAST +} WEBP_FILTER_TYPE; + +typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height, + int stride, uint8_t* out); +// In-place un-filtering. +// Warning! 'prev_line' pointer can be equal to 'cur_line' or 'preds'. +typedef void (*WebPUnfilterFunc)(const uint8_t* prev_line, const uint8_t* preds, + uint8_t* cur_line, int width); + +// Filter the given data using the given predictor. +// 'in' corresponds to a 2-dimensional pixel array of size (stride * height) +// in raster order. +// 'stride' is number of bytes per scan line (with possible padding). +// 'out' should be pre-allocated. +extern WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; + +// In-place reconstruct the original data from the given filtered data. +// The reconstruction will be done for 'num_rows' rows starting from 'row' +// (assuming rows upto 'row - 1' are already reconstructed). +extern WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST]; + +// To be called first before using the above. +void VP8FiltersInit(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_DSP_DSP_H_ diff --git a/internal/libwebp-1.2.1/src/dsp/enc.c b/internal/libwebp-1.2.1/src/dsp/enc.c new file mode 100644 index 0000000..ea47a3f --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/enc.c @@ -0,0 +1,830 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Speed-critical encoding functions. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include // for abs() + +#include "src/dsp/dsp.h" +#include "src/enc/vp8i_enc.h" + +static WEBP_INLINE uint8_t clip_8b(int v) { + return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255; +} + +#if !WEBP_NEON_OMIT_C_CODE +static WEBP_INLINE int clip_max(int v, int max) { + return (v > max) ? max : v; +} +#endif // !WEBP_NEON_OMIT_C_CODE + +//------------------------------------------------------------------------------ +// Compute susceptibility based on DCT-coeff histograms: +// the higher, the "easier" the macroblock is to compress. + +const int VP8DspScan[16 + 4 + 4] = { + // 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, + + 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 +}; + +// general-purpose util function +void VP8SetHistogramData(const int distribution[MAX_COEFF_THRESH + 1], + VP8Histogram* const histo) { + int max_value = 0, last_non_zero = 1; + int k; + for (k = 0; k <= MAX_COEFF_THRESH; ++k) { + const int value = distribution[k]; + if (value > 0) { + if (value > max_value) max_value = value; + last_non_zero = k; + } + } + histo->max_value = max_value; + histo->last_non_zero = last_non_zero; +} + +#if !WEBP_NEON_OMIT_C_CODE +static void CollectHistogram_C(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) { + int k; + int16_t out[16]; + + VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out); + + // Convert coefficients to bin. + for (k = 0; k < 16; ++k) { + const int v = abs(out[k]) >> 3; + const int clipped_value = clip_max(v, MAX_COEFF_THRESH); + ++distribution[clipped_value]; + } + } + VP8SetHistogramData(distribution, histo); +} +#endif // !WEBP_NEON_OMIT_C_CODE + +//------------------------------------------------------------------------------ +// run-time tables (~4k) + +static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255] + +// We declare this variable 'volatile' to prevent instruction reordering +// and make sure it's set to true _last_ (so as to be thread-safe) +static volatile int tables_ok = 0; + +static WEBP_TSAN_IGNORE_FUNCTION void InitTables(void) { + if (!tables_ok) { + int i; + for (i = -255; i <= 255 + 255; ++i) { + clip1[255 + i] = clip_8b(i); + } + tables_ok = 1; + } +} + + +//------------------------------------------------------------------------------ +// Transforms (Paragraph 14.4) + +#if !WEBP_NEON_OMIT_C_CODE + +#define STORE(x, y, v) \ + dst[(x) + (y) * BPS] = clip_8b(ref[(x) + (y) * BPS] + ((v) >> 3)) + +static const int kC1 = 20091 + (1 << 16); +static const int kC2 = 35468; +#define MUL(a, b) (((a) * (b)) >> 16) + +static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, + uint8_t* dst) { + int C[4 * 4], *tmp; + int i; + tmp = C; + for (i = 0; i < 4; ++i) { // vertical pass + const int a = in[0] + in[8]; + const int b = in[0] - in[8]; + const int c = MUL(in[4], kC2) - MUL(in[12], kC1); + const int d = MUL(in[4], kC1) + MUL(in[12], kC2); + tmp[0] = a + d; + tmp[1] = b + c; + tmp[2] = b - c; + tmp[3] = a - d; + tmp += 4; + in++; + } + + tmp = C; + for (i = 0; i < 4; ++i) { // horizontal pass + const int dc = tmp[0] + 4; + const int a = dc + tmp[8]; + const int b = dc - tmp[8]; + const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1); + const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2); + STORE(0, i, a + d); + STORE(1, i, b + c); + STORE(2, i, b - c); + STORE(3, i, a - d); + tmp++; + } +} + +static void ITransform_C(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_C(const uint8_t* src, const uint8_t* ref, int16_t* out) { + int i; + int tmp[16]; + for (i = 0; i < 4; ++i, src += BPS, ref += BPS) { + const int d0 = src[0] - ref[0]; // 9bit dynamic range ([-255,255]) + const int d1 = src[1] - ref[1]; + const int d2 = src[2] - ref[2]; + const int d3 = src[3] - ref[3]; + const int a0 = (d0 + d3); // 10b [-510,510] + const int a1 = (d1 + d2); + const int a2 = (d1 - d2); + const int a3 = (d0 - d3); + tmp[0 + i * 4] = (a0 + a1) * 8; // 14b [-8160,8160] + tmp[1 + i * 4] = (a2 * 2217 + a3 * 5352 + 1812) >> 9; // [-7536,7542] + tmp[2 + i * 4] = (a0 - a1) * 8; + tmp[3 + i * 4] = (a3 * 2217 - a2 * 5352 + 937) >> 9; + } + for (i = 0; i < 4; ++i) { + const int a0 = (tmp[0 + i] + tmp[12 + i]); // 15b + const int a1 = (tmp[4 + i] + tmp[ 8 + i]); + const int a2 = (tmp[4 + i] - tmp[ 8 + i]); + const int a3 = (tmp[0 + i] - tmp[12 + i]); + out[0 + i] = (a0 + a1 + 7) >> 4; // 12b + out[4 + i] = ((a2 * 2217 + a3 * 5352 + 12000) >> 16) + (a3 != 0); + out[8 + i] = (a0 - a1 + 7) >> 4; + out[12+ i] = ((a3 * 2217 - a2 * 5352 + 51000) >> 16); + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +static void FTransform2_C(const uint8_t* src, const uint8_t* ref, + int16_t* out) { + VP8FTransform(src, ref, out); + VP8FTransform(src + 4, ref + 4, out + 16); +} + +#if !WEBP_NEON_OMIT_C_CODE +static void FTransformWHT_C(const int16_t* in, int16_t* out) { + // input is 12b signed + int32_t tmp[16]; + int i; + for (i = 0; i < 4; ++i, in += 64) { + const int a0 = (in[0 * 16] + in[2 * 16]); // 13b + const int a1 = (in[1 * 16] + in[3 * 16]); + const int a2 = (in[1 * 16] - in[3 * 16]); + const int a3 = (in[0 * 16] - in[2 * 16]); + tmp[0 + i * 4] = a0 + a1; // 14b + tmp[1 + i * 4] = a3 + a2; + tmp[2 + i * 4] = a3 - a2; + tmp[3 + i * 4] = a0 - a1; + } + for (i = 0; i < 4; ++i) { + const int a0 = (tmp[0 + i] + tmp[8 + i]); // 15b + const int a1 = (tmp[4 + i] + tmp[12+ i]); + const int a2 = (tmp[4 + i] - tmp[12+ i]); + const int a3 = (tmp[0 + i] - tmp[8 + i]); + const int b0 = a0 + a1; // 16b + const int b1 = a3 + a2; + const int b2 = a3 - a2; + const int b3 = a0 - a1; + out[ 0 + i] = b0 >> 1; // 15b + out[ 4 + i] = b1 >> 1; + out[ 8 + i] = b2 >> 1; + out[12 + i] = b3 >> 1; + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +#undef MUL +#undef STORE + +//------------------------------------------------------------------------------ +// Intra predictions + +static WEBP_INLINE void Fill(uint8_t* dst, int value, int size) { + int j; + for (j = 0; j < size; ++j) { + memset(dst + j * BPS, value, size); + } +} + +static WEBP_INLINE void VerticalPred(uint8_t* dst, + const uint8_t* top, int size) { + int j; + if (top != NULL) { + for (j = 0; j < size; ++j) memcpy(dst + j * BPS, top, size); + } else { + Fill(dst, 127, size); + } +} + +static WEBP_INLINE void HorizontalPred(uint8_t* dst, + const uint8_t* left, int size) { + if (left != NULL) { + int j; + for (j = 0; j < size; ++j) { + memset(dst + j * BPS, left[j], size); + } + } else { + Fill(dst, 129, size); + } +} + +static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left, + const uint8_t* top, int size) { + int y; + if (left != NULL) { + if (top != NULL) { + const uint8_t* const clip = clip1 + 255 - left[-1]; + for (y = 0; y < size; ++y) { + const uint8_t* const clip_table = clip + left[y]; + int x; + for (x = 0; x < size; ++x) { + dst[x] = clip_table[top[x]]; + } + dst += BPS; + } + } else { + HorizontalPred(dst, left, size); + } + } else { + // true motion without left samples (hence: with default 129 value) + // is equivalent to VE prediction where you just copy the top samples. + // Note that if top samples are not available, the default value is + // then 129, and not 127 as in the VerticalPred case. + if (top != NULL) { + VerticalPred(dst, top, size); + } else { + Fill(dst, 129, size); + } + } +} + +static WEBP_INLINE void DCMode(uint8_t* dst, const uint8_t* left, + const uint8_t* top, + int size, int round, int shift) { + int DC = 0; + int j; + if (top != NULL) { + for (j = 0; j < size; ++j) DC += top[j]; + if (left != NULL) { // top and left present + for (j = 0; j < size; ++j) DC += left[j]; + } else { // top, but no left + DC += DC; + } + DC = (DC + round) >> shift; + } else if (left != NULL) { // left but no top + for (j = 0; j < size; ++j) DC += left[j]; + DC += DC; + DC = (DC + round) >> shift; + } else { // no top, no left, nothing. + DC = 0x80; + } + Fill(dst, DC, size); +} + +//------------------------------------------------------------------------------ +// Chroma 8x8 prediction (paragraph 12.2) + +static void IntraChromaPreds_C(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + // U block + DCMode(C8DC8 + dst, left, top, 8, 8, 4); + VerticalPred(C8VE8 + dst, top, 8); + HorizontalPred(C8HE8 + dst, left, 8); + TrueMotion(C8TM8 + dst, left, top, 8); + // V block + dst += 8; + if (top != NULL) top += 8; + if (left != NULL) left += 16; + DCMode(C8DC8 + dst, left, top, 8, 8, 4); + VerticalPred(C8VE8 + dst, top, 8); + HorizontalPred(C8HE8 + dst, left, 8); + TrueMotion(C8TM8 + dst, left, top, 8); +} + +//------------------------------------------------------------------------------ +// luma 16x16 prediction (paragraph 12.3) + +static void Intra16Preds_C(uint8_t* dst, + const uint8_t* left, const uint8_t* top) { + DCMode(I16DC16 + dst, left, top, 16, 16, 5); + VerticalPred(I16VE16 + dst, top, 16); + HorizontalPred(I16HE16 + dst, left, 16); + TrueMotion(I16TM16 + dst, left, top, 16); +} + +//------------------------------------------------------------------------------ +// luma 4x4 prediction + +#define DST(x, y) dst[(x) + (y) * BPS] +#define AVG3(a, b, c) ((uint8_t)(((a) + 2 * (b) + (c) + 2) >> 2)) +#define AVG2(a, b) (((a) + (b) + 1) >> 1) + +static void VE4(uint8_t* dst, const uint8_t* top) { // vertical + const uint8_t vals[4] = { + AVG3(top[-1], top[0], top[1]), + AVG3(top[ 0], top[1], top[2]), + AVG3(top[ 1], top[2], top[3]), + AVG3(top[ 2], top[3], top[4]) + }; + int i; + for (i = 0; i < 4; ++i) { + memcpy(dst + i * BPS, vals, 4); + } +} + +static 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 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]; + Fill(dst, dc >> 3, 4); +} + +static void RD4(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]; + const int D = top[3]; + DST(0, 3) = AVG3(J, K, L); + DST(0, 2) = DST(1, 3) = AVG3(I, J, K); + DST(0, 1) = DST(1, 2) = DST(2, 3) = AVG3(X, I, J); + DST(0, 0) = DST(1, 1) = DST(2, 2) = DST(3, 3) = AVG3(A, X, I); + DST(1, 0) = DST(2, 1) = DST(3, 2) = AVG3(B, A, X); + DST(2, 0) = DST(3, 1) = AVG3(C, B, A); + DST(3, 0) = AVG3(D, C, B); +} + +static void LD4(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) = AVG3(A, B, C); + DST(1, 0) = DST(0, 1) = AVG3(B, C, D); + DST(2, 0) = DST(1, 1) = DST(0, 2) = AVG3(C, D, E); + DST(3, 0) = DST(2, 1) = DST(1, 2) = DST(0, 3) = AVG3(D, E, F); + DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G); + DST(3, 2) = DST(2, 3) = AVG3(F, G, H); + DST(3, 3) = AVG3(G, H, H); +} + +static 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 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 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 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 void TM4(uint8_t* dst, const uint8_t* top) { + int x, y; + const uint8_t* const clip = clip1 + 255 - top[-1]; + for (y = 0; y < 4; ++y) { + const uint8_t* const clip_table = clip + top[-2 - y]; + for (x = 0; x < 4; ++x) { + dst[x] = clip_table[top[x]]; + } + dst += BPS; + } +} + +#undef DST +#undef AVG3 +#undef AVG2 + +// Left samples are top[-5 .. -2], top_left is top[-1], top are +// located at top[0..3], and top right is top[4..7] +static void Intra4Preds_C(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); +} + +//------------------------------------------------------------------------------ +// Metric + +#if !WEBP_NEON_OMIT_C_CODE +static WEBP_INLINE int GetSSE(const uint8_t* a, const uint8_t* b, + int w, int h) { + int count = 0; + int y, x; + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + const int diff = (int)a[x] - b[x]; + count += diff * diff; + } + a += BPS; + b += BPS; + } + return count; +} + +static int SSE16x16_C(const uint8_t* a, const uint8_t* b) { + return GetSSE(a, b, 16, 16); +} +static int SSE16x8_C(const uint8_t* a, const uint8_t* b) { + return GetSSE(a, b, 16, 8); +} +static int SSE8x8_C(const uint8_t* a, const uint8_t* b) { + return GetSSE(a, b, 8, 8); +} +static int SSE4x4_C(const uint8_t* a, const uint8_t* b) { + return GetSSE(a, b, 4, 4); +} +#endif // !WEBP_NEON_OMIT_C_CODE + +static void Mean16x4_C(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 +// +// We try to match the spectral content (weighted) between source and +// reconstructed samples. + +#if !WEBP_NEON_OMIT_C_CODE +// Hadamard transform +// Returns the weighted sum of the absolute value of transformed coefficients. +// w[] contains a row-major 4 by 4 symmetric matrix. +static int TTransform(const uint8_t* in, const uint16_t* w) { + int sum = 0; + int tmp[16]; + int i; + // horizontal pass + for (i = 0; i < 4; ++i, in += BPS) { + const int a0 = in[0] + in[2]; + const int a1 = in[1] + in[3]; + const int a2 = in[1] - in[3]; + const int a3 = in[0] - in[2]; + tmp[0 + i * 4] = a0 + a1; + tmp[1 + i * 4] = a3 + a2; + tmp[2 + i * 4] = a3 - a2; + tmp[3 + i * 4] = a0 - a1; + } + // vertical pass + for (i = 0; i < 4; ++i, ++w) { + const int a0 = tmp[0 + i] + tmp[8 + i]; + const int a1 = tmp[4 + i] + tmp[12+ i]; + const int a2 = tmp[4 + i] - tmp[12+ i]; + const int a3 = tmp[0 + i] - tmp[8 + i]; + const int b0 = a0 + a1; + const int b1 = a3 + a2; + const int b2 = a3 - a2; + const int b3 = a0 - a1; + + sum += w[ 0] * abs(b0); + sum += w[ 4] * abs(b1); + sum += w[ 8] * abs(b2); + sum += w[12] * abs(b3); + } + return sum; +} + +static int Disto4x4_C(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_C(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_C(a + x + y, b + x + y, w); + } + } + return D; +} +#endif // !WEBP_NEON_OMIT_C_CODE + +//------------------------------------------------------------------------------ +// Quantization +// + +static const uint8_t kZigzag[16] = { + 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 +}; + +// Simple quantization +static int QuantizeBlock_C(int16_t in[16], int16_t out[16], + const VP8Matrix* const mtx) { + int last = -1; + int n; + 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]) + mtx->sharpen_[j]; + 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); +} + +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC +static int Quantize2Blocks_C(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; +} +#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC + +//------------------------------------------------------------------------------ +// Block copy + +static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int w, int h) { + int y; + for (y = 0; y < h; ++y) { + memcpy(dst, src, w); + src += BPS; + dst += BPS; + } +} + +static void Copy4x4_C(const uint8_t* src, uint8_t* dst) { + Copy(src, dst, 4, 4); +} + +static void Copy16x8_C(const uint8_t* src, uint8_t* dst) { + Copy(src, dst, 16, 8); +} + +//------------------------------------------------------------------------------ +// Initialization + +// Speed-critical function pointers. We have to initialize them to the default +// implementations within VP8EncDspInit(). +VP8CHisto VP8CollectHistogram; +VP8Idct VP8ITransform; +VP8Fdct VP8FTransform; +VP8Fdct VP8FTransform2; +VP8WHT VP8FTransformWHT; +VP8Intra4Preds VP8EncPredLuma4; +VP8IntraPreds VP8EncPredLuma16; +VP8IntraPreds VP8EncPredChroma8; +VP8Metric VP8SSE16x16; +VP8Metric VP8SSE8x8; +VP8Metric VP8SSE16x8; +VP8Metric VP8SSE4x4; +VP8WMetric VP8TDisto4x4; +VP8WMetric VP8TDisto16x16; +VP8MeanMetric VP8Mean16x4; +VP8QuantizeBlock VP8EncQuantizeBlock; +VP8Quantize2Blocks VP8EncQuantize2Blocks; +VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT; +VP8BlockCopy VP8Copy4x4; +VP8BlockCopy VP8Copy16x8; + +extern void VP8EncDspInitSSE2(void); +extern void VP8EncDspInitSSE41(void); +extern void VP8EncDspInitNEON(void); +extern void VP8EncDspInitMIPS32(void); +extern void VP8EncDspInitMIPSdspR2(void); +extern void VP8EncDspInitMSA(void); + +WEBP_DSP_INIT_FUNC(VP8EncDspInit) { + VP8DspInit(); // common inverse transforms + InitTables(); + + // default C implementations +#if !WEBP_NEON_OMIT_C_CODE + VP8ITransform = ITransform_C; + VP8FTransform = FTransform_C; + VP8FTransformWHT = FTransformWHT_C; + VP8TDisto4x4 = Disto4x4_C; + VP8TDisto16x16 = Disto16x16_C; + VP8CollectHistogram = CollectHistogram_C; + VP8SSE16x16 = SSE16x16_C; + VP8SSE16x8 = SSE16x8_C; + VP8SSE8x8 = SSE8x8_C; + VP8SSE4x4 = SSE4x4_C; +#endif + +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC + VP8EncQuantizeBlock = QuantizeBlock_C; + VP8EncQuantize2Blocks = Quantize2Blocks_C; +#endif + + VP8FTransform2 = FTransform2_C; + VP8EncPredLuma4 = Intra4Preds_C; + VP8EncPredLuma16 = Intra16Preds_C; + VP8EncPredChroma8 = IntraChromaPreds_C; + VP8Mean16x4 = Mean16x4_C; + VP8EncQuantizeBlockWHT = QuantizeBlock_C; + VP8Copy4x4 = Copy4x4_C; + VP8Copy16x8 = Copy16x8_C; + + // If defined, use CPUInfo() to overwrite some pointers with faster versions. + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + VP8EncDspInitSSE2(); +#if defined(WEBP_HAVE_SSE41) + if (VP8GetCPUInfo(kSSE4_1)) { + VP8EncDspInitSSE41(); + } +#endif + } +#endif +#if defined(WEBP_USE_MIPS32) + if (VP8GetCPUInfo(kMIPS32)) { + VP8EncDspInitMIPS32(); + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + VP8EncDspInitMIPSdspR2(); + } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8EncDspInitMSA(); + } +#endif + } + +#if defined(WEBP_HAVE_NEON) + if (WEBP_NEON_OMIT_C_CODE || + (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) { + VP8EncDspInitNEON(); + } +#endif + + assert(VP8ITransform != NULL); + assert(VP8FTransform != NULL); + assert(VP8FTransformWHT != NULL); + assert(VP8TDisto4x4 != NULL); + assert(VP8TDisto16x16 != NULL); + assert(VP8CollectHistogram != NULL); + assert(VP8SSE16x16 != NULL); + assert(VP8SSE16x8 != NULL); + assert(VP8SSE8x8 != NULL); + assert(VP8SSE4x4 != NULL); + assert(VP8EncQuantizeBlock != NULL); + assert(VP8EncQuantize2Blocks != NULL); + assert(VP8FTransform2 != NULL); + assert(VP8EncPredLuma4 != NULL); + assert(VP8EncPredLuma16 != NULL); + assert(VP8EncPredChroma8 != NULL); + assert(VP8Mean16x4 != NULL); + assert(VP8EncQuantizeBlockWHT != NULL); + assert(VP8Copy4x4 != NULL); + assert(VP8Copy16x8 != NULL); +} diff --git a/internal/libwebp-1.2.1/src/dsp/enc_mips32.c b/internal/libwebp-1.2.1/src/dsp/enc_mips32.c new file mode 100644 index 0000000..618f0fc --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/enc_mips32.c @@ -0,0 +1,677 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS version of speed-critical encoding functions. +// +// Author(s): Djordje Pesut (djordje.pesut@imgtec.com) +// Jovan Zelincevic (jovan.zelincevic@imgtec.com) +// Slobodan Prijic (slobodan.prijic@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS32) + +#include "src/dsp/mips_macro.h" +#include "src/enc/vp8i_enc.h" +#include "src/enc/cost_enc.h" + +static const int kC1 = 20091 + (1 << 16); +static const int kC2 = 35468; + +// macro for one vertical pass in ITransformOne +// MUL macro inlined +// temp0..temp15 holds tmp[0]..tmp[15] +// A..D - offsets in bytes to load from in buffer +// TEMP0..TEMP3 - registers for corresponding tmp elements +// TEMP4..TEMP5 - temporary registers +#define VERTICAL_PASS(A, B, C, D, TEMP4, TEMP0, TEMP1, TEMP2, TEMP3) \ + "lh %[temp16], " #A "(%[temp20]) \n\t" \ + "lh %[temp18], " #B "(%[temp20]) \n\t" \ + "lh %[temp17], " #C "(%[temp20]) \n\t" \ + "lh %[temp19], " #D "(%[temp20]) \n\t" \ + "addu %[" #TEMP4 "], %[temp16], %[temp18] \n\t" \ + "subu %[temp16], %[temp16], %[temp18] \n\t" \ + "mul %[" #TEMP0 "], %[temp17], %[kC2] \n\t" \ + "mul %[temp18], %[temp19], %[kC1] \n\t" \ + "mul %[temp17], %[temp17], %[kC1] \n\t" \ + "mul %[temp19], %[temp19], %[kC2] \n\t" \ + "sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\n" \ + "sra %[temp18], %[temp18], 16 \n\n" \ + "sra %[temp17], %[temp17], 16 \n\n" \ + "sra %[temp19], %[temp19], 16 \n\n" \ + "subu %[" #TEMP2 "], %[" #TEMP0 "], %[temp18] \n\t" \ + "addu %[" #TEMP3 "], %[temp17], %[temp19] \n\t" \ + "addu %[" #TEMP0 "], %[" #TEMP4 "], %[" #TEMP3 "] \n\t" \ + "addu %[" #TEMP1 "], %[temp16], %[" #TEMP2 "] \n\t" \ + "subu %[" #TEMP2 "], %[temp16], %[" #TEMP2 "] \n\t" \ + "subu %[" #TEMP3 "], %[" #TEMP4 "], %[" #TEMP3 "] \n\t" + +// macro for one horizontal pass in ITransformOne +// MUL and STORE macros inlined +// a = clip_8b(a) is replaced with: a = max(a, 0); a = min(a, 255) +// temp0..temp15 holds tmp[0]..tmp[15] +// A - offset in bytes to load from ref and store to dst buffer +// TEMP0, TEMP4, TEMP8 and TEMP12 - registers for corresponding tmp elements +#define HORIZONTAL_PASS(A, TEMP0, TEMP4, TEMP8, TEMP12) \ + "addiu %[" #TEMP0 "], %[" #TEMP0 "], 4 \n\t" \ + "addu %[temp16], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \ + "subu %[temp17], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \ + "mul %[" #TEMP0 "], %[" #TEMP4 "], %[kC2] \n\t" \ + "mul %[" #TEMP8 "], %[" #TEMP12 "], %[kC1] \n\t" \ + "mul %[" #TEMP4 "], %[" #TEMP4 "], %[kC1] \n\t" \ + "mul %[" #TEMP12 "], %[" #TEMP12 "], %[kC2] \n\t" \ + "sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\t" \ + "sra %[" #TEMP8 "], %[" #TEMP8 "], 16 \n\t" \ + "sra %[" #TEMP4 "], %[" #TEMP4 "], 16 \n\t" \ + "sra %[" #TEMP12 "], %[" #TEMP12 "], 16 \n\t" \ + "subu %[temp18], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \ + "addu %[temp19], %[" #TEMP4 "], %[" #TEMP12 "] \n\t" \ + "addu %[" #TEMP0 "], %[temp16], %[temp19] \n\t" \ + "addu %[" #TEMP4 "], %[temp17], %[temp18] \n\t" \ + "subu %[" #TEMP8 "], %[temp17], %[temp18] \n\t" \ + "subu %[" #TEMP12 "], %[temp16], %[temp19] \n\t" \ + "lw %[temp20], 0(%[args]) \n\t" \ + "sra %[" #TEMP0 "], %[" #TEMP0 "], 3 \n\t" \ + "sra %[" #TEMP4 "], %[" #TEMP4 "], 3 \n\t" \ + "sra %[" #TEMP8 "], %[" #TEMP8 "], 3 \n\t" \ + "sra %[" #TEMP12 "], %[" #TEMP12 "], 3 \n\t" \ + "lbu %[temp16], 0+" XSTR(BPS) "*" #A "(%[temp20]) \n\t" \ + "lbu %[temp17], 1+" XSTR(BPS) "*" #A "(%[temp20]) \n\t" \ + "lbu %[temp18], 2+" XSTR(BPS) "*" #A "(%[temp20]) \n\t" \ + "lbu %[temp19], 3+" XSTR(BPS) "*" #A "(%[temp20]) \n\t" \ + "addu %[" #TEMP0 "], %[temp16], %[" #TEMP0 "] \n\t" \ + "addu %[" #TEMP4 "], %[temp17], %[" #TEMP4 "] \n\t" \ + "addu %[" #TEMP8 "], %[temp18], %[" #TEMP8 "] \n\t" \ + "addu %[" #TEMP12 "], %[temp19], %[" #TEMP12 "] \n\t" \ + "slt %[temp16], %[" #TEMP0 "], $zero \n\t" \ + "slt %[temp17], %[" #TEMP4 "], $zero \n\t" \ + "slt %[temp18], %[" #TEMP8 "], $zero \n\t" \ + "slt %[temp19], %[" #TEMP12 "], $zero \n\t" \ + "movn %[" #TEMP0 "], $zero, %[temp16] \n\t" \ + "movn %[" #TEMP4 "], $zero, %[temp17] \n\t" \ + "movn %[" #TEMP8 "], $zero, %[temp18] \n\t" \ + "movn %[" #TEMP12 "], $zero, %[temp19] \n\t" \ + "addiu %[temp20], $zero, 255 \n\t" \ + "slt %[temp16], %[" #TEMP0 "], %[temp20] \n\t" \ + "slt %[temp17], %[" #TEMP4 "], %[temp20] \n\t" \ + "slt %[temp18], %[" #TEMP8 "], %[temp20] \n\t" \ + "slt %[temp19], %[" #TEMP12 "], %[temp20] \n\t" \ + "movz %[" #TEMP0 "], %[temp20], %[temp16] \n\t" \ + "movz %[" #TEMP4 "], %[temp20], %[temp17] \n\t" \ + "lw %[temp16], 8(%[args]) \n\t" \ + "movz %[" #TEMP8 "], %[temp20], %[temp18] \n\t" \ + "movz %[" #TEMP12 "], %[temp20], %[temp19] \n\t" \ + "sb %[" #TEMP0 "], 0+" XSTR(BPS) "*" #A "(%[temp16]) \n\t" \ + "sb %[" #TEMP4 "], 1+" XSTR(BPS) "*" #A "(%[temp16]) \n\t" \ + "sb %[" #TEMP8 "], 2+" XSTR(BPS) "*" #A "(%[temp16]) \n\t" \ + "sb %[" #TEMP12 "], 3+" XSTR(BPS) "*" #A "(%[temp16]) \n\t" + +// Does one or two inverse transforms. +static WEBP_INLINE void ITransformOne_MIPS32(const uint8_t* ref, + const int16_t* in, + uint8_t* dst) { + int temp0, temp1, temp2, temp3, temp4, temp5, temp6; + int temp7, temp8, temp9, temp10, temp11, temp12, temp13; + int temp14, temp15, temp16, temp17, temp18, temp19, temp20; + const int* args[3] = {(const int*)ref, (const int*)in, (const int*)dst}; + + __asm__ volatile( + "lw %[temp20], 4(%[args]) \n\t" + VERTICAL_PASS(0, 16, 8, 24, temp4, temp0, temp1, temp2, temp3) + VERTICAL_PASS(2, 18, 10, 26, temp8, temp4, temp5, temp6, temp7) + VERTICAL_PASS(4, 20, 12, 28, temp12, temp8, temp9, temp10, temp11) + VERTICAL_PASS(6, 22, 14, 30, temp20, temp12, temp13, temp14, temp15) + + HORIZONTAL_PASS(0, temp0, temp4, temp8, temp12) + HORIZONTAL_PASS(1, temp1, temp5, temp9, temp13) + HORIZONTAL_PASS(2, temp2, temp6, temp10, temp14) + HORIZONTAL_PASS(3, temp3, temp7, temp11, temp15) + + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11), + [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), [temp14]"=&r"(temp14), + [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), [temp17]"=&r"(temp17), + [temp18]"=&r"(temp18), [temp19]"=&r"(temp19), [temp20]"=&r"(temp20) + : [args]"r"(args), [kC1]"r"(kC1), [kC2]"r"(kC2) + : "memory", "hi", "lo" + ); +} + +static void ITransform_MIPS32(const uint8_t* ref, const int16_t* in, + uint8_t* dst, int do_two) { + ITransformOne_MIPS32(ref, in, dst); + if (do_two) { + ITransformOne_MIPS32(ref + 4, in + 16, dst + 4); + } +} + +#undef VERTICAL_PASS +#undef HORIZONTAL_PASS + +// macro for one pass through for loop in QuantizeBlock +// QUANTDIV macro inlined +// J - offset in bytes (kZigzag[n] * 2) +// K - offset in bytes (kZigzag[n] * 4) +// N - offset in bytes (n * 2) +#define QUANTIZE_ONE(J, K, N) \ + "lh %[temp0], " #J "(%[ppin]) \n\t" \ + "lhu %[temp1], " #J "(%[ppsharpen]) \n\t" \ + "lw %[temp2], " #K "(%[ppzthresh]) \n\t" \ + "sra %[sign], %[temp0], 15 \n\t" \ + "xor %[coeff], %[temp0], %[sign] \n\t" \ + "subu %[coeff], %[coeff], %[sign] \n\t" \ + "addu %[coeff], %[coeff], %[temp1] \n\t" \ + "slt %[temp4], %[temp2], %[coeff] \n\t" \ + "addiu %[temp5], $zero, 0 \n\t" \ + "addiu %[level], $zero, 0 \n\t" \ + "beqz %[temp4], 2f \n\t" \ + "lhu %[temp1], " #J "(%[ppiq]) \n\t" \ + "lw %[temp2], " #K "(%[ppbias]) \n\t" \ + "lhu %[temp3], " #J "(%[ppq]) \n\t" \ + "mul %[level], %[coeff], %[temp1] \n\t" \ + "addu %[level], %[level], %[temp2] \n\t" \ + "sra %[level], %[level], 17 \n\t" \ + "slt %[temp4], %[max_level], %[level] \n\t" \ + "movn %[level], %[max_level], %[temp4] \n\t" \ + "xor %[level], %[level], %[sign] \n\t" \ + "subu %[level], %[level], %[sign] \n\t" \ + "mul %[temp5], %[level], %[temp3] \n\t" \ +"2: \n\t" \ + "sh %[temp5], " #J "(%[ppin]) \n\t" \ + "sh %[level], " #N "(%[pout]) \n\t" + +static int QuantizeBlock_MIPS32(int16_t in[16], int16_t out[16], + const VP8Matrix* const mtx) { + int temp0, temp1, temp2, temp3, temp4, temp5; + int sign, coeff, level, i; + int max_level = MAX_LEVEL; + + int16_t* ppin = &in[0]; + int16_t* pout = &out[0]; + const uint16_t* ppsharpen = &mtx->sharpen_[0]; + const uint32_t* ppzthresh = &mtx->zthresh_[0]; + const uint16_t* ppq = &mtx->q_[0]; + const uint16_t* ppiq = &mtx->iq_[0]; + const uint32_t* ppbias = &mtx->bias_[0]; + + __asm__ volatile( + QUANTIZE_ONE( 0, 0, 0) + QUANTIZE_ONE( 2, 4, 2) + QUANTIZE_ONE( 8, 16, 4) + QUANTIZE_ONE(16, 32, 6) + QUANTIZE_ONE(10, 20, 8) + QUANTIZE_ONE( 4, 8, 10) + QUANTIZE_ONE( 6, 12, 12) + QUANTIZE_ONE(12, 24, 14) + QUANTIZE_ONE(18, 36, 16) + QUANTIZE_ONE(24, 48, 18) + QUANTIZE_ONE(26, 52, 20) + QUANTIZE_ONE(20, 40, 22) + QUANTIZE_ONE(14, 28, 24) + QUANTIZE_ONE(22, 44, 26) + QUANTIZE_ONE(28, 56, 28) + QUANTIZE_ONE(30, 60, 30) + + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [sign]"=&r"(sign), [coeff]"=&r"(coeff), + [level]"=&r"(level) + : [pout]"r"(pout), [ppin]"r"(ppin), + [ppiq]"r"(ppiq), [max_level]"r"(max_level), + [ppbias]"r"(ppbias), [ppzthresh]"r"(ppzthresh), + [ppsharpen]"r"(ppsharpen), [ppq]"r"(ppq) + : "memory", "hi", "lo" + ); + + // moved out from macro to increase possibility for earlier breaking + for (i = 15; i >= 0; i--) { + if (out[i]) return 1; + } + return 0; +} + +static int Quantize2Blocks_MIPS32(int16_t in[32], int16_t out[32], + const VP8Matrix* const mtx) { + int nz; + nz = QuantizeBlock_MIPS32(in + 0 * 16, out + 0 * 16, mtx) << 0; + nz |= QuantizeBlock_MIPS32(in + 1 * 16, out + 1 * 16, mtx) << 1; + return nz; +} + +#undef QUANTIZE_ONE + +// macro for one horizontal pass in Disto4x4 (TTransform) +// two calls of function TTransform are merged into single one +// A - offset in bytes to load from a and b buffers +// E..H - offsets in bytes to store first results to tmp buffer +// E1..H1 - offsets in bytes to store second results to tmp buffer +#define HORIZONTAL_PASS(A, E, F, G, H, E1, F1, G1, H1) \ + "lbu %[temp0], 0+" XSTR(BPS) "*" #A "(%[a]) \n\t" \ + "lbu %[temp1], 1+" XSTR(BPS) "*" #A "(%[a]) \n\t" \ + "lbu %[temp2], 2+" XSTR(BPS) "*" #A "(%[a]) \n\t" \ + "lbu %[temp3], 3+" XSTR(BPS) "*" #A "(%[a]) \n\t" \ + "lbu %[temp4], 0+" XSTR(BPS) "*" #A "(%[b]) \n\t" \ + "lbu %[temp5], 1+" XSTR(BPS) "*" #A "(%[b]) \n\t" \ + "lbu %[temp6], 2+" XSTR(BPS) "*" #A "(%[b]) \n\t" \ + "lbu %[temp7], 3+" XSTR(BPS) "*" #A "(%[b]) \n\t" \ + "addu %[temp8], %[temp0], %[temp2] \n\t" \ + "subu %[temp0], %[temp0], %[temp2] \n\t" \ + "addu %[temp2], %[temp1], %[temp3] \n\t" \ + "subu %[temp1], %[temp1], %[temp3] \n\t" \ + "addu %[temp3], %[temp4], %[temp6] \n\t" \ + "subu %[temp4], %[temp4], %[temp6] \n\t" \ + "addu %[temp6], %[temp5], %[temp7] \n\t" \ + "subu %[temp5], %[temp5], %[temp7] \n\t" \ + "addu %[temp7], %[temp8], %[temp2] \n\t" \ + "subu %[temp2], %[temp8], %[temp2] \n\t" \ + "addu %[temp8], %[temp0], %[temp1] \n\t" \ + "subu %[temp0], %[temp0], %[temp1] \n\t" \ + "addu %[temp1], %[temp3], %[temp6] \n\t" \ + "subu %[temp3], %[temp3], %[temp6] \n\t" \ + "addu %[temp6], %[temp4], %[temp5] \n\t" \ + "subu %[temp4], %[temp4], %[temp5] \n\t" \ + "sw %[temp7], " #E "(%[tmp]) \n\t" \ + "sw %[temp2], " #H "(%[tmp]) \n\t" \ + "sw %[temp8], " #F "(%[tmp]) \n\t" \ + "sw %[temp0], " #G "(%[tmp]) \n\t" \ + "sw %[temp1], " #E1 "(%[tmp]) \n\t" \ + "sw %[temp3], " #H1 "(%[tmp]) \n\t" \ + "sw %[temp6], " #F1 "(%[tmp]) \n\t" \ + "sw %[temp4], " #G1 "(%[tmp]) \n\t" + +// macro for one vertical pass in Disto4x4 (TTransform) +// two calls of function TTransform are merged into single one +// since only one accu is available in mips32r1 instruction set +// first is done second call of function TTransform and after +// that first one. +// const int sum1 = TTransform(a, w); +// const int sum2 = TTransform(b, w); +// return abs(sum2 - sum1) >> 5; +// (sum2 - sum1) is calculated with madds (sub2) and msubs (sub1) +// A..D - offsets in bytes to load first results from tmp buffer +// A1..D1 - offsets in bytes to load second results from tmp buffer +// E..H - offsets in bytes to load from w buffer +#define VERTICAL_PASS(A, B, C, D, A1, B1, C1, D1, E, F, G, H) \ + "lw %[temp0], " #A1 "(%[tmp]) \n\t" \ + "lw %[temp1], " #C1 "(%[tmp]) \n\t" \ + "lw %[temp2], " #B1 "(%[tmp]) \n\t" \ + "lw %[temp3], " #D1 "(%[tmp]) \n\t" \ + "addu %[temp8], %[temp0], %[temp1] \n\t" \ + "subu %[temp0], %[temp0], %[temp1] \n\t" \ + "addu %[temp1], %[temp2], %[temp3] \n\t" \ + "subu %[temp2], %[temp2], %[temp3] \n\t" \ + "addu %[temp3], %[temp8], %[temp1] \n\t" \ + "subu %[temp8], %[temp8], %[temp1] \n\t" \ + "addu %[temp1], %[temp0], %[temp2] \n\t" \ + "subu %[temp0], %[temp0], %[temp2] \n\t" \ + "sra %[temp4], %[temp3], 31 \n\t" \ + "sra %[temp5], %[temp1], 31 \n\t" \ + "sra %[temp6], %[temp0], 31 \n\t" \ + "sra %[temp7], %[temp8], 31 \n\t" \ + "xor %[temp3], %[temp3], %[temp4] \n\t" \ + "xor %[temp1], %[temp1], %[temp5] \n\t" \ + "xor %[temp0], %[temp0], %[temp6] \n\t" \ + "xor %[temp8], %[temp8], %[temp7] \n\t" \ + "subu %[temp3], %[temp3], %[temp4] \n\t" \ + "subu %[temp1], %[temp1], %[temp5] \n\t" \ + "subu %[temp0], %[temp0], %[temp6] \n\t" \ + "subu %[temp8], %[temp8], %[temp7] \n\t" \ + "lhu %[temp4], " #E "(%[w]) \n\t" \ + "lhu %[temp5], " #F "(%[w]) \n\t" \ + "lhu %[temp6], " #G "(%[w]) \n\t" \ + "lhu %[temp7], " #H "(%[w]) \n\t" \ + "madd %[temp4], %[temp3] \n\t" \ + "madd %[temp5], %[temp1] \n\t" \ + "madd %[temp6], %[temp0] \n\t" \ + "madd %[temp7], %[temp8] \n\t" \ + "lw %[temp0], " #A "(%[tmp]) \n\t" \ + "lw %[temp1], " #C "(%[tmp]) \n\t" \ + "lw %[temp2], " #B "(%[tmp]) \n\t" \ + "lw %[temp3], " #D "(%[tmp]) \n\t" \ + "addu %[temp8], %[temp0], %[temp1] \n\t" \ + "subu %[temp0], %[temp0], %[temp1] \n\t" \ + "addu %[temp1], %[temp2], %[temp3] \n\t" \ + "subu %[temp2], %[temp2], %[temp3] \n\t" \ + "addu %[temp3], %[temp8], %[temp1] \n\t" \ + "subu %[temp1], %[temp8], %[temp1] \n\t" \ + "addu %[temp8], %[temp0], %[temp2] \n\t" \ + "subu %[temp0], %[temp0], %[temp2] \n\t" \ + "sra %[temp2], %[temp3], 31 \n\t" \ + "xor %[temp3], %[temp3], %[temp2] \n\t" \ + "subu %[temp3], %[temp3], %[temp2] \n\t" \ + "msub %[temp4], %[temp3] \n\t" \ + "sra %[temp2], %[temp8], 31 \n\t" \ + "sra %[temp3], %[temp0], 31 \n\t" \ + "sra %[temp4], %[temp1], 31 \n\t" \ + "xor %[temp8], %[temp8], %[temp2] \n\t" \ + "xor %[temp0], %[temp0], %[temp3] \n\t" \ + "xor %[temp1], %[temp1], %[temp4] \n\t" \ + "subu %[temp8], %[temp8], %[temp2] \n\t" \ + "subu %[temp0], %[temp0], %[temp3] \n\t" \ + "subu %[temp1], %[temp1], %[temp4] \n\t" \ + "msub %[temp5], %[temp8] \n\t" \ + "msub %[temp6], %[temp0] \n\t" \ + "msub %[temp7], %[temp1] \n\t" + +static int Disto4x4_MIPS32(const uint8_t* const a, const uint8_t* const b, + const uint16_t* const w) { + int tmp[32]; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; + + __asm__ volatile( + HORIZONTAL_PASS(0, 0, 4, 8, 12, 64, 68, 72, 76) + HORIZONTAL_PASS(1, 16, 20, 24, 28, 80, 84, 88, 92) + HORIZONTAL_PASS(2, 32, 36, 40, 44, 96, 100, 104, 108) + HORIZONTAL_PASS(3, 48, 52, 56, 60, 112, 116, 120, 124) + "mthi $zero \n\t" + "mtlo $zero \n\t" + VERTICAL_PASS( 0, 16, 32, 48, 64, 80, 96, 112, 0, 8, 16, 24) + VERTICAL_PASS( 4, 20, 36, 52, 68, 84, 100, 116, 2, 10, 18, 26) + VERTICAL_PASS( 8, 24, 40, 56, 72, 88, 104, 120, 4, 12, 20, 28) + VERTICAL_PASS(12, 28, 44, 60, 76, 92, 108, 124, 6, 14, 22, 30) + "mflo %[temp0] \n\t" + "sra %[temp1], %[temp0], 31 \n\t" + "xor %[temp0], %[temp0], %[temp1] \n\t" + "subu %[temp0], %[temp0], %[temp1] \n\t" + "sra %[temp0], %[temp0], 5 \n\t" + + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8) + : [a]"r"(a), [b]"r"(b), [w]"r"(w), [tmp]"r"(tmp) + : "memory", "hi", "lo" + ); + + return temp0; +} + +#undef VERTICAL_PASS +#undef HORIZONTAL_PASS + +static int Disto16x16_MIPS32(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_MIPS32(a + x + y, b + x + y, w); + } + } + return D; +} + +// macro for one horizontal pass in FTransform +// temp0..temp15 holds tmp[0]..tmp[15] +// A - offset in bytes to load from src and ref buffers +// TEMP0..TEMP3 - registers for corresponding tmp elements +#define HORIZONTAL_PASS(A, TEMP0, TEMP1, TEMP2, TEMP3) \ + "lw %[" #TEMP1 "], 0(%[args]) \n\t" \ + "lw %[" #TEMP2 "], 4(%[args]) \n\t" \ + "lbu %[temp16], 0+" XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \ + "lbu %[temp17], 0+" XSTR(BPS) "*" #A "(%[" #TEMP2 "]) \n\t" \ + "lbu %[temp18], 1+" XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \ + "lbu %[temp19], 1+" XSTR(BPS) "*" #A "(%[" #TEMP2 "]) \n\t" \ + "subu %[temp20], %[temp16], %[temp17] \n\t" \ + "lbu %[temp16], 2+" XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \ + "lbu %[temp17], 2+" XSTR(BPS) "*" #A "(%[" #TEMP2 "]) \n\t" \ + "subu %[" #TEMP0 "], %[temp18], %[temp19] \n\t" \ + "lbu %[temp18], 3+" XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \ + "lbu %[temp19], 3+" XSTR(BPS) "*" #A "(%[" #TEMP2 "]) \n\t" \ + "subu %[" #TEMP1 "], %[temp16], %[temp17] \n\t" \ + "subu %[" #TEMP2 "], %[temp18], %[temp19] \n\t" \ + "addu %[" #TEMP3 "], %[temp20], %[" #TEMP2 "] \n\t" \ + "subu %[" #TEMP2 "], %[temp20], %[" #TEMP2 "] \n\t" \ + "addu %[temp20], %[" #TEMP0 "], %[" #TEMP1 "] \n\t" \ + "subu %[" #TEMP0 "], %[" #TEMP0 "], %[" #TEMP1 "] \n\t" \ + "mul %[temp16], %[" #TEMP2 "], %[c5352] \n\t" \ + "mul %[temp17], %[" #TEMP2 "], %[c2217] \n\t" \ + "mul %[temp18], %[" #TEMP0 "], %[c5352] \n\t" \ + "mul %[temp19], %[" #TEMP0 "], %[c2217] \n\t" \ + "addu %[" #TEMP1 "], %[" #TEMP3 "], %[temp20] \n\t" \ + "subu %[temp20], %[" #TEMP3 "], %[temp20] \n\t" \ + "sll %[" #TEMP0 "], %[" #TEMP1 "], 3 \n\t" \ + "sll %[" #TEMP2 "], %[temp20], 3 \n\t" \ + "addiu %[temp16], %[temp16], 1812 \n\t" \ + "addiu %[temp17], %[temp17], 937 \n\t" \ + "addu %[temp16], %[temp16], %[temp19] \n\t" \ + "subu %[temp17], %[temp17], %[temp18] \n\t" \ + "sra %[" #TEMP1 "], %[temp16], 9 \n\t" \ + "sra %[" #TEMP3 "], %[temp17], 9 \n\t" + +// macro for one vertical pass in FTransform +// temp0..temp15 holds tmp[0]..tmp[15] +// A..D - offsets in bytes to store to out buffer +// TEMP0, TEMP4, TEMP8 and TEMP12 - registers for corresponding tmp elements +#define VERTICAL_PASS(A, B, C, D, TEMP0, TEMP4, TEMP8, TEMP12) \ + "addu %[temp16], %[" #TEMP0 "], %[" #TEMP12 "] \n\t" \ + "subu %[temp19], %[" #TEMP0 "], %[" #TEMP12 "] \n\t" \ + "addu %[temp17], %[" #TEMP4 "], %[" #TEMP8 "] \n\t" \ + "subu %[temp18], %[" #TEMP4 "], %[" #TEMP8 "] \n\t" \ + "mul %[" #TEMP8 "], %[temp19], %[c2217] \n\t" \ + "mul %[" #TEMP12 "], %[temp18], %[c2217] \n\t" \ + "mul %[" #TEMP4 "], %[temp19], %[c5352] \n\t" \ + "mul %[temp18], %[temp18], %[c5352] \n\t" \ + "addiu %[temp16], %[temp16], 7 \n\t" \ + "addu %[" #TEMP0 "], %[temp16], %[temp17] \n\t" \ + "sra %[" #TEMP0 "], %[" #TEMP0 "], 4 \n\t" \ + "addu %[" #TEMP12 "], %[" #TEMP12 "], %[" #TEMP4 "] \n\t" \ + "subu %[" #TEMP4 "], %[temp16], %[temp17] \n\t" \ + "sra %[" #TEMP4 "], %[" #TEMP4 "], 4 \n\t" \ + "addiu %[" #TEMP8 "], %[" #TEMP8 "], 30000 \n\t" \ + "addiu %[" #TEMP12 "], %[" #TEMP12 "], 12000 \n\t" \ + "addiu %[" #TEMP8 "], %[" #TEMP8 "], 21000 \n\t" \ + "subu %[" #TEMP8 "], %[" #TEMP8 "], %[temp18] \n\t" \ + "sra %[" #TEMP12 "], %[" #TEMP12 "], 16 \n\t" \ + "sra %[" #TEMP8 "], %[" #TEMP8 "], 16 \n\t" \ + "addiu %[temp16], %[" #TEMP12 "], 1 \n\t" \ + "movn %[" #TEMP12 "], %[temp16], %[temp19] \n\t" \ + "sh %[" #TEMP0 "], " #A "(%[temp20]) \n\t" \ + "sh %[" #TEMP4 "], " #C "(%[temp20]) \n\t" \ + "sh %[" #TEMP8 "], " #D "(%[temp20]) \n\t" \ + "sh %[" #TEMP12 "], " #B "(%[temp20]) \n\t" + +static void FTransform_MIPS32(const uint8_t* src, const uint8_t* ref, + int16_t* out) { + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; + int temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16; + int temp17, temp18, temp19, temp20; + const int c2217 = 2217; + const int c5352 = 5352; + const int* const args[3] = + { (const int*)src, (const int*)ref, (const int*)out }; + + __asm__ volatile( + HORIZONTAL_PASS(0, temp0, temp1, temp2, temp3) + HORIZONTAL_PASS(1, temp4, temp5, temp6, temp7) + HORIZONTAL_PASS(2, temp8, temp9, temp10, temp11) + HORIZONTAL_PASS(3, temp12, temp13, temp14, temp15) + "lw %[temp20], 8(%[args]) \n\t" + VERTICAL_PASS(0, 8, 16, 24, temp0, temp4, temp8, temp12) + VERTICAL_PASS(2, 10, 18, 26, temp1, temp5, temp9, temp13) + VERTICAL_PASS(4, 12, 20, 28, temp2, temp6, temp10, temp14) + VERTICAL_PASS(6, 14, 22, 30, temp3, temp7, temp11, temp15) + + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11), + [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), [temp14]"=&r"(temp14), + [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), [temp17]"=&r"(temp17), + [temp18]"=&r"(temp18), [temp19]"=&r"(temp19), [temp20]"=&r"(temp20) + : [args]"r"(args), [c2217]"r"(c2217), [c5352]"r"(c5352) + : "memory", "hi", "lo" + ); +} + +#undef VERTICAL_PASS +#undef HORIZONTAL_PASS + +#if !defined(WORK_AROUND_GCC) + +#define GET_SSE_INNER(A, B, C, D) \ + "lbu %[temp0], " #A "(%[a]) \n\t" \ + "lbu %[temp1], " #A "(%[b]) \n\t" \ + "lbu %[temp2], " #B "(%[a]) \n\t" \ + "lbu %[temp3], " #B "(%[b]) \n\t" \ + "lbu %[temp4], " #C "(%[a]) \n\t" \ + "lbu %[temp5], " #C "(%[b]) \n\t" \ + "lbu %[temp6], " #D "(%[a]) \n\t" \ + "lbu %[temp7], " #D "(%[b]) \n\t" \ + "subu %[temp0], %[temp0], %[temp1] \n\t" \ + "subu %[temp2], %[temp2], %[temp3] \n\t" \ + "subu %[temp4], %[temp4], %[temp5] \n\t" \ + "subu %[temp6], %[temp6], %[temp7] \n\t" \ + "madd %[temp0], %[temp0] \n\t" \ + "madd %[temp2], %[temp2] \n\t" \ + "madd %[temp4], %[temp4] \n\t" \ + "madd %[temp6], %[temp6] \n\t" + +#define GET_SSE(A, B, C, D) \ + GET_SSE_INNER(A, A + 1, A + 2, A + 3) \ + GET_SSE_INNER(B, B + 1, B + 2, B + 3) \ + GET_SSE_INNER(C, C + 1, C + 2, C + 3) \ + GET_SSE_INNER(D, D + 1, D + 2, D + 3) + +static int SSE16x16_MIPS32(const uint8_t* a, const uint8_t* b) { + int count; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + + __asm__ volatile( + "mult $zero, $zero \n\t" + + GET_SSE( 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS) + GET_SSE( 1 * BPS, 4 + 1 * BPS, 8 + 1 * BPS, 12 + 1 * BPS) + GET_SSE( 2 * BPS, 4 + 2 * BPS, 8 + 2 * BPS, 12 + 2 * BPS) + GET_SSE( 3 * BPS, 4 + 3 * BPS, 8 + 3 * BPS, 12 + 3 * BPS) + GET_SSE( 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS) + GET_SSE( 5 * BPS, 4 + 5 * BPS, 8 + 5 * BPS, 12 + 5 * BPS) + GET_SSE( 6 * BPS, 4 + 6 * BPS, 8 + 6 * BPS, 12 + 6 * BPS) + GET_SSE( 7 * BPS, 4 + 7 * BPS, 8 + 7 * BPS, 12 + 7 * BPS) + GET_SSE( 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS) + GET_SSE( 9 * BPS, 4 + 9 * BPS, 8 + 9 * BPS, 12 + 9 * BPS) + GET_SSE(10 * BPS, 4 + 10 * BPS, 8 + 10 * BPS, 12 + 10 * BPS) + GET_SSE(11 * BPS, 4 + 11 * BPS, 8 + 11 * BPS, 12 + 11 * BPS) + GET_SSE(12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS) + GET_SSE(13 * BPS, 4 + 13 * BPS, 8 + 13 * BPS, 12 + 13 * BPS) + GET_SSE(14 * BPS, 4 + 14 * BPS, 8 + 14 * BPS, 12 + 14 * BPS) + GET_SSE(15 * BPS, 4 + 15 * BPS, 8 + 15 * BPS, 12 + 15 * BPS) + + "mflo %[count] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [count]"=&r"(count) + : [a]"r"(a), [b]"r"(b) + : "memory", "hi", "lo" + ); + return count; +} + +static int SSE16x8_MIPS32(const uint8_t* a, const uint8_t* b) { + int count; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + + __asm__ volatile( + "mult $zero, $zero \n\t" + + GET_SSE( 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS) + GET_SSE( 1 * BPS, 4 + 1 * BPS, 8 + 1 * BPS, 12 + 1 * BPS) + GET_SSE( 2 * BPS, 4 + 2 * BPS, 8 + 2 * BPS, 12 + 2 * BPS) + GET_SSE( 3 * BPS, 4 + 3 * BPS, 8 + 3 * BPS, 12 + 3 * BPS) + GET_SSE( 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS) + GET_SSE( 5 * BPS, 4 + 5 * BPS, 8 + 5 * BPS, 12 + 5 * BPS) + GET_SSE( 6 * BPS, 4 + 6 * BPS, 8 + 6 * BPS, 12 + 6 * BPS) + GET_SSE( 7 * BPS, 4 + 7 * BPS, 8 + 7 * BPS, 12 + 7 * BPS) + + "mflo %[count] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [count]"=&r"(count) + : [a]"r"(a), [b]"r"(b) + : "memory", "hi", "lo" + ); + return count; +} + +static int SSE8x8_MIPS32(const uint8_t* a, const uint8_t* b) { + int count; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + + __asm__ volatile( + "mult $zero, $zero \n\t" + + GET_SSE(0 * BPS, 4 + 0 * BPS, 1 * BPS, 4 + 1 * BPS) + GET_SSE(2 * BPS, 4 + 2 * BPS, 3 * BPS, 4 + 3 * BPS) + GET_SSE(4 * BPS, 4 + 4 * BPS, 5 * BPS, 4 + 5 * BPS) + GET_SSE(6 * BPS, 4 + 6 * BPS, 7 * BPS, 4 + 7 * BPS) + + "mflo %[count] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [count]"=&r"(count) + : [a]"r"(a), [b]"r"(b) + : "memory", "hi", "lo" + ); + return count; +} + +static int SSE4x4_MIPS32(const uint8_t* a, const uint8_t* b) { + int count; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + + __asm__ volatile( + "mult $zero, $zero \n\t" + + GET_SSE(0 * BPS, 1 * BPS, 2 * BPS, 3 * BPS) + + "mflo %[count] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [count]"=&r"(count) + : [a]"r"(a), [b]"r"(b) + : "memory", "hi", "lo" + ); + return count; +} + +#undef GET_SSE +#undef GET_SSE_INNER + +#endif // !WORK_AROUND_GCC + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspInitMIPS32(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitMIPS32(void) { + VP8ITransform = ITransform_MIPS32; + VP8FTransform = FTransform_MIPS32; + + VP8EncQuantizeBlock = QuantizeBlock_MIPS32; + VP8EncQuantize2Blocks = Quantize2Blocks_MIPS32; + + VP8TDisto4x4 = Disto4x4_MIPS32; + VP8TDisto16x16 = Disto16x16_MIPS32; + +#if !defined(WORK_AROUND_GCC) + VP8SSE16x16 = SSE16x16_MIPS32; + VP8SSE8x8 = SSE8x8_MIPS32; + VP8SSE16x8 = SSE16x8_MIPS32; + VP8SSE4x4 = SSE4x4_MIPS32; +#endif +} + +#else // !WEBP_USE_MIPS32 + +WEBP_DSP_INIT_STUB(VP8EncDspInitMIPS32) + +#endif // WEBP_USE_MIPS32 diff --git a/internal/libwebp-1.2.1/src/dsp/enc_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/enc_mips_dsp_r2.c new file mode 100644 index 0000000..9ddd895 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/enc_mips_dsp_r2.c @@ -0,0 +1,1517 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS version of speed-critical encoding functions. +// +// Author(s): Darko Laus (darko.laus@imgtec.com) +// Mirko Raus (mirko.raus@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) + +#include "src/dsp/mips_macro.h" +#include "src/enc/cost_enc.h" +#include "src/enc/vp8i_enc.h" + +static const int kC1 = 20091 + (1 << 16); +static const int kC2 = 35468; + +// O - output +// I - input (macro doesn't change it) +#define ADD_SUB_HALVES_X4(O0, O1, O2, O3, O4, O5, O6, O7, \ + I0, I1, I2, I3, I4, I5, I6, I7) \ + "addq.ph %[" #O0 "], %[" #I0 "], %[" #I1 "] \n\t" \ + "subq.ph %[" #O1 "], %[" #I0 "], %[" #I1 "] \n\t" \ + "addq.ph %[" #O2 "], %[" #I2 "], %[" #I3 "] \n\t" \ + "subq.ph %[" #O3 "], %[" #I2 "], %[" #I3 "] \n\t" \ + "addq.ph %[" #O4 "], %[" #I4 "], %[" #I5 "] \n\t" \ + "subq.ph %[" #O5 "], %[" #I4 "], %[" #I5 "] \n\t" \ + "addq.ph %[" #O6 "], %[" #I6 "], %[" #I7 "] \n\t" \ + "subq.ph %[" #O7 "], %[" #I6 "], %[" #I7 "] \n\t" + +// IO - input/output +#define ABS_X8(IO0, IO1, IO2, IO3, IO4, IO5, IO6, IO7) \ + "absq_s.ph %[" #IO0 "], %[" #IO0 "] \n\t" \ + "absq_s.ph %[" #IO1 "], %[" #IO1 "] \n\t" \ + "absq_s.ph %[" #IO2 "], %[" #IO2 "] \n\t" \ + "absq_s.ph %[" #IO3 "], %[" #IO3 "] \n\t" \ + "absq_s.ph %[" #IO4 "], %[" #IO4 "] \n\t" \ + "absq_s.ph %[" #IO5 "], %[" #IO5 "] \n\t" \ + "absq_s.ph %[" #IO6 "], %[" #IO6 "] \n\t" \ + "absq_s.ph %[" #IO7 "], %[" #IO7 "] \n\t" + +// dpa.w.ph $ac0 temp0 ,temp1 +// $ac += temp0[31..16] * temp1[31..16] + temp0[15..0] * temp1[15..0] +// dpax.w.ph $ac0 temp0 ,temp1 +// $ac += temp0[31..16] * temp1[15..0] + temp0[15..0] * temp1[31..16] +// O - output +// I - input (macro doesn't change it) +#define MUL_HALF(O0, I0, I1, I2, I3, I4, I5, I6, I7, \ + I8, I9, I10, I11, I12, I13, I14, I15) \ + "mult $ac0, $zero, $zero \n\t" \ + "dpa.w.ph $ac0, %[" #I2 "], %[" #I0 "] \n\t" \ + "dpax.w.ph $ac0, %[" #I5 "], %[" #I6 "] \n\t" \ + "dpa.w.ph $ac0, %[" #I8 "], %[" #I9 "] \n\t" \ + "dpax.w.ph $ac0, %[" #I11 "], %[" #I4 "] \n\t" \ + "dpa.w.ph $ac0, %[" #I12 "], %[" #I7 "] \n\t" \ + "dpax.w.ph $ac0, %[" #I13 "], %[" #I1 "] \n\t" \ + "dpa.w.ph $ac0, %[" #I14 "], %[" #I3 "] \n\t" \ + "dpax.w.ph $ac0, %[" #I15 "], %[" #I10 "] \n\t" \ + "mflo %[" #O0 "], $ac0 \n\t" + +#define OUTPUT_EARLY_CLOBBER_REGS_17() \ + OUTPUT_EARLY_CLOBBER_REGS_10(), \ + [temp11]"=&r"(temp11), [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), \ + [temp14]"=&r"(temp14), [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), \ + [temp17]"=&r"(temp17) + +// macro for one horizontal pass in FTransform +// temp0..temp15 holds tmp[0]..tmp[15] +// A - offset in bytes to load from src and ref buffers +// TEMP0..TEMP3 - registers for corresponding tmp elements +#define HORIZONTAL_PASS(A, TEMP0, TEMP1, TEMP2, TEMP3) \ + "lw %[" #TEMP0 "], 0(%[args]) \n\t" \ + "lw %[" #TEMP1 "], 4(%[args]) \n\t" \ + "lw %[" #TEMP2 "], " XSTR(BPS) "*" #A "(%[" #TEMP0 "]) \n\t" \ + "lw %[" #TEMP3 "], " XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \ + "preceu.ph.qbl %[" #TEMP0 "], %[" #TEMP2 "] \n\t" \ + "preceu.ph.qbl %[" #TEMP1 "], %[" #TEMP3 "] \n\t" \ + "preceu.ph.qbr %[" #TEMP2 "], %[" #TEMP2 "] \n\t" \ + "preceu.ph.qbr %[" #TEMP3 "], %[" #TEMP3 "] \n\t" \ + "subq.ph %[" #TEMP0 "], %[" #TEMP0 "], %[" #TEMP1 "] \n\t" \ + "subq.ph %[" #TEMP2 "], %[" #TEMP2 "], %[" #TEMP3 "] \n\t" \ + "rotr %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\t" \ + "addq.ph %[" #TEMP1 "], %[" #TEMP2 "], %[" #TEMP0 "] \n\t" \ + "subq.ph %[" #TEMP3 "], %[" #TEMP2 "], %[" #TEMP0 "] \n\t" \ + "seh %[" #TEMP0 "], %[" #TEMP1 "] \n\t" \ + "sra %[temp16], %[" #TEMP1 "], 16 \n\t" \ + "seh %[temp19], %[" #TEMP3 "] \n\t" \ + "sra %[" #TEMP3 "], %[" #TEMP3 "], 16 \n\t" \ + "subu %[" #TEMP2 "], %[" #TEMP0 "], %[temp16] \n\t" \ + "addu %[" #TEMP0 "], %[" #TEMP0 "], %[temp16] \n\t" \ + "mul %[temp17], %[temp19], %[c2217] \n\t" \ + "mul %[temp18], %[" #TEMP3 "], %[c5352] \n\t" \ + "mul %[" #TEMP1 "], %[temp19], %[c5352] \n\t" \ + "mul %[temp16], %[" #TEMP3 "], %[c2217] \n\t" \ + "sll %[" #TEMP2 "], %[" #TEMP2 "], 3 \n\t" \ + "sll %[" #TEMP0 "], %[" #TEMP0 "], 3 \n\t" \ + "subu %[" #TEMP3 "], %[temp17], %[temp18] \n\t" \ + "addu %[" #TEMP1 "], %[temp16], %[" #TEMP1 "] \n\t" \ + "addiu %[" #TEMP3 "], %[" #TEMP3 "], 937 \n\t" \ + "addiu %[" #TEMP1 "], %[" #TEMP1 "], 1812 \n\t" \ + "sra %[" #TEMP3 "], %[" #TEMP3 "], 9 \n\t" \ + "sra %[" #TEMP1 "], %[" #TEMP1 "], 9 \n\t" + +// macro for one vertical pass in FTransform +// temp0..temp15 holds tmp[0]..tmp[15] +// A..D - offsets in bytes to store to out buffer +// TEMP0, TEMP4, TEMP8 and TEMP12 - registers for corresponding tmp elements +#define VERTICAL_PASS(A, B, C, D, TEMP0, TEMP4, TEMP8, TEMP12) \ + "addu %[temp16], %[" #TEMP0 "], %[" #TEMP12 "] \n\t" \ + "subu %[temp19], %[" #TEMP0 "], %[" #TEMP12 "] \n\t" \ + "addu %[temp17], %[" #TEMP4 "], %[" #TEMP8 "] \n\t" \ + "subu %[temp18], %[" #TEMP4 "], %[" #TEMP8 "] \n\t" \ + "mul %[" #TEMP8 "], %[temp19], %[c2217] \n\t" \ + "mul %[" #TEMP12 "], %[temp18], %[c2217] \n\t" \ + "mul %[" #TEMP4 "], %[temp19], %[c5352] \n\t" \ + "mul %[temp18], %[temp18], %[c5352] \n\t" \ + "addiu %[temp16], %[temp16], 7 \n\t" \ + "addu %[" #TEMP0 "], %[temp16], %[temp17] \n\t" \ + "sra %[" #TEMP0 "], %[" #TEMP0 "], 4 \n\t" \ + "addu %[" #TEMP12 "], %[" #TEMP12 "], %[" #TEMP4 "] \n\t" \ + "subu %[" #TEMP4 "], %[temp16], %[temp17] \n\t" \ + "sra %[" #TEMP4 "], %[" #TEMP4 "], 4 \n\t" \ + "addiu %[" #TEMP8 "], %[" #TEMP8 "], 30000 \n\t" \ + "addiu %[" #TEMP12 "], %[" #TEMP12 "], 12000 \n\t" \ + "addiu %[" #TEMP8 "], %[" #TEMP8 "], 21000 \n\t" \ + "subu %[" #TEMP8 "], %[" #TEMP8 "], %[temp18] \n\t" \ + "sra %[" #TEMP12 "], %[" #TEMP12 "], 16 \n\t" \ + "sra %[" #TEMP8 "], %[" #TEMP8 "], 16 \n\t" \ + "addiu %[temp16], %[" #TEMP12 "], 1 \n\t" \ + "movn %[" #TEMP12 "], %[temp16], %[temp19] \n\t" \ + "sh %[" #TEMP0 "], " #A "(%[temp20]) \n\t" \ + "sh %[" #TEMP4 "], " #C "(%[temp20]) \n\t" \ + "sh %[" #TEMP8 "], " #D "(%[temp20]) \n\t" \ + "sh %[" #TEMP12 "], " #B "(%[temp20]) \n\t" + +static void FTransform_MIPSdspR2(const uint8_t* src, const uint8_t* ref, + int16_t* out) { + const int c2217 = 2217; + const int c5352 = 5352; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; + int temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16; + int temp17, temp18, temp19, temp20; + const int* const args[3] = + { (const int*)src, (const int*)ref, (const int*)out }; + + __asm__ volatile ( + HORIZONTAL_PASS(0, temp0, temp1, temp2, temp3) + HORIZONTAL_PASS(1, temp4, temp5, temp6, temp7) + HORIZONTAL_PASS(2, temp8, temp9, temp10, temp11) + HORIZONTAL_PASS(3, temp12, temp13, temp14, temp15) + "lw %[temp20], 8(%[args]) \n\t" + VERTICAL_PASS(0, 8, 16, 24, temp0, temp4, temp8, temp12) + VERTICAL_PASS(2, 10, 18, 26, temp1, temp5, temp9, temp13) + VERTICAL_PASS(4, 12, 20, 28, temp2, temp6, temp10, temp14) + VERTICAL_PASS(6, 14, 22, 30, temp3, temp7, temp11, temp15) + OUTPUT_EARLY_CLOBBER_REGS_18(), + [temp0]"=&r"(temp0), [temp19]"=&r"(temp19), [temp20]"=&r"(temp20) + : [args]"r"(args), [c2217]"r"(c2217), [c5352]"r"(c5352) + : "memory", "hi", "lo" + ); +} + +#undef VERTICAL_PASS +#undef HORIZONTAL_PASS + +static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, + uint8_t* dst) { + int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9; + int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18; + + __asm__ volatile ( + "ulw %[temp1], 0(%[in]) \n\t" + "ulw %[temp2], 16(%[in]) \n\t" + LOAD_IN_X2(temp5, temp6, 24, 26) + ADD_SUB_HALVES(temp3, temp4, temp1, temp2) + LOAD_IN_X2(temp1, temp2, 8, 10) + MUL_SHIFT_SUM(temp7, temp8, temp9, temp10, temp11, temp12, temp13, temp14, + temp10, temp8, temp9, temp7, temp1, temp2, temp5, temp6, + temp13, temp11, temp14, temp12) + INSERT_HALF_X2(temp8, temp7, temp10, temp9) + "ulw %[temp17], 4(%[in]) \n\t" + "ulw %[temp18], 20(%[in]) \n\t" + ADD_SUB_HALVES(temp1, temp2, temp3, temp8) + ADD_SUB_HALVES(temp5, temp6, temp4, temp7) + ADD_SUB_HALVES(temp7, temp8, temp17, temp18) + LOAD_IN_X2(temp17, temp18, 12, 14) + LOAD_IN_X2(temp9, temp10, 28, 30) + MUL_SHIFT_SUM(temp11, temp12, temp13, temp14, temp15, temp16, temp4, temp17, + temp12, temp14, temp11, temp13, temp17, temp18, temp9, temp10, + temp15, temp4, temp16, temp17) + INSERT_HALF_X2(temp11, temp12, temp13, temp14) + ADD_SUB_HALVES(temp17, temp8, temp8, temp11) + ADD_SUB_HALVES(temp3, temp4, temp7, temp12) + + // horizontal + SRA_16(temp9, temp10, temp11, temp12, temp1, temp2, temp5, temp6) + INSERT_HALF_X2(temp1, temp6, temp5, temp2) + SRA_16(temp13, temp14, temp15, temp16, temp3, temp4, temp17, temp8) + "repl.ph %[temp2], 0x4 \n\t" + INSERT_HALF_X2(temp3, temp8, temp17, temp4) + "addq.ph %[temp1], %[temp1], %[temp2] \n\t" + "addq.ph %[temp6], %[temp6], %[temp2] \n\t" + ADD_SUB_HALVES(temp2, temp4, temp1, temp3) + ADD_SUB_HALVES(temp5, temp7, temp6, temp8) + MUL_SHIFT_SUM(temp1, temp3, temp6, temp8, temp9, temp13, temp17, temp18, + temp3, temp13, temp1, temp9, temp9, temp13, temp11, temp15, + temp6, temp17, temp8, temp18) + MUL_SHIFT_SUM(temp6, temp8, temp18, temp17, temp11, temp15, temp12, temp16, + temp8, temp15, temp6, temp11, temp12, temp16, temp10, temp14, + temp18, temp12, temp17, temp16) + INSERT_HALF_X2(temp1, temp3, temp9, temp13) + INSERT_HALF_X2(temp6, temp8, temp11, temp15) + SHIFT_R_SUM_X2(temp9, temp10, temp11, temp12, temp13, temp14, temp15, + temp16, temp2, temp4, temp5, temp7, temp3, temp1, temp8, + temp6) + PACK_2_HALVES_TO_WORD(temp1, temp2, temp3, temp4, temp9, temp12, temp13, + temp16, temp11, temp10, temp15, temp14) + LOAD_WITH_OFFSET_X4(temp10, temp11, temp14, temp15, ref, + 0, 0, 0, 0, + 0, 1, 2, 3, + BPS) + CONVERT_2_BYTES_TO_HALF(temp5, temp6, temp7, temp8, temp17, temp18, temp10, + temp11, temp10, temp11, temp14, temp15) + STORE_SAT_SUM_X2(temp5, temp6, temp7, temp8, temp17, temp18, temp10, temp11, + temp9, temp12, temp1, temp2, temp13, temp16, temp3, temp4, + dst, 0, 1, 2, 3, BPS) + + OUTPUT_EARLY_CLOBBER_REGS_18() + : [dst]"r"(dst), [in]"r"(in), [kC1]"r"(kC1), [kC2]"r"(kC2), [ref]"r"(ref) + : "memory", "hi", "lo" + ); +} + +static void ITransform_MIPSdspR2(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 int Disto4x4_MIPSdspR2(const uint8_t* const a, const uint8_t* const b, + const uint16_t* const w) { + int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9; + int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17; + + __asm__ volatile ( + LOAD_WITH_OFFSET_X4(temp1, temp2, temp3, temp4, a, + 0, 0, 0, 0, + 0, 1, 2, 3, + BPS) + CONVERT_2_BYTES_TO_HALF(temp5, temp6, temp7, temp8, temp9,temp10, temp11, + temp12, temp1, temp2, temp3, temp4) + ADD_SUB_HALVES_X4(temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, + temp5, temp6, temp7, temp8, temp9, temp10, temp11, temp12) + PACK_2_HALVES_TO_WORD(temp9, temp10, temp11, temp12, temp1, temp3, temp5, + temp7, temp2, temp4, temp6, temp8) + ADD_SUB_HALVES_X4(temp2, temp4, temp6, temp8, temp9, temp1, temp3, temp10, + temp1, temp9, temp3, temp10, temp5, temp11, temp7, temp12) + ADD_SUB_HALVES_X4(temp5, temp11, temp7, temp2, temp9, temp3, temp6, temp12, + temp2, temp9, temp6, temp3, temp4, temp1, temp8, temp10) + ADD_SUB_HALVES_X4(temp1, temp4, temp10, temp8, temp7, temp11, temp5, temp2, + temp5, temp7, temp11, temp2, temp9, temp6, temp3, temp12) + ABS_X8(temp1, temp4, temp10, temp8, temp7, temp11, temp5, temp2) + LOAD_WITH_OFFSET_X4(temp3, temp6, temp9, temp12, w, + 0, 4, 8, 12, + 0, 0, 0, 0, + 0) + LOAD_WITH_OFFSET_X4(temp13, temp14, temp15, temp16, w, + 0, 4, 8, 12, + 1, 1, 1, 1, + 16) + MUL_HALF(temp17, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, + temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16) + LOAD_WITH_OFFSET_X4(temp1, temp2, temp3, temp4, b, + 0, 0, 0, 0, + 0, 1, 2, 3, + BPS) + CONVERT_2_BYTES_TO_HALF(temp5,temp6, temp7, temp8, temp9,temp10, temp11, + temp12, temp1, temp2, temp3, temp4) + ADD_SUB_HALVES_X4(temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, + temp5, temp6, temp7, temp8, temp9, temp10, temp11, temp12) + PACK_2_HALVES_TO_WORD(temp9, temp10, temp11, temp12, temp1, temp3, temp5, + temp7, temp2, temp4, temp6, temp8) + ADD_SUB_HALVES_X4(temp2, temp4, temp6, temp8, temp9, temp1, temp3, temp10, + temp1, temp9, temp3, temp10, temp5, temp11, temp7, temp12) + ADD_SUB_HALVES_X4(temp5, temp11, temp7, temp2, temp9, temp3, temp6, temp12, + temp2, temp9, temp6, temp3, temp4, temp1, temp8, temp10) + ADD_SUB_HALVES_X4(temp1, temp4, temp10, temp8, temp7, temp11, temp5, temp2, + temp5, temp7, temp11, temp2, temp9, temp6, temp3, temp12) + ABS_X8(temp1, temp4, temp10, temp8, temp7, temp11, temp5, temp2) + LOAD_WITH_OFFSET_X4(temp3, temp6, temp9, temp12, w, + 0, 4, 8, 12, + 0, 0, 0, 0, + 0) + LOAD_WITH_OFFSET_X4(temp13, temp14, temp15, temp16, w, + 0, 4, 8, 12, + 1, 1, 1, 1, + 16) + MUL_HALF(temp3, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, + temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16) + OUTPUT_EARLY_CLOBBER_REGS_17() + : [a]"r"(a), [b]"r"(b), [w]"r"(w) + : "memory", "hi", "lo" + ); + return abs(temp3 - temp17) >> 5; +} + +static int Disto16x16_MIPSdspR2(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_MIPSdspR2(a + x + y, b + x + y, w); + } + } + return D; +} + +//------------------------------------------------------------------------------ +// Intra predictions + +#define FILL_PART(J, SIZE) \ + "usw %[value], 0+" #J "*" XSTR(BPS) "(%[dst]) \n\t" \ + "usw %[value], 4+" #J "*" XSTR(BPS) "(%[dst]) \n\t" \ + ".if " #SIZE " == 16 \n\t" \ + "usw %[value], 8+" #J "*" XSTR(BPS) "(%[dst]) \n\t" \ + "usw %[value], 12+" #J "*" XSTR(BPS) "(%[dst]) \n\t" \ + ".endif \n\t" + +#define FILL_8_OR_16(DST, VALUE, SIZE) do { \ + int value = (VALUE); \ + __asm__ volatile ( \ + "replv.qb %[value], %[value] \n\t" \ + FILL_PART( 0, SIZE) \ + FILL_PART( 1, SIZE) \ + FILL_PART( 2, SIZE) \ + FILL_PART( 3, SIZE) \ + FILL_PART( 4, SIZE) \ + FILL_PART( 5, SIZE) \ + FILL_PART( 6, SIZE) \ + FILL_PART( 7, SIZE) \ + ".if " #SIZE " == 16 \n\t" \ + FILL_PART( 8, 16) \ + FILL_PART( 9, 16) \ + FILL_PART(10, 16) \ + FILL_PART(11, 16) \ + FILL_PART(12, 16) \ + FILL_PART(13, 16) \ + FILL_PART(14, 16) \ + FILL_PART(15, 16) \ + ".endif \n\t" \ + : [value]"+&r"(value) \ + : [dst]"r"((DST)) \ + : "memory" \ + ); \ +} while (0) + +#define VERTICAL_PRED(DST, TOP, SIZE) \ +static WEBP_INLINE void VerticalPred##SIZE(uint8_t* (DST), \ + const uint8_t* (TOP)) { \ + int j; \ + if ((TOP)) { \ + for (j = 0; j < (SIZE); ++j) memcpy((DST) + j * BPS, (TOP), (SIZE)); \ + } else { \ + FILL_8_OR_16((DST), 127, (SIZE)); \ + } \ +} + +VERTICAL_PRED(dst, top, 8) +VERTICAL_PRED(dst, top, 16) + +#undef VERTICAL_PRED + +#define HORIZONTAL_PRED(DST, LEFT, SIZE) \ +static WEBP_INLINE void HorizontalPred##SIZE(uint8_t* (DST), \ + const uint8_t* (LEFT)) { \ + if (LEFT) { \ + int j; \ + for (j = 0; j < (SIZE); ++j) { \ + memset((DST) + j * BPS, (LEFT)[j], (SIZE)); \ + } \ + } else { \ + FILL_8_OR_16((DST), 129, (SIZE)); \ + } \ +} + +HORIZONTAL_PRED(dst, left, 8) +HORIZONTAL_PRED(dst, left, 16) + +#undef HORIZONTAL_PRED + +#define CLIPPING() \ + "preceu.ph.qbl %[temp2], %[temp0] \n\t" \ + "preceu.ph.qbr %[temp0], %[temp0] \n\t" \ + "preceu.ph.qbl %[temp3], %[temp1] \n\t" \ + "preceu.ph.qbr %[temp1], %[temp1] \n\t" \ + "addu.ph %[temp2], %[temp2], %[leftY_1] \n\t" \ + "addu.ph %[temp0], %[temp0], %[leftY_1] \n\t" \ + "addu.ph %[temp3], %[temp3], %[leftY_1] \n\t" \ + "addu.ph %[temp1], %[temp1], %[leftY_1] \n\t" \ + "shll_s.ph %[temp2], %[temp2], 7 \n\t" \ + "shll_s.ph %[temp0], %[temp0], 7 \n\t" \ + "shll_s.ph %[temp3], %[temp3], 7 \n\t" \ + "shll_s.ph %[temp1], %[temp1], 7 \n\t" \ + "precrqu_s.qb.ph %[temp0], %[temp2], %[temp0] \n\t" \ + "precrqu_s.qb.ph %[temp1], %[temp3], %[temp1] \n\t" + +#define CLIP_8B_TO_DST(DST, LEFT, TOP, SIZE) do { \ + int leftY_1 = ((int)(LEFT)[y] << 16) + (LEFT)[y]; \ + int temp0, temp1, temp2, temp3; \ + __asm__ volatile ( \ + "replv.ph %[leftY_1], %[leftY_1] \n\t" \ + "ulw %[temp0], 0(%[top]) \n\t" \ + "ulw %[temp1], 4(%[top]) \n\t" \ + "subu.ph %[leftY_1], %[leftY_1], %[left_1] \n\t" \ + CLIPPING() \ + "usw %[temp0], 0(%[dst]) \n\t" \ + "usw %[temp1], 4(%[dst]) \n\t" \ + ".if " #SIZE " == 16 \n\t" \ + "ulw %[temp0], 8(%[top]) \n\t" \ + "ulw %[temp1], 12(%[top]) \n\t" \ + CLIPPING() \ + "usw %[temp0], 8(%[dst]) \n\t" \ + "usw %[temp1], 12(%[dst]) \n\t" \ + ".endif \n\t" \ + : [leftY_1]"+&r"(leftY_1), [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), \ + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3) \ + : [left_1]"r"(left_1), [top]"r"((TOP)), [dst]"r"((DST)) \ + : "memory" \ + ); \ +} while (0) + +#define CLIP_TO_DST(DST, LEFT, TOP, SIZE) do { \ + int y; \ + const int left_1 = ((int)(LEFT)[-1] << 16) + (LEFT)[-1]; \ + for (y = 0; y < (SIZE); ++y) { \ + CLIP_8B_TO_DST((DST), (LEFT), (TOP), (SIZE)); \ + (DST) += BPS; \ + } \ +} while (0) + +#define TRUE_MOTION(DST, LEFT, TOP, SIZE) \ +static WEBP_INLINE void TrueMotion##SIZE(uint8_t* (DST), const uint8_t* (LEFT),\ + const uint8_t* (TOP)) { \ + if ((LEFT) != NULL) { \ + if ((TOP) != NULL) { \ + CLIP_TO_DST((DST), (LEFT), (TOP), (SIZE)); \ + } else { \ + HorizontalPred##SIZE((DST), (LEFT)); \ + } \ + } else { \ + /* true motion without left samples (hence: with default 129 value) */ \ + /* is equivalent to VE prediction where you just copy the top samples. */ \ + /* Note that if top samples are not available, the default value is */ \ + /* then 129, and not 127 as in the VerticalPred case. */ \ + if ((TOP) != NULL) { \ + VerticalPred##SIZE((DST), (TOP)); \ + } else { \ + FILL_8_OR_16((DST), 129, (SIZE)); \ + } \ + } \ +} + +TRUE_MOTION(dst, left, top, 8) +TRUE_MOTION(dst, left, top, 16) + +#undef TRUE_MOTION +#undef CLIP_TO_DST +#undef CLIP_8B_TO_DST +#undef CLIPPING + +static WEBP_INLINE void DCMode16(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + int DC, DC1; + int temp0, temp1, temp2, temp3; + + __asm__ volatile( + "beqz %[top], 2f \n\t" + LOAD_WITH_OFFSET_X4(temp0, temp1, temp2, temp3, top, + 0, 4, 8, 12, + 0, 0, 0, 0, + 0) + "raddu.w.qb %[temp0], %[temp0] \n\t" + "raddu.w.qb %[temp1], %[temp1] \n\t" + "raddu.w.qb %[temp2], %[temp2] \n\t" + "raddu.w.qb %[temp3], %[temp3] \n\t" + "addu %[temp0], %[temp0], %[temp1] \n\t" + "addu %[temp2], %[temp2], %[temp3] \n\t" + "addu %[DC], %[temp0], %[temp2] \n\t" + "move %[DC1], %[DC] \n\t" + "beqz %[left], 1f \n\t" + LOAD_WITH_OFFSET_X4(temp0, temp1, temp2, temp3, left, + 0, 4, 8, 12, + 0, 0, 0, 0, + 0) + "raddu.w.qb %[temp0], %[temp0] \n\t" + "raddu.w.qb %[temp1], %[temp1] \n\t" + "raddu.w.qb %[temp2], %[temp2] \n\t" + "raddu.w.qb %[temp3], %[temp3] \n\t" + "addu %[temp0], %[temp0], %[temp1] \n\t" + "addu %[temp2], %[temp2], %[temp3] \n\t" + "addu %[DC1], %[temp0], %[temp2] \n\t" + "1: \n\t" + "addu %[DC], %[DC], %[DC1] \n\t" + "j 3f \n\t" + "2: \n\t" + "beqz %[left], 4f \n\t" + LOAD_WITH_OFFSET_X4(temp0, temp1, temp2, temp3, left, + 0, 4, 8, 12, + 0, 0, 0, 0, + 0) + "raddu.w.qb %[temp0], %[temp0] \n\t" + "raddu.w.qb %[temp1], %[temp1] \n\t" + "raddu.w.qb %[temp2], %[temp2] \n\t" + "raddu.w.qb %[temp3], %[temp3] \n\t" + "addu %[temp0], %[temp0], %[temp1] \n\t" + "addu %[temp2], %[temp2], %[temp3] \n\t" + "addu %[DC], %[temp0], %[temp2] \n\t" + "addu %[DC], %[DC], %[DC] \n\t" + "3: \n\t" + "shra_r.w %[DC], %[DC], 5 \n\t" + "j 5f \n\t" + "4: \n\t" + "li %[DC], 0x80 \n\t" + "5: \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [DC]"=&r"(DC), + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [DC1]"=&r"(DC1) + : [left]"r"(left), [top]"r"(top) + : "memory" + ); + + FILL_8_OR_16(dst, DC, 16); +} + +static WEBP_INLINE void DCMode8(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + int DC, DC1; + int temp0, temp1, temp2, temp3; + + __asm__ volatile( + "beqz %[top], 2f \n\t" + "ulw %[temp0], 0(%[top]) \n\t" + "ulw %[temp1], 4(%[top]) \n\t" + "raddu.w.qb %[temp0], %[temp0] \n\t" + "raddu.w.qb %[temp1], %[temp1] \n\t" + "addu %[DC], %[temp0], %[temp1] \n\t" + "move %[DC1], %[DC] \n\t" + "beqz %[left], 1f \n\t" + "ulw %[temp2], 0(%[left]) \n\t" + "ulw %[temp3], 4(%[left]) \n\t" + "raddu.w.qb %[temp2], %[temp2] \n\t" + "raddu.w.qb %[temp3], %[temp3] \n\t" + "addu %[DC1], %[temp2], %[temp3] \n\t" + "1: \n\t" + "addu %[DC], %[DC], %[DC1] \n\t" + "j 3f \n\t" + "2: \n\t" + "beqz %[left], 4f \n\t" + "ulw %[temp2], 0(%[left]) \n\t" + "ulw %[temp3], 4(%[left]) \n\t" + "raddu.w.qb %[temp2], %[temp2] \n\t" + "raddu.w.qb %[temp3], %[temp3] \n\t" + "addu %[DC], %[temp2], %[temp3] \n\t" + "addu %[DC], %[DC], %[DC] \n\t" + "3: \n\t" + "shra_r.w %[DC], %[DC], 4 \n\t" + "j 5f \n\t" + "4: \n\t" + "li %[DC], 0x80 \n\t" + "5: \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [DC]"=&r"(DC), + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [DC1]"=&r"(DC1) + : [left]"r"(left), [top]"r"(top) + : "memory" + ); + + FILL_8_OR_16(dst, DC, 8); +} + +static void DC4(uint8_t* dst, const uint8_t* top) { + int temp0, temp1; + __asm__ volatile( + "ulw %[temp0], 0(%[top]) \n\t" + "ulw %[temp1], -5(%[top]) \n\t" + "raddu.w.qb %[temp0], %[temp0] \n\t" + "raddu.w.qb %[temp1], %[temp1] \n\t" + "addu %[temp0], %[temp0], %[temp1] \n\t" + "addiu %[temp0], %[temp0], 4 \n\t" + "srl %[temp0], %[temp0], 3 \n\t" + "replv.qb %[temp0], %[temp0] \n\t" + "usw %[temp0], 0*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp0], 1*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp0], 2*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp0], 3*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void TM4(uint8_t* dst, const uint8_t* top) { + int a10, a32, temp0, temp1, temp2, temp3, temp4, temp5; + const int c35 = 0xff00ff; + __asm__ volatile ( + "lbu %[temp1], 0(%[top]) \n\t" + "lbu %[a10], 1(%[top]) \n\t" + "lbu %[temp2], 2(%[top]) \n\t" + "lbu %[a32], 3(%[top]) \n\t" + "ulw %[temp0], -5(%[top]) \n\t" + "lbu %[temp4], -1(%[top]) \n\t" + "append %[a10], %[temp1], 16 \n\t" + "append %[a32], %[temp2], 16 \n\t" + "replv.ph %[temp4], %[temp4] \n\t" + "shrl.ph %[temp1], %[temp0], 8 \n\t" + "and %[temp0], %[temp0], %[c35] \n\t" + "subu.ph %[temp1], %[temp1], %[temp4] \n\t" + "subu.ph %[temp0], %[temp0], %[temp4] \n\t" + "srl %[temp2], %[temp1], 16 \n\t" + "srl %[temp3], %[temp0], 16 \n\t" + "replv.ph %[temp2], %[temp2] \n\t" + "replv.ph %[temp3], %[temp3] \n\t" + "replv.ph %[temp4], %[temp1] \n\t" + "replv.ph %[temp5], %[temp0] \n\t" + "addu.ph %[temp0], %[temp3], %[a10] \n\t" + "addu.ph %[temp1], %[temp3], %[a32] \n\t" + "addu.ph %[temp3], %[temp2], %[a10] \n\t" + "addu.ph %[temp2], %[temp2], %[a32] \n\t" + "shll_s.ph %[temp0], %[temp0], 7 \n\t" + "shll_s.ph %[temp1], %[temp1], 7 \n\t" + "shll_s.ph %[temp3], %[temp3], 7 \n\t" + "shll_s.ph %[temp2], %[temp2], 7 \n\t" + "precrqu_s.qb.ph %[temp0], %[temp1], %[temp0] \n\t" + "precrqu_s.qb.ph %[temp1], %[temp2], %[temp3] \n\t" + "addu.ph %[temp2], %[temp5], %[a10] \n\t" + "addu.ph %[temp3], %[temp5], %[a32] \n\t" + "addu.ph %[temp5], %[temp4], %[a10] \n\t" + "addu.ph %[temp4], %[temp4], %[a32] \n\t" + "shll_s.ph %[temp2], %[temp2], 7 \n\t" + "shll_s.ph %[temp3], %[temp3], 7 \n\t" + "shll_s.ph %[temp4], %[temp4], 7 \n\t" + "shll_s.ph %[temp5], %[temp5], 7 \n\t" + "precrqu_s.qb.ph %[temp2], %[temp3], %[temp2] \n\t" + "precrqu_s.qb.ph %[temp3], %[temp4], %[temp5] \n\t" + "usw %[temp1], 0*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp0], 1*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp3], 2*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp2], 3*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [a10]"=&r"(a10), [a32]"=&r"(a32) + : [c35]"r"(c35), [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void VE4(uint8_t* dst, const uint8_t* top) { + int temp0, temp1, temp2, temp3, temp4, temp5, temp6; + __asm__ volatile( + "ulw %[temp0], -1(%[top]) \n\t" + "ulh %[temp1], 3(%[top]) \n\t" + "preceu.ph.qbr %[temp2], %[temp0] \n\t" + "preceu.ph.qbl %[temp3], %[temp0] \n\t" + "preceu.ph.qbr %[temp4], %[temp1] \n\t" + "packrl.ph %[temp5], %[temp3], %[temp2] \n\t" + "packrl.ph %[temp6], %[temp4], %[temp3] \n\t" + "shll.ph %[temp5], %[temp5], 1 \n\t" + "shll.ph %[temp6], %[temp6], 1 \n\t" + "addq.ph %[temp2], %[temp5], %[temp2] \n\t" + "addq.ph %[temp6], %[temp6], %[temp4] \n\t" + "addq.ph %[temp2], %[temp2], %[temp3] \n\t" + "addq.ph %[temp6], %[temp6], %[temp3] \n\t" + "shra_r.ph %[temp2], %[temp2], 2 \n\t" + "shra_r.ph %[temp6], %[temp6], 2 \n\t" + "precr.qb.ph %[temp4], %[temp6], %[temp2] \n\t" + "usw %[temp4], 0*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp4], 1*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp4], 2*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp4], 3*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void HE4(uint8_t* dst, const uint8_t* top) { + int temp0, temp1, temp2, temp3, temp4, temp5, temp6; + __asm__ volatile( + "ulw %[temp0], -4(%[top]) \n\t" + "lbu %[temp1], -5(%[top]) \n\t" + "preceu.ph.qbr %[temp2], %[temp0] \n\t" + "preceu.ph.qbl %[temp3], %[temp0] \n\t" + "replv.ph %[temp4], %[temp1] \n\t" + "packrl.ph %[temp5], %[temp3], %[temp2] \n\t" + "packrl.ph %[temp6], %[temp2], %[temp4] \n\t" + "shll.ph %[temp5], %[temp5], 1 \n\t" + "shll.ph %[temp6], %[temp6], 1 \n\t" + "addq.ph %[temp3], %[temp3], %[temp5] \n\t" + "addq.ph %[temp3], %[temp3], %[temp2] \n\t" + "addq.ph %[temp2], %[temp2], %[temp6] \n\t" + "addq.ph %[temp2], %[temp2], %[temp4] \n\t" + "shra_r.ph %[temp3], %[temp3], 2 \n\t" + "shra_r.ph %[temp2], %[temp2], 2 \n\t" + "replv.qb %[temp0], %[temp3] \n\t" + "replv.qb %[temp1], %[temp2] \n\t" + "srl %[temp3], %[temp3], 16 \n\t" + "srl %[temp2], %[temp2], 16 \n\t" + "replv.qb %[temp3], %[temp3] \n\t" + "replv.qb %[temp2], %[temp2] \n\t" + "usw %[temp3], 0*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp0], 1*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp2], 2*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp1], 3*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void RD4(uint8_t* dst, const uint8_t* top) { + int temp0, temp1, temp2, temp3, temp4, temp5; + int temp6, temp7, temp8, temp9, temp10, temp11; + __asm__ volatile( + "ulw %[temp0], -5(%[top]) \n\t" + "ulw %[temp1], -1(%[top]) \n\t" + "preceu.ph.qbl %[temp2], %[temp0] \n\t" + "preceu.ph.qbr %[temp3], %[temp0] \n\t" + "preceu.ph.qbr %[temp4], %[temp1] \n\t" + "preceu.ph.qbl %[temp5], %[temp1] \n\t" + "packrl.ph %[temp6], %[temp2], %[temp3] \n\t" + "packrl.ph %[temp7], %[temp4], %[temp2] \n\t" + "packrl.ph %[temp8], %[temp5], %[temp4] \n\t" + "shll.ph %[temp6], %[temp6], 1 \n\t" + "addq.ph %[temp9], %[temp2], %[temp6] \n\t" + "shll.ph %[temp7], %[temp7], 1 \n\t" + "addq.ph %[temp9], %[temp9], %[temp3] \n\t" + "shll.ph %[temp8], %[temp8], 1 \n\t" + "shra_r.ph %[temp9], %[temp9], 2 \n\t" + "addq.ph %[temp10], %[temp4], %[temp7] \n\t" + "addq.ph %[temp11], %[temp5], %[temp8] \n\t" + "addq.ph %[temp10], %[temp10], %[temp2] \n\t" + "addq.ph %[temp11], %[temp11], %[temp4] \n\t" + "shra_r.ph %[temp10], %[temp10], 2 \n\t" + "shra_r.ph %[temp11], %[temp11], 2 \n\t" + "lbu %[temp0], 3(%[top]) \n\t" + "lbu %[temp1], 2(%[top]) \n\t" + "lbu %[temp2], 1(%[top]) \n\t" + "sll %[temp1], %[temp1], 1 \n\t" + "addu %[temp0], %[temp0], %[temp1] \n\t" + "addu %[temp0], %[temp0], %[temp2] \n\t" + "precr.qb.ph %[temp9], %[temp10], %[temp9] \n\t" + "shra_r.w %[temp0], %[temp0], 2 \n\t" + "precr.qb.ph %[temp10], %[temp11], %[temp10] \n\t" + "usw %[temp9], 3*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp10], 1*" XSTR(BPS) "(%[dst]) \n\t" + "prepend %[temp9], %[temp11], 8 \n\t" + "prepend %[temp10], %[temp0], 8 \n\t" + "usw %[temp9], 2*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp10], 0*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void VR4(uint8_t* dst, const uint8_t* top) { + int temp0, temp1, temp2, temp3, temp4; + int temp5, temp6, temp7, temp8, temp9; + __asm__ volatile ( + "ulw %[temp0], -4(%[top]) \n\t" + "ulw %[temp1], 0(%[top]) \n\t" + "preceu.ph.qbl %[temp2], %[temp0] \n\t" + "preceu.ph.qbr %[temp0], %[temp0] \n\t" + "preceu.ph.qbla %[temp3], %[temp1] \n\t" + "preceu.ph.qbra %[temp1], %[temp1] \n\t" + "packrl.ph %[temp7], %[temp3], %[temp2] \n\t" + "addqh_r.ph %[temp4], %[temp1], %[temp3] \n\t" + "move %[temp6], %[temp1] \n\t" + "append %[temp1], %[temp2], 16 \n\t" + "shll.ph %[temp9], %[temp6], 1 \n\t" + "addqh_r.ph %[temp5], %[temp7], %[temp6] \n\t" + "shll.ph %[temp8], %[temp7], 1 \n\t" + "addu.ph %[temp3], %[temp7], %[temp3] \n\t" + "addu.ph %[temp1], %[temp1], %[temp6] \n\t" + "packrl.ph %[temp7], %[temp2], %[temp0] \n\t" + "addu.ph %[temp6], %[temp0], %[temp2] \n\t" + "addu.ph %[temp3], %[temp3], %[temp9] \n\t" + "addu.ph %[temp1], %[temp1], %[temp8] \n\t" + "shll.ph %[temp7], %[temp7], 1 \n\t" + "shra_r.ph %[temp3], %[temp3], 2 \n\t" + "shra_r.ph %[temp1], %[temp1], 2 \n\t" + "addu.ph %[temp6], %[temp6], %[temp7] \n\t" + "shra_r.ph %[temp6], %[temp6], 2 \n\t" + "precrq.ph.w %[temp8], %[temp4], %[temp5] \n\t" + "append %[temp4], %[temp5], 16 \n\t" + "precrq.ph.w %[temp2], %[temp3], %[temp1] \n\t" + "append %[temp3], %[temp1], 16 \n\t" + "precr.qb.ph %[temp8], %[temp8], %[temp4] \n\t" + "precr.qb.ph %[temp3], %[temp2], %[temp3] \n\t" + "usw %[temp8], 0*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp3], 1*" XSTR(BPS) "(%[dst]) \n\t" + "append %[temp3], %[temp6], 8 \n\t" + "srl %[temp6], %[temp6], 16 \n\t" + "append %[temp8], %[temp6], 8 \n\t" + "usw %[temp3], 3*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp8], 2*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void LD4(uint8_t* dst, const uint8_t* top) { + int temp0, temp1, temp2, temp3, temp4, temp5; + int temp6, temp7, temp8, temp9, temp10, temp11; + __asm__ volatile( + "ulw %[temp0], 0(%[top]) \n\t" + "ulw %[temp1], 4(%[top]) \n\t" + "preceu.ph.qbl %[temp2], %[temp0] \n\t" + "preceu.ph.qbr %[temp3], %[temp0] \n\t" + "preceu.ph.qbr %[temp4], %[temp1] \n\t" + "preceu.ph.qbl %[temp5], %[temp1] \n\t" + "packrl.ph %[temp6], %[temp2], %[temp3] \n\t" + "packrl.ph %[temp7], %[temp4], %[temp2] \n\t" + "packrl.ph %[temp8], %[temp5], %[temp4] \n\t" + "shll.ph %[temp6], %[temp6], 1 \n\t" + "addq.ph %[temp9], %[temp2], %[temp6] \n\t" + "shll.ph %[temp7], %[temp7], 1 \n\t" + "addq.ph %[temp9], %[temp9], %[temp3] \n\t" + "shll.ph %[temp8], %[temp8], 1 \n\t" + "shra_r.ph %[temp9], %[temp9], 2 \n\t" + "addq.ph %[temp10], %[temp4], %[temp7] \n\t" + "addq.ph %[temp11], %[temp5], %[temp8] \n\t" + "addq.ph %[temp10], %[temp10], %[temp2] \n\t" + "addq.ph %[temp11], %[temp11], %[temp4] \n\t" + "shra_r.ph %[temp10], %[temp10], 2 \n\t" + "shra_r.ph %[temp11], %[temp11], 2 \n\t" + "srl %[temp1], %[temp1], 24 \n\t" + "sll %[temp1], %[temp1], 1 \n\t" + "raddu.w.qb %[temp5], %[temp5] \n\t" + "precr.qb.ph %[temp9], %[temp10], %[temp9] \n\t" + "precr.qb.ph %[temp10], %[temp11], %[temp10] \n\t" + "addu %[temp1], %[temp1], %[temp5] \n\t" + "shra_r.w %[temp1], %[temp1], 2 \n\t" + "usw %[temp9], 0*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp10], 2*" XSTR(BPS) "(%[dst]) \n\t" + "prepend %[temp9], %[temp11], 8 \n\t" + "prepend %[temp10], %[temp1], 8 \n\t" + "usw %[temp9], 1*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp10], 3*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void VL4(uint8_t* dst, const uint8_t* top) { + int temp0, temp1, temp2, temp3, temp4; + int temp5, temp6, temp7, temp8, temp9; + __asm__ volatile ( + "ulw %[temp0], 0(%[top]) \n\t" + "ulw %[temp1], 4(%[top]) \n\t" + "preceu.ph.qbla %[temp2], %[temp0] \n\t" + "preceu.ph.qbra %[temp0], %[temp0] \n\t" + "preceu.ph.qbl %[temp3], %[temp1] \n\t" + "preceu.ph.qbr %[temp1], %[temp1] \n\t" + "addqh_r.ph %[temp4], %[temp0], %[temp2] \n\t" + "packrl.ph %[temp7], %[temp1], %[temp0] \n\t" + "precrq.ph.w %[temp6], %[temp1], %[temp2] \n\t" + "shll.ph %[temp9], %[temp2], 1 \n\t" + "addqh_r.ph %[temp5], %[temp7], %[temp2] \n\t" + "shll.ph %[temp8], %[temp7], 1 \n\t" + "addu.ph %[temp2], %[temp2], %[temp6] \n\t" + "addu.ph %[temp0], %[temp0], %[temp7] \n\t" + "packrl.ph %[temp7], %[temp3], %[temp1] \n\t" + "addu.ph %[temp6], %[temp1], %[temp3] \n\t" + "addu.ph %[temp2], %[temp2], %[temp8] \n\t" + "addu.ph %[temp0], %[temp0], %[temp9] \n\t" + "shll.ph %[temp7], %[temp7], 1 \n\t" + "shra_r.ph %[temp2], %[temp2], 2 \n\t" + "shra_r.ph %[temp0], %[temp0], 2 \n\t" + "addu.ph %[temp6], %[temp6], %[temp7] \n\t" + "shra_r.ph %[temp6], %[temp6], 2 \n\t" + "precrq.ph.w %[temp8], %[temp5], %[temp4] \n\t" + "append %[temp5], %[temp4], 16 \n\t" + "precrq.ph.w %[temp3], %[temp2], %[temp0] \n\t" + "append %[temp2], %[temp0], 16 \n\t" + "precr.qb.ph %[temp8], %[temp8], %[temp5] \n\t" + "precr.qb.ph %[temp3], %[temp3], %[temp2] \n\t" + "usw %[temp8], 0*" XSTR(BPS) "(%[dst]) \n\t" + "prepend %[temp8], %[temp6], 8 \n\t" + "usw %[temp3], 1*" XSTR(BPS) "(%[dst]) \n\t" + "srl %[temp6], %[temp6], 16 \n\t" + "prepend %[temp3], %[temp6], 8 \n\t" + "usw %[temp8], 2*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp3], 3*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void HD4(uint8_t* dst, const uint8_t* top) { + int temp0, temp1, temp2, temp3, temp4; + int temp5, temp6, temp7, temp8, temp9; + __asm__ volatile ( + "ulw %[temp0], -5(%[top]) \n\t" + "ulw %[temp1], -1(%[top]) \n\t" + "preceu.ph.qbla %[temp2], %[temp0] \n\t" + "preceu.ph.qbra %[temp0], %[temp0] \n\t" + "preceu.ph.qbl %[temp3], %[temp1] \n\t" + "preceu.ph.qbr %[temp1], %[temp1] \n\t" + "addqh_r.ph %[temp4], %[temp0], %[temp2] \n\t" + "packrl.ph %[temp7], %[temp1], %[temp0] \n\t" + "precrq.ph.w %[temp6], %[temp1], %[temp2] \n\t" + "shll.ph %[temp9], %[temp2], 1 \n\t" + "addqh_r.ph %[temp5], %[temp7], %[temp2] \n\t" + "shll.ph %[temp8], %[temp7], 1 \n\t" + "addu.ph %[temp2], %[temp2], %[temp6] \n\t" + "addu.ph %[temp0], %[temp0], %[temp7] \n\t" + "packrl.ph %[temp7], %[temp3], %[temp1] \n\t" + "addu.ph %[temp6], %[temp1], %[temp3] \n\t" + "addu.ph %[temp2], %[temp2], %[temp8] \n\t" + "addu.ph %[temp0], %[temp0], %[temp9] \n\t" + "shll.ph %[temp7], %[temp7], 1 \n\t" + "shra_r.ph %[temp2], %[temp2], 2 \n\t" + "shra_r.ph %[temp0], %[temp0], 2 \n\t" + "addu.ph %[temp6], %[temp6], %[temp7] \n\t" + "shra_r.ph %[temp6], %[temp6], 2 \n\t" + "precrq.ph.w %[temp1], %[temp2], %[temp5] \n\t" + "precrq.ph.w %[temp3], %[temp0], %[temp4] \n\t" + "precr.qb.ph %[temp7], %[temp6], %[temp1] \n\t" + "precr.qb.ph %[temp6], %[temp1], %[temp3] \n\t" + "usw %[temp7], 0*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp6], 1*" XSTR(BPS) "(%[dst]) \n\t" + "append %[temp2], %[temp5], 16 \n\t" + "append %[temp0], %[temp4], 16 \n\t" + "precr.qb.ph %[temp5], %[temp3], %[temp2] \n\t" + "precr.qb.ph %[temp4], %[temp2], %[temp0] \n\t" + "usw %[temp5], 2*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp4], 3*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +static void HU4(uint8_t* dst, const uint8_t* top) { + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + __asm__ volatile ( + "ulw %[temp0], -5(%[top]) \n\t" + "preceu.ph.qbl %[temp1], %[temp0] \n\t" + "preceu.ph.qbr %[temp2], %[temp0] \n\t" + "packrl.ph %[temp3], %[temp1], %[temp2] \n\t" + "replv.qb %[temp7], %[temp2] \n\t" + "addqh_r.ph %[temp4], %[temp1], %[temp3] \n\t" + "addqh_r.ph %[temp5], %[temp3], %[temp2] \n\t" + "shll.ph %[temp6], %[temp3], 1 \n\t" + "addu.ph %[temp3], %[temp2], %[temp3] \n\t" + "addu.ph %[temp6], %[temp1], %[temp6] \n\t" + "shll.ph %[temp0], %[temp2], 1 \n\t" + "addu.ph %[temp6], %[temp6], %[temp2] \n\t" + "addu.ph %[temp0], %[temp3], %[temp0] \n\t" + "shra_r.ph %[temp6], %[temp6], 2 \n\t" + "shra_r.ph %[temp0], %[temp0], 2 \n\t" + "packrl.ph %[temp3], %[temp6], %[temp5] \n\t" + "precrq.ph.w %[temp2], %[temp6], %[temp4] \n\t" + "append %[temp0], %[temp5], 16 \n\t" + "precr.qb.ph %[temp3], %[temp3], %[temp2] \n\t" + "usw %[temp3], 0*" XSTR(BPS) "(%[dst]) \n\t" + "precr.qb.ph %[temp1], %[temp7], %[temp0] \n\t" + "usw %[temp7], 3*" XSTR(BPS) "(%[dst]) \n\t" + "packrl.ph %[temp2], %[temp1], %[temp3] \n\t" + "usw %[temp1], 2*" XSTR(BPS) "(%[dst]) \n\t" + "usw %[temp2], 1*" XSTR(BPS) "(%[dst]) \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7) + : [top]"r"(top), [dst]"r"(dst) + : "memory" + ); +} + +//------------------------------------------------------------------------------ +// Chroma 8x8 prediction (paragraph 12.2) + +static void IntraChromaPreds_MIPSdspR2(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + // U block + DCMode8(C8DC8 + dst, left, top); + VerticalPred8(C8VE8 + dst, top); + HorizontalPred8(C8HE8 + dst, left); + TrueMotion8(C8TM8 + dst, left, top); + // V block + dst += 8; + if (top) top += 8; + if (left) left += 16; + DCMode8(C8DC8 + dst, left, top); + VerticalPred8(C8VE8 + dst, top); + HorizontalPred8(C8HE8 + dst, left); + TrueMotion8(C8TM8 + dst, left, top); +} + +//------------------------------------------------------------------------------ +// luma 16x16 prediction (paragraph 12.3) + +static void Intra16Preds_MIPSdspR2(uint8_t* dst, + const uint8_t* left, const uint8_t* top) { + DCMode16(I16DC16 + dst, left, top); + VerticalPred16(I16VE16 + dst, top); + HorizontalPred16(I16HE16 + dst, left); + TrueMotion16(I16TM16 + dst, left, top); +} + +// Left samples are top[-5 .. -2], top_left is top[-1], top are +// located at top[0..3], and top right is top[4..7] +static void Intra4Preds_MIPSdspR2(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); +} + +//------------------------------------------------------------------------------ +// Metric + +#if !defined(WORK_AROUND_GCC) + +#define GET_SSE_INNER(A) \ + "lw %[temp0], " #A "(%[a]) \n\t" \ + "lw %[temp1], " #A "(%[b]) \n\t" \ + "preceu.ph.qbr %[temp2], %[temp0] \n\t" \ + "preceu.ph.qbl %[temp0], %[temp0] \n\t" \ + "preceu.ph.qbr %[temp3], %[temp1] \n\t" \ + "preceu.ph.qbl %[temp1], %[temp1] \n\t" \ + "subq.ph %[temp2], %[temp2], %[temp3] \n\t" \ + "subq.ph %[temp0], %[temp0], %[temp1] \n\t" \ + "dpa.w.ph $ac0, %[temp2], %[temp2] \n\t" \ + "dpa.w.ph $ac0, %[temp0], %[temp0] \n\t" + +#define GET_SSE(A, B, C, D) \ + GET_SSE_INNER(A) \ + GET_SSE_INNER(B) \ + GET_SSE_INNER(C) \ + GET_SSE_INNER(D) + +static int SSE16x16_MIPSdspR2(const uint8_t* a, const uint8_t* b) { + int count; + int temp0, temp1, temp2, temp3; + __asm__ volatile ( + "mult $zero, $zero \n\t" + GET_SSE( 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS) + GET_SSE( 1 * BPS, 4 + 1 * BPS, 8 + 1 * BPS, 12 + 1 * BPS) + GET_SSE( 2 * BPS, 4 + 2 * BPS, 8 + 2 * BPS, 12 + 2 * BPS) + GET_SSE( 3 * BPS, 4 + 3 * BPS, 8 + 3 * BPS, 12 + 3 * BPS) + GET_SSE( 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS) + GET_SSE( 5 * BPS, 4 + 5 * BPS, 8 + 5 * BPS, 12 + 5 * BPS) + GET_SSE( 6 * BPS, 4 + 6 * BPS, 8 + 6 * BPS, 12 + 6 * BPS) + GET_SSE( 7 * BPS, 4 + 7 * BPS, 8 + 7 * BPS, 12 + 7 * BPS) + GET_SSE( 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS) + GET_SSE( 9 * BPS, 4 + 9 * BPS, 8 + 9 * BPS, 12 + 9 * BPS) + GET_SSE(10 * BPS, 4 + 10 * BPS, 8 + 10 * BPS, 12 + 10 * BPS) + GET_SSE(11 * BPS, 4 + 11 * BPS, 8 + 11 * BPS, 12 + 11 * BPS) + GET_SSE(12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS) + GET_SSE(13 * BPS, 4 + 13 * BPS, 8 + 13 * BPS, 12 + 13 * BPS) + GET_SSE(14 * BPS, 4 + 14 * BPS, 8 + 14 * BPS, 12 + 14 * BPS) + GET_SSE(15 * BPS, 4 + 15 * BPS, 8 + 15 * BPS, 12 + 15 * BPS) + "mflo %[count] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [count]"=&r"(count) + : [a]"r"(a), [b]"r"(b) + : "memory", "hi", "lo" + ); + return count; +} + +static int SSE16x8_MIPSdspR2(const uint8_t* a, const uint8_t* b) { + int count; + int temp0, temp1, temp2, temp3; + __asm__ volatile ( + "mult $zero, $zero \n\t" + GET_SSE( 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS) + GET_SSE( 1 * BPS, 4 + 1 * BPS, 8 + 1 * BPS, 12 + 1 * BPS) + GET_SSE( 2 * BPS, 4 + 2 * BPS, 8 + 2 * BPS, 12 + 2 * BPS) + GET_SSE( 3 * BPS, 4 + 3 * BPS, 8 + 3 * BPS, 12 + 3 * BPS) + GET_SSE( 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS) + GET_SSE( 5 * BPS, 4 + 5 * BPS, 8 + 5 * BPS, 12 + 5 * BPS) + GET_SSE( 6 * BPS, 4 + 6 * BPS, 8 + 6 * BPS, 12 + 6 * BPS) + GET_SSE( 7 * BPS, 4 + 7 * BPS, 8 + 7 * BPS, 12 + 7 * BPS) + "mflo %[count] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [count]"=&r"(count) + : [a]"r"(a), [b]"r"(b) + : "memory", "hi", "lo" + ); + return count; +} + +static int SSE8x8_MIPSdspR2(const uint8_t* a, const uint8_t* b) { + int count; + int temp0, temp1, temp2, temp3; + __asm__ volatile ( + "mult $zero, $zero \n\t" + GET_SSE(0 * BPS, 4 + 0 * BPS, 1 * BPS, 4 + 1 * BPS) + GET_SSE(2 * BPS, 4 + 2 * BPS, 3 * BPS, 4 + 3 * BPS) + GET_SSE(4 * BPS, 4 + 4 * BPS, 5 * BPS, 4 + 5 * BPS) + GET_SSE(6 * BPS, 4 + 6 * BPS, 7 * BPS, 4 + 7 * BPS) + "mflo %[count] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [count]"=&r"(count) + : [a]"r"(a), [b]"r"(b) + : "memory", "hi", "lo" + ); + return count; +} + +static int SSE4x4_MIPSdspR2(const uint8_t* a, const uint8_t* b) { + int count; + int temp0, temp1, temp2, temp3; + __asm__ volatile ( + "mult $zero, $zero \n\t" + GET_SSE(0 * BPS, 1 * BPS, 2 * BPS, 3 * BPS) + "mflo %[count] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [count]"=&r"(count) + : [a]"r"(a), [b]"r"(b) + : "memory", "hi", "lo" + ); + return count; +} + +#undef GET_SSE +#undef GET_SSE_INNER + +#endif // !WORK_AROUND_GCC + +#undef FILL_8_OR_16 +#undef FILL_PART +#undef OUTPUT_EARLY_CLOBBER_REGS_17 +#undef MUL_HALF +#undef ABS_X8 +#undef ADD_SUB_HALVES_X4 + +//------------------------------------------------------------------------------ +// Quantization +// + +// macro for one pass through for loop in QuantizeBlock reading 2 values at time +// QUANTDIV macro inlined +// J - offset in bytes (kZigzag[n] * 2) +// K - offset in bytes (kZigzag[n] * 4) +// N - offset in bytes (n * 2) +// N1 - offset in bytes ((n + 1) * 2) +#define QUANTIZE_ONE(J, K, N, N1) \ + "ulw %[temp1], " #J "(%[ppin]) \n\t" \ + "ulw %[temp2], " #J "(%[ppsharpen]) \n\t" \ + "lhu %[temp3], " #K "(%[ppzthresh]) \n\t" \ + "lhu %[temp6], " #K "+4(%[ppzthresh]) \n\t" \ + "absq_s.ph %[temp4], %[temp1] \n\t" \ + "ins %[temp3], %[temp6], 16, 16 \n\t" \ + "addu.ph %[coeff], %[temp4], %[temp2] \n\t" \ + "shra.ph %[sign], %[temp1], 15 \n\t" \ + "li %[level], 0x10001 \n\t" \ + "cmp.lt.ph %[temp3], %[coeff] \n\t" \ + "lhu %[temp1], " #J "(%[ppiq]) \n\t" \ + "pick.ph %[temp5], %[level], $0 \n\t" \ + "lw %[temp2], " #K "(%[ppbias]) \n\t" \ + "beqz %[temp5], 0f \n\t" \ + "lhu %[temp3], " #J "(%[ppq]) \n\t" \ + "beq %[temp5], %[level], 1f \n\t" \ + "andi %[temp5], %[temp5], 0x1 \n\t" \ + "andi %[temp4], %[coeff], 0xffff \n\t" \ + "beqz %[temp5], 2f \n\t" \ + "mul %[level], %[temp4], %[temp1] \n\t" \ + "sh $0, " #J "+2(%[ppin]) \n\t" \ + "sh $0, " #N1 "(%[pout]) \n\t" \ + "addu %[level], %[level], %[temp2] \n\t" \ + "sra %[level], %[level], 17 \n\t" \ + "slt %[temp4], %[max_level], %[level] \n\t" \ + "movn %[level], %[max_level], %[temp4] \n\t" \ + "andi %[temp6], %[sign], 0xffff \n\t" \ + "xor %[level], %[level], %[temp6] \n\t" \ + "subu %[level], %[level], %[temp6] \n\t" \ + "mul %[temp5], %[level], %[temp3] \n\t" \ + "or %[ret], %[ret], %[level] \n\t" \ + "sh %[level], " #N "(%[pout]) \n\t" \ + "sh %[temp5], " #J "(%[ppin]) \n\t" \ + "j 3f \n\t" \ +"2: \n\t" \ + "lhu %[temp1], " #J "+2(%[ppiq]) \n\t" \ + "srl %[temp5], %[coeff], 16 \n\t" \ + "mul %[level], %[temp5], %[temp1] \n\t" \ + "lw %[temp2], " #K "+4(%[ppbias]) \n\t" \ + "lhu %[temp3], " #J "+2(%[ppq]) \n\t" \ + "addu %[level], %[level], %[temp2] \n\t" \ + "sra %[level], %[level], 17 \n\t" \ + "srl %[temp6], %[sign], 16 \n\t" \ + "slt %[temp4], %[max_level], %[level] \n\t" \ + "movn %[level], %[max_level], %[temp4] \n\t" \ + "xor %[level], %[level], %[temp6] \n\t" \ + "subu %[level], %[level], %[temp6] \n\t" \ + "mul %[temp5], %[level], %[temp3] \n\t" \ + "sh $0, " #J "(%[ppin]) \n\t" \ + "sh $0, " #N "(%[pout]) \n\t" \ + "or %[ret], %[ret], %[level] \n\t" \ + "sh %[temp5], " #J "+2(%[ppin]) \n\t" \ + "sh %[level], " #N1 "(%[pout]) \n\t" \ + "j 3f \n\t" \ +"1: \n\t" \ + "lhu %[temp1], " #J "(%[ppiq]) \n\t" \ + "lw %[temp2], " #K "(%[ppbias]) \n\t" \ + "ulw %[temp3], " #J "(%[ppq]) \n\t" \ + "andi %[temp5], %[coeff], 0xffff \n\t" \ + "srl %[temp0], %[coeff], 16 \n\t" \ + "lhu %[temp6], " #J "+2(%[ppiq]) \n\t" \ + "lw %[coeff], " #K "+4(%[ppbias]) \n\t" \ + "mul %[level], %[temp5], %[temp1] \n\t" \ + "mul %[temp4], %[temp0], %[temp6] \n\t" \ + "addu %[level], %[level], %[temp2] \n\t" \ + "addu %[temp4], %[temp4], %[coeff] \n\t" \ + "precrq.ph.w %[level], %[temp4], %[level] \n\t" \ + "shra.ph %[level], %[level], 1 \n\t" \ + "cmp.lt.ph %[max_level1],%[level] \n\t" \ + "pick.ph %[level], %[max_level], %[level] \n\t" \ + "xor %[level], %[level], %[sign] \n\t" \ + "subu.ph %[level], %[level], %[sign] \n\t" \ + "mul.ph %[temp3], %[level], %[temp3] \n\t" \ + "or %[ret], %[ret], %[level] \n\t" \ + "sh %[level], " #N "(%[pout]) \n\t" \ + "srl %[level], %[level], 16 \n\t" \ + "sh %[level], " #N1 "(%[pout]) \n\t" \ + "usw %[temp3], " #J "(%[ppin]) \n\t" \ + "j 3f \n\t" \ +"0: \n\t" \ + "sh $0, " #N "(%[pout]) \n\t" \ + "sh $0, " #N1 "(%[pout]) \n\t" \ + "usw $0, " #J "(%[ppin]) \n\t" \ +"3: \n\t" + +static int QuantizeBlock_MIPSdspR2(int16_t in[16], int16_t out[16], + const VP8Matrix* const mtx) { + int temp0, temp1, temp2, temp3, temp4, temp5,temp6; + int sign, coeff, level; + int max_level = MAX_LEVEL; + int max_level1 = max_level << 16 | max_level; + int ret = 0; + + int16_t* ppin = &in[0]; + int16_t* pout = &out[0]; + const uint16_t* ppsharpen = &mtx->sharpen_[0]; + const uint32_t* ppzthresh = &mtx->zthresh_[0]; + const uint16_t* ppq = &mtx->q_[0]; + const uint16_t* ppiq = &mtx->iq_[0]; + const uint32_t* ppbias = &mtx->bias_[0]; + + __asm__ volatile ( + QUANTIZE_ONE( 0, 0, 0, 2) + QUANTIZE_ONE( 4, 8, 10, 12) + QUANTIZE_ONE( 8, 16, 4, 8) + QUANTIZE_ONE(12, 24, 14, 24) + QUANTIZE_ONE(16, 32, 6, 16) + QUANTIZE_ONE(20, 40, 22, 26) + QUANTIZE_ONE(24, 48, 18, 20) + QUANTIZE_ONE(28, 56, 28, 30) + + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [sign]"=&r"(sign), [coeff]"=&r"(coeff), + [level]"=&r"(level), [temp6]"=&r"(temp6), [ret]"+&r"(ret) + : [ppin]"r"(ppin), [pout]"r"(pout), [max_level1]"r"(max_level1), + [ppiq]"r"(ppiq), [max_level]"r"(max_level), + [ppbias]"r"(ppbias), [ppzthresh]"r"(ppzthresh), + [ppsharpen]"r"(ppsharpen), [ppq]"r"(ppq) + : "memory", "hi", "lo" + ); + + return (ret != 0); +} + +static int Quantize2Blocks_MIPSdspR2(int16_t in[32], int16_t out[32], + const VP8Matrix* const mtx) { + int nz; + nz = QuantizeBlock_MIPSdspR2(in + 0 * 16, out + 0 * 16, mtx) << 0; + nz |= QuantizeBlock_MIPSdspR2(in + 1 * 16, out + 1 * 16, mtx) << 1; + return nz; +} + +#undef QUANTIZE_ONE + +// macro for one horizontal pass in FTransformWHT +// temp0..temp7 holds tmp[0]..tmp[15] +// A, B, C, D - offset in bytes to load from in buffer +// TEMP0, TEMP1 - registers for corresponding tmp elements +#define HORIZONTAL_PASS_WHT(A, B, C, D, TEMP0, TEMP1) \ + "lh %[" #TEMP0 "], " #A "(%[in]) \n\t" \ + "lh %[" #TEMP1 "], " #B "(%[in]) \n\t" \ + "lh %[temp8], " #C "(%[in]) \n\t" \ + "lh %[temp9], " #D "(%[in]) \n\t" \ + "ins %[" #TEMP1 "], %[" #TEMP0 "], 16, 16 \n\t" \ + "ins %[temp9], %[temp8], 16, 16 \n\t" \ + "subq.ph %[temp8], %[" #TEMP1 "], %[temp9] \n\t" \ + "addq.ph %[temp9], %[" #TEMP1 "], %[temp9] \n\t" \ + "precrq.ph.w %[" #TEMP0 "], %[temp8], %[temp9] \n\t" \ + "append %[temp8], %[temp9], 16 \n\t" \ + "subq.ph %[" #TEMP1 "], %[" #TEMP0 "], %[temp8] \n\t" \ + "addq.ph %[" #TEMP0 "], %[" #TEMP0 "], %[temp8] \n\t" \ + "rotr %[" #TEMP1 "], %[" #TEMP1 "], 16 \n\t" + +// macro for one vertical pass in FTransformWHT +// temp0..temp7 holds tmp[0]..tmp[15] +// A, B, C, D - offsets in bytes to store to out buffer +// TEMP0, TEMP2, TEMP4 and TEMP6 - registers for corresponding tmp elements +#define VERTICAL_PASS_WHT(A, B, C, D, TEMP0, TEMP2, TEMP4, TEMP6) \ + "addq.ph %[temp8], %[" #TEMP0 "], %[" #TEMP4 "] \n\t" \ + "addq.ph %[temp9], %[" #TEMP2 "], %[" #TEMP6 "] \n\t" \ + "subq.ph %[" #TEMP2 "], %[" #TEMP2 "], %[" #TEMP6 "] \n\t" \ + "subq.ph %[" #TEMP6 "], %[" #TEMP0 "], %[" #TEMP4 "] \n\t" \ + "addqh.ph %[" #TEMP0 "], %[temp8], %[temp9] \n\t" \ + "subqh.ph %[" #TEMP4 "], %[" #TEMP6 "], %[" #TEMP2 "] \n\t" \ + "addqh.ph %[" #TEMP2 "], %[" #TEMP2 "], %[" #TEMP6 "] \n\t" \ + "subqh.ph %[" #TEMP6 "], %[temp8], %[temp9] \n\t" \ + "usw %[" #TEMP0 "], " #A "(%[out]) \n\t" \ + "usw %[" #TEMP2 "], " #B "(%[out]) \n\t" \ + "usw %[" #TEMP4 "], " #C "(%[out]) \n\t" \ + "usw %[" #TEMP6 "], " #D "(%[out]) \n\t" + +static void FTransformWHT_MIPSdspR2(const int16_t* in, int16_t* out) { + int temp0, temp1, temp2, temp3, temp4; + int temp5, temp6, temp7, temp8, temp9; + + __asm__ volatile ( + HORIZONTAL_PASS_WHT( 0, 32, 64, 96, temp0, temp1) + HORIZONTAL_PASS_WHT(128, 160, 192, 224, temp2, temp3) + HORIZONTAL_PASS_WHT(256, 288, 320, 352, temp4, temp5) + HORIZONTAL_PASS_WHT(384, 416, 448, 480, temp6, temp7) + VERTICAL_PASS_WHT(0, 8, 16, 24, temp0, temp2, temp4, temp6) + VERTICAL_PASS_WHT(4, 12, 20, 28, temp1, temp3, temp5, temp7) + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), + [temp9]"=&r"(temp9) + : [in]"r"(in), [out]"r"(out) + : "memory" + ); +} + +#undef VERTICAL_PASS_WHT +#undef HORIZONTAL_PASS_WHT + +// macro for converting coefficients to bin +// convert 8 coeffs at time +// A, B, C, D - offsets in bytes to load from out buffer +#define CONVERT_COEFFS_TO_BIN(A, B, C, D) \ + "ulw %[temp0], " #A "(%[out]) \n\t" \ + "ulw %[temp1], " #B "(%[out]) \n\t" \ + "ulw %[temp2], " #C "(%[out]) \n\t" \ + "ulw %[temp3], " #D "(%[out]) \n\t" \ + "absq_s.ph %[temp0], %[temp0] \n\t" \ + "absq_s.ph %[temp1], %[temp1] \n\t" \ + "absq_s.ph %[temp2], %[temp2] \n\t" \ + "absq_s.ph %[temp3], %[temp3] \n\t" \ + "shra.ph %[temp0], %[temp0], 3 \n\t" \ + "shra.ph %[temp1], %[temp1], 3 \n\t" \ + "shra.ph %[temp2], %[temp2], 3 \n\t" \ + "shra.ph %[temp3], %[temp3], 3 \n\t" \ + "shll_s.ph %[temp0], %[temp0], 10 \n\t" \ + "shll_s.ph %[temp1], %[temp1], 10 \n\t" \ + "shll_s.ph %[temp2], %[temp2], 10 \n\t" \ + "shll_s.ph %[temp3], %[temp3], 10 \n\t" \ + "shrl.ph %[temp0], %[temp0], 10 \n\t" \ + "shrl.ph %[temp1], %[temp1], 10 \n\t" \ + "shrl.ph %[temp2], %[temp2], 10 \n\t" \ + "shrl.ph %[temp3], %[temp3], 10 \n\t" \ + "shll.ph %[temp0], %[temp0], 2 \n\t" \ + "shll.ph %[temp1], %[temp1], 2 \n\t" \ + "shll.ph %[temp2], %[temp2], 2 \n\t" \ + "shll.ph %[temp3], %[temp3], 2 \n\t" \ + "ext %[temp4], %[temp0], 0, 16 \n\t" \ + "ext %[temp0], %[temp0], 16, 16 \n\t" \ + "addu %[temp4], %[temp4], %[dist] \n\t" \ + "addu %[temp0], %[temp0], %[dist] \n\t" \ + "ext %[temp5], %[temp1], 0, 16 \n\t" \ + "lw %[temp8], 0(%[temp4]) \n\t" \ + "ext %[temp1], %[temp1], 16, 16 \n\t" \ + "addu %[temp5], %[temp5], %[dist] \n\t" \ + "addiu %[temp8], %[temp8], 1 \n\t" \ + "sw %[temp8], 0(%[temp4]) \n\t" \ + "lw %[temp8], 0(%[temp0]) \n\t" \ + "addu %[temp1], %[temp1], %[dist] \n\t" \ + "ext %[temp6], %[temp2], 0, 16 \n\t" \ + "addiu %[temp8], %[temp8], 1 \n\t" \ + "sw %[temp8], 0(%[temp0]) \n\t" \ + "lw %[temp8], 0(%[temp5]) \n\t" \ + "ext %[temp2], %[temp2], 16, 16 \n\t" \ + "addu %[temp6], %[temp6], %[dist] \n\t" \ + "addiu %[temp8], %[temp8], 1 \n\t" \ + "sw %[temp8], 0(%[temp5]) \n\t" \ + "lw %[temp8], 0(%[temp1]) \n\t" \ + "addu %[temp2], %[temp2], %[dist] \n\t" \ + "ext %[temp7], %[temp3], 0, 16 \n\t" \ + "addiu %[temp8], %[temp8], 1 \n\t" \ + "sw %[temp8], 0(%[temp1]) \n\t" \ + "lw %[temp8], 0(%[temp6]) \n\t" \ + "ext %[temp3], %[temp3], 16, 16 \n\t" \ + "addu %[temp7], %[temp7], %[dist] \n\t" \ + "addiu %[temp8], %[temp8], 1 \n\t" \ + "sw %[temp8], 0(%[temp6]) \n\t" \ + "lw %[temp8], 0(%[temp2]) \n\t" \ + "addu %[temp3], %[temp3], %[dist] \n\t" \ + "addiu %[temp8], %[temp8], 1 \n\t" \ + "sw %[temp8], 0(%[temp2]) \n\t" \ + "lw %[temp8], 0(%[temp7]) \n\t" \ + "addiu %[temp8], %[temp8], 1 \n\t" \ + "sw %[temp8], 0(%[temp7]) \n\t" \ + "lw %[temp8], 0(%[temp3]) \n\t" \ + "addiu %[temp8], %[temp8], 1 \n\t" \ + "sw %[temp8], 0(%[temp3]) \n\t" + +static void CollectHistogram_MIPSdspR2(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 }; + const int max_coeff = (MAX_COEFF_THRESH << 16) + MAX_COEFF_THRESH; + for (j = start_block; j < end_block; ++j) { + int16_t out[16]; + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; + + VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out); + + // Convert coefficients to bin. + __asm__ volatile ( + CONVERT_COEFFS_TO_BIN( 0, 4, 8, 12) + CONVERT_COEFFS_TO_BIN(16, 20, 24, 28) + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8) + : [dist]"r"(distribution), [out]"r"(out), [max_coeff]"r"(max_coeff) + : "memory" + ); + } + VP8SetHistogramData(distribution, histo); +} + +#undef CONVERT_COEFFS_TO_BIN + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspInitMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitMIPSdspR2(void) { + VP8FTransform = FTransform_MIPSdspR2; + VP8FTransformWHT = FTransformWHT_MIPSdspR2; + VP8ITransform = ITransform_MIPSdspR2; + + VP8TDisto4x4 = Disto4x4_MIPSdspR2; + VP8TDisto16x16 = Disto16x16_MIPSdspR2; + + VP8EncPredLuma16 = Intra16Preds_MIPSdspR2; + VP8EncPredChroma8 = IntraChromaPreds_MIPSdspR2; + VP8EncPredLuma4 = Intra4Preds_MIPSdspR2; + +#if !defined(WORK_AROUND_GCC) + VP8SSE16x16 = SSE16x16_MIPSdspR2; + VP8SSE8x8 = SSE8x8_MIPSdspR2; + VP8SSE16x8 = SSE16x8_MIPSdspR2; + VP8SSE4x4 = SSE4x4_MIPSdspR2; +#endif + + VP8EncQuantizeBlock = QuantizeBlock_MIPSdspR2; + VP8EncQuantize2Blocks = Quantize2Blocks_MIPSdspR2; + + VP8CollectHistogram = CollectHistogram_MIPSdspR2; +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(VP8EncDspInitMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 diff --git a/internal/libwebp-1.2.1/src/dsp/enc_msa.c b/internal/libwebp-1.2.1/src/dsp/enc_msa.c new file mode 100644 index 0000000..6f85add --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/enc_msa.c @@ -0,0 +1,896 @@ +// 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 "src/dsp/dsp.h" + +#if defined(WEBP_USE_MSA) + +#include +#include "src/dsp/msa_macro.h" +#include "src/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_MSA(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_MSA(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 = { 0 }, src1 = { 0 }; + 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_MSA(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_MSA(const uint8_t* in, const uint16_t* w) { + int sum; + uint32_t in0_m, in1_m, in2_m, in3_m; + v16i8 src0 = { 0 }; + 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_MSA(const uint8_t* const a, const uint8_t* const b, + const uint16_t* const w) { + const int sum1 = TTransform_MSA(a, w); + const int sum2 = TTransform_MSA(b, w); + return abs(sum2 - sum1) >> 5; +} + +static int Disto16x16_MSA(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_MSA(a + x + y, b + x + y, w); + } + } + return D; +} + +//------------------------------------------------------------------------------ +// Histogram + +static void CollectHistogram_MSA(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 v16u8 A1 = { 0 }; + const uint64_t val_m = LD(top - 1); + const v16u8 A = (v16u8)__msa_insert_d((v2i64)A1, 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 v16u8 A2 = { 0 }; + const uint64_t val_m = LD(top - 5); + const v16u8 A1 = (v16u8)__msa_insert_d((v2i64)A2, 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 v16u8 A1 = { 0 }; + const uint64_t val_m = LD(top); + const v16u8 A = (v16u8)__msa_insert_d((v2i64)A1, 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_MSA(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_MSA(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 = { 0 }; + 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_MSA(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_MSA(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_MSA(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_MSA(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_MSA(const uint8_t* a, const uint8_t* b) { + uint32_t sum = 0; + uint32_t src0, src1, src2, src3, ref0, ref1, ref2, ref3; + v16u8 src = { 0 }, ref = { 0 }, 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_MSA(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(zero, tmp2, zero, 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_MSA(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_MSA; + VP8FTransform = FTransform_MSA; + VP8FTransformWHT = FTransformWHT_MSA; + + VP8TDisto4x4 = Disto4x4_MSA; + VP8TDisto16x16 = Disto16x16_MSA; + VP8CollectHistogram = CollectHistogram_MSA; + + VP8EncPredLuma4 = Intra4Preds_MSA; + VP8EncPredLuma16 = Intra16Preds_MSA; + VP8EncPredChroma8 = IntraChromaPreds_MSA; + + VP8SSE16x16 = SSE16x16_MSA; + VP8SSE16x8 = SSE16x8_MSA; + VP8SSE8x8 = SSE8x8_MSA; + VP8SSE4x4 = SSE4x4_MSA; + + VP8EncQuantizeBlock = QuantizeBlock_MSA; + VP8EncQuantize2Blocks = Quantize2Blocks_MSA; + VP8EncQuantizeBlockWHT = QuantizeBlock_MSA; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(VP8EncDspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/internal/libwebp-1.2.1/src/dsp/enc_neon.c b/internal/libwebp-1.2.1/src/dsp/enc_neon.c new file mode 100644 index 0000000..43bf124 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/enc_neon.c @@ -0,0 +1,938 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// ARM NEON version of speed-critical encoding functions. +// +// adapted from libvpx (http://www.webmproject.org/code/) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_NEON) + +#include + +#include "src/dsp/neon.h" +#include "src/enc/vp8i_enc.h" + +//------------------------------------------------------------------------------ +// Transforms (Paragraph 14.4) + +// Inverse transform. +// This code is pretty much the same as TransformOne in the dec_neon.c, except +// for subtraction to *ref. See the comments there for algorithmic explanations. + +static const int16_t kC1 = 20091; +static const int16_t kC2 = 17734; // half of kC2, actually. See comment above. + +// This code works but is *slower* than the inlined-asm version below +// (with gcc-4.6). So we disable it for now. Later, it'll be conditional to +// WEBP_USE_INTRINSICS define. +// With gcc-4.8, it's a little faster speed than inlined-assembly. +#if defined(WEBP_USE_INTRINSICS) + +// Treats 'v' as an uint8x8_t and zero extends to an int16x8_t. +static WEBP_INLINE int16x8_t ConvertU8ToS16_NEON(uint32x2_t v) { + return vreinterpretq_s16_u16(vmovl_u8(vreinterpret_u8_u32(v))); +} + +// Performs unsigned 8b saturation on 'dst01' and 'dst23' storing the result +// to the corresponding rows of 'dst'. +static WEBP_INLINE void SaturateAndStore4x4_NEON(uint8_t* const dst, + const int16x8_t dst01, + const int16x8_t dst23) { + // Unsigned saturate to 8b. + const uint8x8_t dst01_u8 = vqmovun_s16(dst01); + const uint8x8_t dst23_u8 = vqmovun_s16(dst23); + + // Store the results. + vst1_lane_u32((uint32_t*)(dst + 0 * BPS), vreinterpret_u32_u8(dst01_u8), 0); + vst1_lane_u32((uint32_t*)(dst + 1 * BPS), vreinterpret_u32_u8(dst01_u8), 1); + vst1_lane_u32((uint32_t*)(dst + 2 * BPS), vreinterpret_u32_u8(dst23_u8), 0); + vst1_lane_u32((uint32_t*)(dst + 3 * BPS), vreinterpret_u32_u8(dst23_u8), 1); +} + +static WEBP_INLINE void Add4x4_NEON(const int16x8_t row01, + const int16x8_t row23, + const uint8_t* const ref, + uint8_t* const dst) { + uint32x2_t dst01 = vdup_n_u32(0); + uint32x2_t dst23 = vdup_n_u32(0); + + // Load the source pixels. + dst01 = vld1_lane_u32((uint32_t*)(ref + 0 * BPS), dst01, 0); + dst23 = vld1_lane_u32((uint32_t*)(ref + 2 * BPS), dst23, 0); + dst01 = vld1_lane_u32((uint32_t*)(ref + 1 * BPS), dst01, 1); + dst23 = vld1_lane_u32((uint32_t*)(ref + 3 * BPS), dst23, 1); + + { + // Convert to 16b. + const int16x8_t dst01_s16 = ConvertU8ToS16_NEON(dst01); + const int16x8_t dst23_s16 = ConvertU8ToS16_NEON(dst23); + + // Descale with rounding. + const int16x8_t out01 = vrsraq_n_s16(dst01_s16, row01, 3); + const int16x8_t out23 = vrsraq_n_s16(dst23_s16, row23, 3); + // Add the inverse transform. + SaturateAndStore4x4_NEON(dst, out01, out23); + } +} + +static WEBP_INLINE void Transpose8x2_NEON(const int16x8_t in0, + const int16x8_t in1, + int16x8x2_t* const out) { + // a0 a1 a2 a3 | b0 b1 b2 b3 => a0 b0 c0 d0 | a1 b1 c1 d1 + // c0 c1 c2 c3 | d0 d1 d2 d3 a2 b2 c2 d2 | a3 b3 c3 d3 + const int16x8x2_t tmp0 = vzipq_s16(in0, in1); // a0 c0 a1 c1 a2 c2 ... + // b0 d0 b1 d1 b2 d2 ... + *out = vzipq_s16(tmp0.val[0], tmp0.val[1]); +} + +static WEBP_INLINE void TransformPass_NEON(int16x8x2_t* const rows) { + // {rows} = in0 | in4 + // in8 | in12 + // B1 = in4 | in12 + const int16x8_t B1 = + vcombine_s16(vget_high_s16(rows->val[0]), vget_high_s16(rows->val[1])); + // C0 = kC1 * in4 | kC1 * in12 + // C1 = kC2 * in4 | kC2 * in12 + const int16x8_t C0 = vsraq_n_s16(B1, vqdmulhq_n_s16(B1, kC1), 1); + const int16x8_t C1 = vqdmulhq_n_s16(B1, kC2); + const int16x4_t a = vqadd_s16(vget_low_s16(rows->val[0]), + vget_low_s16(rows->val[1])); // in0 + in8 + const int16x4_t b = vqsub_s16(vget_low_s16(rows->val[0]), + vget_low_s16(rows->val[1])); // in0 - in8 + // c = kC2 * in4 - kC1 * in12 + // d = kC1 * in4 + kC2 * in12 + const int16x4_t c = vqsub_s16(vget_low_s16(C1), vget_high_s16(C0)); + const int16x4_t d = vqadd_s16(vget_low_s16(C0), vget_high_s16(C1)); + const int16x8_t D0 = vcombine_s16(a, b); // D0 = a | b + const int16x8_t D1 = vcombine_s16(d, c); // D1 = d | c + const int16x8_t E0 = vqaddq_s16(D0, D1); // a+d | b+c + const int16x8_t E_tmp = vqsubq_s16(D0, D1); // a-d | b-c + const int16x8_t E1 = vcombine_s16(vget_high_s16(E_tmp), vget_low_s16(E_tmp)); + Transpose8x2_NEON(E0, E1, rows); +} + +static void ITransformOne_NEON(const uint8_t* ref, + const int16_t* in, uint8_t* dst) { + int16x8x2_t rows; + INIT_VECTOR2(rows, vld1q_s16(in + 0), vld1q_s16(in + 8)); + TransformPass_NEON(&rows); + TransformPass_NEON(&rows); + Add4x4_NEON(rows.val[0], rows.val[1], ref, dst); +} + +#else + +static void ITransformOne_NEON(const uint8_t* ref, + const int16_t* in, uint8_t* dst) { + const int kBPS = BPS; + const int16_t kC1C2[] = { kC1, kC2, 0, 0 }; + + __asm__ volatile ( + "vld1.16 {q1, q2}, [%[in]] \n" + "vld1.16 {d0}, [%[kC1C2]] \n" + + // d2: in[0] + // d3: in[8] + // d4: in[4] + // d5: in[12] + "vswp d3, d4 \n" + + // q8 = {in[4], in[12]} * kC1 * 2 >> 16 + // q9 = {in[4], in[12]} * kC2 >> 16 + "vqdmulh.s16 q8, q2, d0[0] \n" + "vqdmulh.s16 q9, q2, d0[1] \n" + + // d22 = a = in[0] + in[8] + // d23 = b = in[0] - in[8] + "vqadd.s16 d22, d2, d3 \n" + "vqsub.s16 d23, d2, d3 \n" + + // q8 = in[4]/[12] * kC1 >> 16 + "vshr.s16 q8, q8, #1 \n" + + // Add {in[4], in[12]} back after the multiplication. + "vqadd.s16 q8, q2, q8 \n" + + // d20 = c = in[4]*kC2 - in[12]*kC1 + // d21 = d = in[4]*kC1 + in[12]*kC2 + "vqsub.s16 d20, d18, d17 \n" + "vqadd.s16 d21, d19, d16 \n" + + // d2 = tmp[0] = a + d + // d3 = tmp[1] = b + c + // d4 = tmp[2] = b - c + // d5 = tmp[3] = a - d + "vqadd.s16 d2, d22, d21 \n" + "vqadd.s16 d3, d23, d20 \n" + "vqsub.s16 d4, d23, d20 \n" + "vqsub.s16 d5, d22, d21 \n" + + "vzip.16 q1, q2 \n" + "vzip.16 q1, q2 \n" + + "vswp d3, d4 \n" + + // q8 = {tmp[4], tmp[12]} * kC1 * 2 >> 16 + // q9 = {tmp[4], tmp[12]} * kC2 >> 16 + "vqdmulh.s16 q8, q2, d0[0] \n" + "vqdmulh.s16 q9, q2, d0[1] \n" + + // d22 = a = tmp[0] + tmp[8] + // d23 = b = tmp[0] - tmp[8] + "vqadd.s16 d22, d2, d3 \n" + "vqsub.s16 d23, d2, d3 \n" + + "vshr.s16 q8, q8, #1 \n" + "vqadd.s16 q8, q2, q8 \n" + + // d20 = c = in[4]*kC2 - in[12]*kC1 + // d21 = d = in[4]*kC1 + in[12]*kC2 + "vqsub.s16 d20, d18, d17 \n" + "vqadd.s16 d21, d19, d16 \n" + + // d2 = tmp[0] = a + d + // d3 = tmp[1] = b + c + // d4 = tmp[2] = b - c + // d5 = tmp[3] = a - d + "vqadd.s16 d2, d22, d21 \n" + "vqadd.s16 d3, d23, d20 \n" + "vqsub.s16 d4, d23, d20 \n" + "vqsub.s16 d5, d22, d21 \n" + + "vld1.32 d6[0], [%[ref]], %[kBPS] \n" + "vld1.32 d6[1], [%[ref]], %[kBPS] \n" + "vld1.32 d7[0], [%[ref]], %[kBPS] \n" + "vld1.32 d7[1], [%[ref]], %[kBPS] \n" + + "sub %[ref], %[ref], %[kBPS], lsl #2 \n" + + // (val) + 4 >> 3 + "vrshr.s16 d2, d2, #3 \n" + "vrshr.s16 d3, d3, #3 \n" + "vrshr.s16 d4, d4, #3 \n" + "vrshr.s16 d5, d5, #3 \n" + + "vzip.16 q1, q2 \n" + "vzip.16 q1, q2 \n" + + // Must accumulate before saturating + "vmovl.u8 q8, d6 \n" + "vmovl.u8 q9, d7 \n" + + "vqadd.s16 q1, q1, q8 \n" + "vqadd.s16 q2, q2, q9 \n" + + "vqmovun.s16 d0, q1 \n" + "vqmovun.s16 d1, q2 \n" + + "vst1.32 d0[0], [%[dst]], %[kBPS] \n" + "vst1.32 d0[1], [%[dst]], %[kBPS] \n" + "vst1.32 d1[0], [%[dst]], %[kBPS] \n" + "vst1.32 d1[1], [%[dst]] \n" + + : [in] "+r"(in), [dst] "+r"(dst) // modified registers + : [kBPS] "r"(kBPS), [kC1C2] "r"(kC1C2), [ref] "r"(ref) // constants + : "memory", "q0", "q1", "q2", "q8", "q9", "q10", "q11" // clobbered + ); +} + +#endif // WEBP_USE_INTRINSICS + +static void ITransform_NEON(const uint8_t* ref, + const int16_t* in, uint8_t* dst, int do_two) { + ITransformOne_NEON(ref, in, dst); + if (do_two) { + ITransformOne_NEON(ref + 4, in + 16, dst + 4); + } +} + +// Load all 4x4 pixels into a single uint8x16_t variable. +static uint8x16_t Load4x4_NEON(const uint8_t* src) { + uint32x4_t out = vdupq_n_u32(0); + out = vld1q_lane_u32((const uint32_t*)(src + 0 * BPS), out, 0); + out = vld1q_lane_u32((const uint32_t*)(src + 1 * BPS), out, 1); + out = vld1q_lane_u32((const uint32_t*)(src + 2 * BPS), out, 2); + out = vld1q_lane_u32((const uint32_t*)(src + 3 * BPS), out, 3); + return vreinterpretq_u8_u32(out); +} + +// Forward transform. + +#if defined(WEBP_USE_INTRINSICS) + +static WEBP_INLINE void Transpose4x4_S16_NEON(const int16x4_t A, + const int16x4_t B, + const int16x4_t C, + const int16x4_t D, + int16x8_t* const out01, + int16x8_t* const out32) { + const int16x4x2_t AB = vtrn_s16(A, B); + const int16x4x2_t CD = vtrn_s16(C, D); + const int32x2x2_t tmp02 = vtrn_s32(vreinterpret_s32_s16(AB.val[0]), + vreinterpret_s32_s16(CD.val[0])); + const int32x2x2_t tmp13 = vtrn_s32(vreinterpret_s32_s16(AB.val[1]), + vreinterpret_s32_s16(CD.val[1])); + *out01 = vreinterpretq_s16_s64( + vcombine_s64(vreinterpret_s64_s32(tmp02.val[0]), + vreinterpret_s64_s32(tmp13.val[0]))); + *out32 = vreinterpretq_s16_s64( + vcombine_s64(vreinterpret_s64_s32(tmp13.val[1]), + vreinterpret_s64_s32(tmp02.val[1]))); +} + +static WEBP_INLINE int16x8_t DiffU8ToS16_NEON(const uint8x8_t a, + const uint8x8_t b) { + return vreinterpretq_s16_u16(vsubl_u8(a, b)); +} + +static void FTransform_NEON(const uint8_t* src, const uint8_t* ref, + int16_t* out) { + int16x8_t d0d1, d3d2; // working 4x4 int16 variables + { + const uint8x16_t S0 = Load4x4_NEON(src); + const uint8x16_t R0 = Load4x4_NEON(ref); + const int16x8_t D0D1 = DiffU8ToS16_NEON(vget_low_u8(S0), vget_low_u8(R0)); + const int16x8_t D2D3 = DiffU8ToS16_NEON(vget_high_u8(S0), vget_high_u8(R0)); + const int16x4_t D0 = vget_low_s16(D0D1); + const int16x4_t D1 = vget_high_s16(D0D1); + const int16x4_t D2 = vget_low_s16(D2D3); + const int16x4_t D3 = vget_high_s16(D2D3); + Transpose4x4_S16_NEON(D0, D1, D2, D3, &d0d1, &d3d2); + } + { // 1rst pass + const int32x4_t kCst937 = vdupq_n_s32(937); + const int32x4_t kCst1812 = vdupq_n_s32(1812); + const int16x8_t a0a1 = vaddq_s16(d0d1, d3d2); // d0+d3 | d1+d2 (=a0|a1) + const int16x8_t a3a2 = vsubq_s16(d0d1, d3d2); // d0-d3 | d1-d2 (=a3|a2) + const int16x8_t a0a1_2 = vshlq_n_s16(a0a1, 3); + const int16x4_t tmp0 = vadd_s16(vget_low_s16(a0a1_2), + vget_high_s16(a0a1_2)); + const int16x4_t tmp2 = vsub_s16(vget_low_s16(a0a1_2), + vget_high_s16(a0a1_2)); + const int32x4_t a3_2217 = vmull_n_s16(vget_low_s16(a3a2), 2217); + const int32x4_t a2_2217 = vmull_n_s16(vget_high_s16(a3a2), 2217); + const int32x4_t a2_p_a3 = vmlal_n_s16(a2_2217, vget_low_s16(a3a2), 5352); + const int32x4_t a3_m_a2 = vmlsl_n_s16(a3_2217, vget_high_s16(a3a2), 5352); + const int16x4_t tmp1 = vshrn_n_s32(vaddq_s32(a2_p_a3, kCst1812), 9); + const int16x4_t tmp3 = vshrn_n_s32(vaddq_s32(a3_m_a2, kCst937), 9); + Transpose4x4_S16_NEON(tmp0, tmp1, tmp2, tmp3, &d0d1, &d3d2); + } + { // 2nd pass + // the (1<<16) addition is for the replacement: a3!=0 <-> 1-(a3==0) + const int32x4_t kCst12000 = vdupq_n_s32(12000 + (1 << 16)); + const int32x4_t kCst51000 = vdupq_n_s32(51000); + const int16x8_t a0a1 = vaddq_s16(d0d1, d3d2); // d0+d3 | d1+d2 (=a0|a1) + const int16x8_t a3a2 = vsubq_s16(d0d1, d3d2); // d0-d3 | d1-d2 (=a3|a2) + const int16x4_t a0_k7 = vadd_s16(vget_low_s16(a0a1), vdup_n_s16(7)); + const int16x4_t out0 = vshr_n_s16(vadd_s16(a0_k7, vget_high_s16(a0a1)), 4); + const int16x4_t out2 = vshr_n_s16(vsub_s16(a0_k7, vget_high_s16(a0a1)), 4); + const int32x4_t a3_2217 = vmull_n_s16(vget_low_s16(a3a2), 2217); + const int32x4_t a2_2217 = vmull_n_s16(vget_high_s16(a3a2), 2217); + const int32x4_t a2_p_a3 = vmlal_n_s16(a2_2217, vget_low_s16(a3a2), 5352); + const int32x4_t a3_m_a2 = vmlsl_n_s16(a3_2217, vget_high_s16(a3a2), 5352); + const int16x4_t tmp1 = vaddhn_s32(a2_p_a3, kCst12000); + const int16x4_t out3 = vaddhn_s32(a3_m_a2, kCst51000); + const int16x4_t a3_eq_0 = + vreinterpret_s16_u16(vceq_s16(vget_low_s16(a3a2), vdup_n_s16(0))); + const int16x4_t out1 = vadd_s16(tmp1, a3_eq_0); + vst1_s16(out + 0, out0); + vst1_s16(out + 4, out1); + vst1_s16(out + 8, out2); + vst1_s16(out + 12, out3); + } +} + +#else + +// adapted from vp8/encoder/arm/neon/shortfdct_neon.asm +static const int16_t kCoeff16[] = { + 5352, 5352, 5352, 5352, 2217, 2217, 2217, 2217 +}; +static const int32_t kCoeff32[] = { + 1812, 1812, 1812, 1812, + 937, 937, 937, 937, + 12000, 12000, 12000, 12000, + 51000, 51000, 51000, 51000 +}; + +static void FTransform_NEON(const uint8_t* src, const uint8_t* ref, + int16_t* out) { + const int kBPS = BPS; + const uint8_t* src_ptr = src; + const uint8_t* ref_ptr = ref; + const int16_t* coeff16 = kCoeff16; + const int32_t* coeff32 = kCoeff32; + + __asm__ volatile ( + // load src into q4, q5 in high half + "vld1.8 {d8}, [%[src_ptr]], %[kBPS] \n" + "vld1.8 {d10}, [%[src_ptr]], %[kBPS] \n" + "vld1.8 {d9}, [%[src_ptr]], %[kBPS] \n" + "vld1.8 {d11}, [%[src_ptr]] \n" + + // load ref into q6, q7 in high half + "vld1.8 {d12}, [%[ref_ptr]], %[kBPS] \n" + "vld1.8 {d14}, [%[ref_ptr]], %[kBPS] \n" + "vld1.8 {d13}, [%[ref_ptr]], %[kBPS] \n" + "vld1.8 {d15}, [%[ref_ptr]] \n" + + // Pack the high values in to q4 and q6 + "vtrn.32 q4, q5 \n" + "vtrn.32 q6, q7 \n" + + // d[0-3] = src - ref + "vsubl.u8 q0, d8, d12 \n" + "vsubl.u8 q1, d9, d13 \n" + + // load coeff16 into q8(d16=5352, d17=2217) + "vld1.16 {q8}, [%[coeff16]] \n" + + // load coeff32 high half into q9 = 1812, q10 = 937 + "vld1.32 {q9, q10}, [%[coeff32]]! \n" + + // load coeff32 low half into q11=12000, q12=51000 + "vld1.32 {q11,q12}, [%[coeff32]] \n" + + // part 1 + // Transpose. Register dN is the same as dN in C + "vtrn.32 d0, d2 \n" + "vtrn.32 d1, d3 \n" + "vtrn.16 d0, d1 \n" + "vtrn.16 d2, d3 \n" + + "vadd.s16 d4, d0, d3 \n" // a0 = d0 + d3 + "vadd.s16 d5, d1, d2 \n" // a1 = d1 + d2 + "vsub.s16 d6, d1, d2 \n" // a2 = d1 - d2 + "vsub.s16 d7, d0, d3 \n" // a3 = d0 - d3 + + "vadd.s16 d0, d4, d5 \n" // a0 + a1 + "vshl.s16 d0, d0, #3 \n" // temp[0+i*4] = (a0+a1) << 3 + "vsub.s16 d2, d4, d5 \n" // a0 - a1 + "vshl.s16 d2, d2, #3 \n" // (temp[2+i*4] = (a0-a1) << 3 + + "vmlal.s16 q9, d7, d16 \n" // a3*5352 + 1812 + "vmlal.s16 q10, d7, d17 \n" // a3*2217 + 937 + "vmlal.s16 q9, d6, d17 \n" // a2*2217 + a3*5352 + 1812 + "vmlsl.s16 q10, d6, d16 \n" // a3*2217 + 937 - a2*5352 + + // temp[1+i*4] = (d2*2217 + d3*5352 + 1812) >> 9 + // temp[3+i*4] = (d3*2217 + 937 - d2*5352) >> 9 + "vshrn.s32 d1, q9, #9 \n" + "vshrn.s32 d3, q10, #9 \n" + + // part 2 + // transpose d0=ip[0], d1=ip[4], d2=ip[8], d3=ip[12] + "vtrn.32 d0, d2 \n" + "vtrn.32 d1, d3 \n" + "vtrn.16 d0, d1 \n" + "vtrn.16 d2, d3 \n" + + "vmov.s16 d26, #7 \n" + + "vadd.s16 d4, d0, d3 \n" // a1 = ip[0] + ip[12] + "vadd.s16 d5, d1, d2 \n" // b1 = ip[4] + ip[8] + "vsub.s16 d6, d1, d2 \n" // c1 = ip[4] - ip[8] + "vadd.s16 d4, d4, d26 \n" // a1 + 7 + "vsub.s16 d7, d0, d3 \n" // d1 = ip[0] - ip[12] + + "vadd.s16 d0, d4, d5 \n" // op[0] = a1 + b1 + 7 + "vsub.s16 d2, d4, d5 \n" // op[8] = a1 - b1 + 7 + + "vmlal.s16 q11, d7, d16 \n" // d1*5352 + 12000 + "vmlal.s16 q12, d7, d17 \n" // d1*2217 + 51000 + + "vceq.s16 d4, d7, #0 \n" + + "vshr.s16 d0, d0, #4 \n" + "vshr.s16 d2, d2, #4 \n" + + "vmlal.s16 q11, d6, d17 \n" // c1*2217 + d1*5352 + 12000 + "vmlsl.s16 q12, d6, d16 \n" // d1*2217 - c1*5352 + 51000 + + "vmvn d4, d4 \n" // !(d1 == 0) + // op[4] = (c1*2217 + d1*5352 + 12000)>>16 + "vshrn.s32 d1, q11, #16 \n" + // op[4] += (d1!=0) + "vsub.s16 d1, d1, d4 \n" + // op[12]= (d1*2217 - c1*5352 + 51000)>>16 + "vshrn.s32 d3, q12, #16 \n" + + // set result to out array + "vst1.16 {q0, q1}, [%[out]] \n" + : [src_ptr] "+r"(src_ptr), [ref_ptr] "+r"(ref_ptr), + [coeff32] "+r"(coeff32) // modified registers + : [kBPS] "r"(kBPS), [coeff16] "r"(coeff16), + [out] "r"(out) // constants + : "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", + "q10", "q11", "q12", "q13" // clobbered + ); +} + +#endif + +#define LOAD_LANE_16b(VALUE, LANE) do { \ + (VALUE) = vld1_lane_s16(src, (VALUE), (LANE)); \ + src += stride; \ +} while (0) + +static void FTransformWHT_NEON(const int16_t* src, int16_t* out) { + const int stride = 16; + const int16x4_t zero = vdup_n_s16(0); + int32x4x4_t tmp0; + int16x4x4_t in; + INIT_VECTOR4(in, zero, zero, zero, zero); + LOAD_LANE_16b(in.val[0], 0); + LOAD_LANE_16b(in.val[1], 0); + LOAD_LANE_16b(in.val[2], 0); + LOAD_LANE_16b(in.val[3], 0); + LOAD_LANE_16b(in.val[0], 1); + LOAD_LANE_16b(in.val[1], 1); + LOAD_LANE_16b(in.val[2], 1); + LOAD_LANE_16b(in.val[3], 1); + LOAD_LANE_16b(in.val[0], 2); + LOAD_LANE_16b(in.val[1], 2); + LOAD_LANE_16b(in.val[2], 2); + LOAD_LANE_16b(in.val[3], 2); + LOAD_LANE_16b(in.val[0], 3); + LOAD_LANE_16b(in.val[1], 3); + LOAD_LANE_16b(in.val[2], 3); + LOAD_LANE_16b(in.val[3], 3); + + { + // a0 = in[0 * 16] + in[2 * 16] + // a1 = in[1 * 16] + in[3 * 16] + // a2 = in[1 * 16] - in[3 * 16] + // a3 = in[0 * 16] - in[2 * 16] + const int32x4_t a0 = vaddl_s16(in.val[0], in.val[2]); + const int32x4_t a1 = vaddl_s16(in.val[1], in.val[3]); + const int32x4_t a2 = vsubl_s16(in.val[1], in.val[3]); + const int32x4_t a3 = vsubl_s16(in.val[0], in.val[2]); + tmp0.val[0] = vaddq_s32(a0, a1); + tmp0.val[1] = vaddq_s32(a3, a2); + tmp0.val[2] = vsubq_s32(a3, a2); + tmp0.val[3] = vsubq_s32(a0, a1); + } + { + const int32x4x4_t tmp1 = Transpose4x4_NEON(tmp0); + // a0 = tmp[0 + i] + tmp[ 8 + i] + // a1 = tmp[4 + i] + tmp[12 + i] + // a2 = tmp[4 + i] - tmp[12 + i] + // a3 = tmp[0 + i] - tmp[ 8 + i] + const int32x4_t a0 = vaddq_s32(tmp1.val[0], tmp1.val[2]); + const int32x4_t a1 = vaddq_s32(tmp1.val[1], tmp1.val[3]); + const int32x4_t a2 = vsubq_s32(tmp1.val[1], tmp1.val[3]); + const int32x4_t a3 = vsubq_s32(tmp1.val[0], tmp1.val[2]); + const int32x4_t b0 = vhaddq_s32(a0, a1); // (a0 + a1) >> 1 + const int32x4_t b1 = vhaddq_s32(a3, a2); // (a3 + a2) >> 1 + const int32x4_t b2 = vhsubq_s32(a3, a2); // (a3 - a2) >> 1 + const int32x4_t b3 = vhsubq_s32(a0, a1); // (a0 - a1) >> 1 + const int16x4_t out0 = vmovn_s32(b0); + const int16x4_t out1 = vmovn_s32(b1); + const int16x4_t out2 = vmovn_s32(b2); + const int16x4_t out3 = vmovn_s32(b3); + + vst1_s16(out + 0, out0); + vst1_s16(out + 4, out1); + vst1_s16(out + 8, out2); + vst1_s16(out + 12, out3); + } +} +#undef LOAD_LANE_16b + +//------------------------------------------------------------------------------ +// Texture distortion +// +// We try to match the spectral content (weighted) between source and +// reconstructed samples. + +// a 0123, b 0123 +// a 4567, b 4567 +// a 89ab, b 89ab +// a cdef, b cdef +// +// transpose +// +// a 048c, b 048c +// a 159d, b 159d +// a 26ae, b 26ae +// a 37bf, b 37bf +// +static WEBP_INLINE int16x8x4_t DistoTranspose4x4S16_NEON(int16x8x4_t q4_in) { + const int16x8x2_t q2_tmp0 = vtrnq_s16(q4_in.val[0], q4_in.val[1]); + const int16x8x2_t q2_tmp1 = vtrnq_s16(q4_in.val[2], q4_in.val[3]); + const int32x4x2_t q2_tmp2 = vtrnq_s32(vreinterpretq_s32_s16(q2_tmp0.val[0]), + vreinterpretq_s32_s16(q2_tmp1.val[0])); + const int32x4x2_t q2_tmp3 = vtrnq_s32(vreinterpretq_s32_s16(q2_tmp0.val[1]), + vreinterpretq_s32_s16(q2_tmp1.val[1])); + q4_in.val[0] = vreinterpretq_s16_s32(q2_tmp2.val[0]); + q4_in.val[2] = vreinterpretq_s16_s32(q2_tmp2.val[1]); + q4_in.val[1] = vreinterpretq_s16_s32(q2_tmp3.val[0]); + q4_in.val[3] = vreinterpretq_s16_s32(q2_tmp3.val[1]); + return q4_in; +} + +static WEBP_INLINE int16x8x4_t DistoHorizontalPass_NEON( + const int16x8x4_t q4_in) { + // {a0, a1} = {in[0] + in[2], in[1] + in[3]} + // {a3, a2} = {in[0] - in[2], in[1] - in[3]} + const int16x8_t q_a0 = vaddq_s16(q4_in.val[0], q4_in.val[2]); + const int16x8_t q_a1 = vaddq_s16(q4_in.val[1], q4_in.val[3]); + const int16x8_t q_a3 = vsubq_s16(q4_in.val[0], q4_in.val[2]); + const int16x8_t q_a2 = vsubq_s16(q4_in.val[1], q4_in.val[3]); + int16x8x4_t q4_out; + // tmp[0] = a0 + a1 + // tmp[1] = a3 + a2 + // tmp[2] = a3 - a2 + // tmp[3] = a0 - a1 + INIT_VECTOR4(q4_out, + vabsq_s16(vaddq_s16(q_a0, q_a1)), + vabsq_s16(vaddq_s16(q_a3, q_a2)), + vabdq_s16(q_a3, q_a2), vabdq_s16(q_a0, q_a1)); + return q4_out; +} + +static WEBP_INLINE int16x8x4_t DistoVerticalPass_NEON(const uint8x8x4_t q4_in) { + const int16x8_t q_a0 = vreinterpretq_s16_u16(vaddl_u8(q4_in.val[0], + q4_in.val[2])); + const int16x8_t q_a1 = vreinterpretq_s16_u16(vaddl_u8(q4_in.val[1], + q4_in.val[3])); + const int16x8_t q_a2 = vreinterpretq_s16_u16(vsubl_u8(q4_in.val[1], + q4_in.val[3])); + const int16x8_t q_a3 = vreinterpretq_s16_u16(vsubl_u8(q4_in.val[0], + q4_in.val[2])); + int16x8x4_t q4_out; + + INIT_VECTOR4(q4_out, + vaddq_s16(q_a0, q_a1), vaddq_s16(q_a3, q_a2), + vsubq_s16(q_a3, q_a2), vsubq_s16(q_a0, q_a1)); + return q4_out; +} + +static WEBP_INLINE int16x4x4_t DistoLoadW_NEON(const uint16_t* w) { + const uint16x8_t q_w07 = vld1q_u16(&w[0]); + const uint16x8_t q_w8f = vld1q_u16(&w[8]); + int16x4x4_t d4_w; + INIT_VECTOR4(d4_w, + vget_low_s16(vreinterpretq_s16_u16(q_w07)), + vget_high_s16(vreinterpretq_s16_u16(q_w07)), + vget_low_s16(vreinterpretq_s16_u16(q_w8f)), + vget_high_s16(vreinterpretq_s16_u16(q_w8f))); + return d4_w; +} + +static WEBP_INLINE int32x2_t DistoSum_NEON(const int16x8x4_t q4_in, + const int16x4x4_t d4_w) { + int32x2_t d_sum; + // sum += w[ 0] * abs(b0); + // sum += w[ 4] * abs(b1); + // sum += w[ 8] * abs(b2); + // sum += w[12] * abs(b3); + int32x4_t q_sum0 = vmull_s16(d4_w.val[0], vget_low_s16(q4_in.val[0])); + int32x4_t q_sum1 = vmull_s16(d4_w.val[1], vget_low_s16(q4_in.val[1])); + int32x4_t q_sum2 = vmull_s16(d4_w.val[2], vget_low_s16(q4_in.val[2])); + int32x4_t q_sum3 = vmull_s16(d4_w.val[3], vget_low_s16(q4_in.val[3])); + q_sum0 = vmlsl_s16(q_sum0, d4_w.val[0], vget_high_s16(q4_in.val[0])); + q_sum1 = vmlsl_s16(q_sum1, d4_w.val[1], vget_high_s16(q4_in.val[1])); + q_sum2 = vmlsl_s16(q_sum2, d4_w.val[2], vget_high_s16(q4_in.val[2])); + q_sum3 = vmlsl_s16(q_sum3, d4_w.val[3], vget_high_s16(q4_in.val[3])); + + q_sum0 = vaddq_s32(q_sum0, q_sum1); + q_sum2 = vaddq_s32(q_sum2, q_sum3); + q_sum2 = vaddq_s32(q_sum0, q_sum2); + d_sum = vpadd_s32(vget_low_s32(q_sum2), vget_high_s32(q_sum2)); + d_sum = vpadd_s32(d_sum, d_sum); + return d_sum; +} + +#define LOAD_LANE_32b(src, VALUE, LANE) \ + (VALUE) = vld1_lane_u32((const uint32_t*)(src), (VALUE), (LANE)) + +// Hadamard transform +// Returns the weighted sum of the absolute value of transformed coefficients. +// w[] contains a row-major 4 by 4 symmetric matrix. +static int Disto4x4_NEON(const uint8_t* const a, const uint8_t* const b, + const uint16_t* const w) { + uint32x2_t d_in_ab_0123 = vdup_n_u32(0); + uint32x2_t d_in_ab_4567 = vdup_n_u32(0); + uint32x2_t d_in_ab_89ab = vdup_n_u32(0); + uint32x2_t d_in_ab_cdef = vdup_n_u32(0); + uint8x8x4_t d4_in; + + // load data a, b + LOAD_LANE_32b(a + 0 * BPS, d_in_ab_0123, 0); + LOAD_LANE_32b(a + 1 * BPS, d_in_ab_4567, 0); + LOAD_LANE_32b(a + 2 * BPS, d_in_ab_89ab, 0); + LOAD_LANE_32b(a + 3 * BPS, d_in_ab_cdef, 0); + LOAD_LANE_32b(b + 0 * BPS, d_in_ab_0123, 1); + LOAD_LANE_32b(b + 1 * BPS, d_in_ab_4567, 1); + LOAD_LANE_32b(b + 2 * BPS, d_in_ab_89ab, 1); + LOAD_LANE_32b(b + 3 * BPS, d_in_ab_cdef, 1); + INIT_VECTOR4(d4_in, + vreinterpret_u8_u32(d_in_ab_0123), + vreinterpret_u8_u32(d_in_ab_4567), + vreinterpret_u8_u32(d_in_ab_89ab), + vreinterpret_u8_u32(d_in_ab_cdef)); + + { + // Vertical pass first to avoid a transpose (vertical and horizontal passes + // are commutative because w/kWeightY is symmetric) and subsequent + // transpose. + const int16x8x4_t q4_v = DistoVerticalPass_NEON(d4_in); + const int16x4x4_t d4_w = DistoLoadW_NEON(w); + // horizontal pass + const int16x8x4_t q4_t = DistoTranspose4x4S16_NEON(q4_v); + const int16x8x4_t q4_h = DistoHorizontalPass_NEON(q4_t); + int32x2_t d_sum = DistoSum_NEON(q4_h, d4_w); + + // abs(sum2 - sum1) >> 5 + d_sum = vabs_s32(d_sum); + d_sum = vshr_n_s32(d_sum, 5); + return vget_lane_s32(d_sum, 0); + } +} +#undef LOAD_LANE_32b + +static int Disto16x16_NEON(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_NEON(a + x + y, b + x + y, w); + } + } + return D; +} + +//------------------------------------------------------------------------------ + +static void CollectHistogram_NEON(const uint8_t* ref, const uint8_t* pred, + int start_block, int end_block, + VP8Histogram* const histo) { + const uint16x8_t max_coeff_thresh = vdupq_n_u16(MAX_COEFF_THRESH); + int j; + int distribution[MAX_COEFF_THRESH + 1] = { 0 }; + for (j = start_block; j < end_block; ++j) { + int16_t out[16]; + FTransform_NEON(ref + VP8DspScan[j], pred + VP8DspScan[j], out); + { + int k; + const int16x8_t a0 = vld1q_s16(out + 0); + const int16x8_t b0 = vld1q_s16(out + 8); + const uint16x8_t a1 = vreinterpretq_u16_s16(vabsq_s16(a0)); + const uint16x8_t b1 = vreinterpretq_u16_s16(vabsq_s16(b0)); + const uint16x8_t a2 = vshrq_n_u16(a1, 3); + const uint16x8_t b2 = vshrq_n_u16(b1, 3); + const uint16x8_t a3 = vminq_u16(a2, max_coeff_thresh); + const uint16x8_t b3 = vminq_u16(b2, max_coeff_thresh); + vst1q_s16(out + 0, vreinterpretq_s16_u16(a3)); + vst1q_s16(out + 8, vreinterpretq_s16_u16(b3)); + // Convert coefficients to bin. + for (k = 0; k < 16; ++k) { + ++distribution[out[k]]; + } + } + } + VP8SetHistogramData(distribution, histo); +} + +//------------------------------------------------------------------------------ + +static WEBP_INLINE void AccumulateSSE16_NEON(const uint8_t* const a, + const uint8_t* const b, + uint32x4_t* const sum) { + const uint8x16_t a0 = vld1q_u8(a); + const uint8x16_t b0 = vld1q_u8(b); + const uint8x16_t abs_diff = vabdq_u8(a0, b0); + 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'. +static int SumToInt_NEON(uint32x4_t sum) { + const uint64x2_t sum2 = vpaddlq_u32(sum); + const uint64_t sum3 = vgetq_lane_u64(sum2, 0) + vgetq_lane_u64(sum2, 1); + return (int)sum3; +} + +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) { + AccumulateSSE16_NEON(a + y * BPS, b + y * BPS, &sum); + } + return SumToInt_NEON(sum); +} + +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) { + AccumulateSSE16_NEON(a + y * BPS, b + y * BPS, &sum); + } + return SumToInt_NEON(sum); +} + +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) { + const uint8x8_t a0 = vld1_u8(a + y * BPS); + const uint8x8_t b0 = vld1_u8(b + y * BPS); + const uint8x8_t abs_diff = vabd_u8(a0, b0); + const uint16x8_t prod = vmull_u8(abs_diff, abs_diff); + sum = vpadalq_u16(sum, prod); + } + return SumToInt_NEON(sum); +} + +static int SSE4x4_NEON(const uint8_t* a, const uint8_t* b) { + const uint8x16_t a0 = Load4x4_NEON(a); + const uint8x16_t b0 = Load4x4_NEON(b); + const uint8x16_t abs_diff = vabdq_u8(a0, b0); + 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_NEON(vaddq_u32(sum1, sum2)); +} + +//------------------------------------------------------------------------------ + +// Compilation with gcc-4.6.x is problematic for now. +#if !defined(WORK_AROUND_GCC) + +static int16x8_t Quantize_NEON(int16_t* const in, + const VP8Matrix* const mtx, int offset) { + const uint16x8_t sharp = vld1q_u16(&mtx->sharpen_[offset]); + const uint16x8_t q = vld1q_u16(&mtx->q_[offset]); + const uint16x8_t iq = vld1q_u16(&mtx->iq_[offset]); + const uint32x4_t bias0 = vld1q_u32(&mtx->bias_[offset + 0]); + const uint32x4_t bias1 = vld1q_u32(&mtx->bias_[offset + 4]); + + const int16x8_t a = vld1q_s16(in + offset); // in + const uint16x8_t b = vreinterpretq_u16_s16(vabsq_s16(a)); // coeff = abs(in) + const int16x8_t sign = vshrq_n_s16(a, 15); // sign + const uint16x8_t c = vaddq_u16(b, sharp); // + sharpen + const uint32x4_t m0 = vmull_u16(vget_low_u16(c), vget_low_u16(iq)); + const uint32x4_t m1 = vmull_u16(vget_high_u16(c), vget_high_u16(iq)); + const uint32x4_t m2 = vhaddq_u32(m0, bias0); + const uint32x4_t m3 = vhaddq_u32(m1, bias1); // (coeff * iQ + bias) >> 1 + const uint16x8_t c0 = vcombine_u16(vshrn_n_u32(m2, 16), + vshrn_n_u32(m3, 16)); // QFIX=17 = 16+1 + const uint16x8_t c1 = vminq_u16(c0, vdupq_n_u16(MAX_LEVEL)); + const int16x8_t c2 = veorq_s16(vreinterpretq_s16_u16(c1), sign); + const int16x8_t c3 = vsubq_s16(c2, sign); // restore sign + const int16x8_t c4 = vmulq_s16(c3, vreinterpretq_s16_u16(q)); + vst1q_s16(in + offset, c4); + assert(QFIX == 17); // this function can't work as is if QFIX != 16+1 + return c3; +} + +static const uint8_t kShuffles[4][8] = { + { 0, 1, 2, 3, 8, 9, 16, 17 }, + { 10, 11, 4, 5, 6, 7, 12, 13 }, + { 18, 19, 24, 25, 26, 27, 20, 21 }, + { 14, 15, 22, 23, 28, 29, 30, 31 } +}; + +static int QuantizeBlock_NEON(int16_t in[16], int16_t out[16], + const VP8Matrix* const mtx) { + const int16x8_t out0 = Quantize_NEON(in, mtx, 0); + const int16x8_t out1 = Quantize_NEON(in, mtx, 8); + uint8x8x4_t shuffles; + // vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use + // non-standard versions there. +#if defined(__APPLE__) && defined(__aarch64__) && \ + defined(__apple_build_version__) && (__apple_build_version__< 6020037) + uint8x16x2_t all_out; + INIT_VECTOR2(all_out, vreinterpretq_u8_s16(out0), vreinterpretq_u8_s16(out1)); + INIT_VECTOR4(shuffles, + vtbl2q_u8(all_out, vld1_u8(kShuffles[0])), + vtbl2q_u8(all_out, vld1_u8(kShuffles[1])), + vtbl2q_u8(all_out, vld1_u8(kShuffles[2])), + vtbl2q_u8(all_out, vld1_u8(kShuffles[3]))); +#else + uint8x8x4_t all_out; + INIT_VECTOR4(all_out, + vreinterpret_u8_s16(vget_low_s16(out0)), + vreinterpret_u8_s16(vget_high_s16(out0)), + vreinterpret_u8_s16(vget_low_s16(out1)), + vreinterpret_u8_s16(vget_high_s16(out1))); + INIT_VECTOR4(shuffles, + vtbl4_u8(all_out, vld1_u8(kShuffles[0])), + vtbl4_u8(all_out, vld1_u8(kShuffles[1])), + vtbl4_u8(all_out, vld1_u8(kShuffles[2])), + vtbl4_u8(all_out, vld1_u8(kShuffles[3]))); +#endif + // Zigzag reordering + vst1_u8((uint8_t*)(out + 0), shuffles.val[0]); + vst1_u8((uint8_t*)(out + 4), shuffles.val[1]); + vst1_u8((uint8_t*)(out + 8), shuffles.val[2]); + vst1_u8((uint8_t*)(out + 12), shuffles.val[3]); + // test zeros + if (*(uint64_t*)(out + 0) != 0) return 1; + if (*(uint64_t*)(out + 4) != 0) return 1; + if (*(uint64_t*)(out + 8) != 0) return 1; + if (*(uint64_t*)(out + 12) != 0) return 1; + return 0; +} + +static int Quantize2Blocks_NEON(int16_t in[32], int16_t out[32], + const VP8Matrix* const mtx) { + int nz; + nz = QuantizeBlock_NEON(in + 0 * 16, out + 0 * 16, mtx) << 0; + nz |= QuantizeBlock_NEON(in + 1 * 16, out + 1 * 16, mtx) << 1; + return nz; +} + +#endif // !WORK_AROUND_GCC + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspInitNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitNEON(void) { + VP8ITransform = ITransform_NEON; + VP8FTransform = FTransform_NEON; + + VP8FTransformWHT = FTransformWHT_NEON; + + VP8TDisto4x4 = Disto4x4_NEON; + VP8TDisto16x16 = Disto16x16_NEON; + VP8CollectHistogram = CollectHistogram_NEON; + + VP8SSE16x16 = SSE16x16_NEON; + VP8SSE16x8 = SSE16x8_NEON; + VP8SSE8x8 = SSE8x8_NEON; + VP8SSE4x4 = SSE4x4_NEON; + +#if !defined(WORK_AROUND_GCC) + VP8EncQuantizeBlock = QuantizeBlock_NEON; + VP8EncQuantize2Blocks = Quantize2Blocks_NEON; +#endif +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(VP8EncDspInitNEON) + +#endif // WEBP_USE_NEON diff --git a/internal/libwebp-1.2.1/src/dsp/enc_sse2.c b/internal/libwebp-1.2.1/src/dsp/enc_sse2.c new file mode 100644 index 0000000..b2e78ed --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/enc_sse2.c @@ -0,0 +1,1381 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE2 version of speed-critical encoding functions. +// +// Author: Christian Duvivier (cduvivier@google.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) +#include +#include // for abs() +#include + +#include "src/dsp/common_sse2.h" +#include "src/enc/cost_enc.h" +#include "src/enc/vp8i_enc.h" + +//------------------------------------------------------------------------------ +// Transforms (Paragraph 14.4) + +// Does one or two inverse transforms. +static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst, + int do_two) { + // This implementation makes use of 16-bit fixed point versions of two + // multiply constants: + // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16 + // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16 + // + // To be able to use signed 16-bit integers, we use the following trick to + // have constants within range: + // - Associated constants are obtained by subtracting the 16-bit fixed point + // version of one: + // k = K - (1 << 16) => K = k + (1 << 16) + // K1 = 85267 => k1 = 20091 + // K2 = 35468 => k2 = -30068 + // - The multiplication of a variable by a constant become the sum of the + // variable and the multiplication of that variable by the associated + // constant: + // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x + const __m128i k1 = _mm_set1_epi16(20091); + const __m128i k2 = _mm_set1_epi16(-30068); + __m128i T0, T1, T2, T3; + + // Load and concatenate the transform coefficients (we'll do two inverse + // transforms in parallel). In the case of only one inverse transform, the + // second half of the vectors will just contain random value we'll never + // use nor store. + __m128i in0, in1, in2, in3; + { + in0 = _mm_loadl_epi64((const __m128i*)&in[0]); + in1 = _mm_loadl_epi64((const __m128i*)&in[4]); + in2 = _mm_loadl_epi64((const __m128i*)&in[8]); + in3 = _mm_loadl_epi64((const __m128i*)&in[12]); + // a00 a10 a20 a30 x x x x + // a01 a11 a21 a31 x x x x + // a02 a12 a22 a32 x x x x + // a03 a13 a23 a33 x x x x + if (do_two) { + const __m128i inB0 = _mm_loadl_epi64((const __m128i*)&in[16]); + const __m128i inB1 = _mm_loadl_epi64((const __m128i*)&in[20]); + const __m128i inB2 = _mm_loadl_epi64((const __m128i*)&in[24]); + const __m128i inB3 = _mm_loadl_epi64((const __m128i*)&in[28]); + in0 = _mm_unpacklo_epi64(in0, inB0); + in1 = _mm_unpacklo_epi64(in1, inB1); + in2 = _mm_unpacklo_epi64(in2, inB2); + in3 = _mm_unpacklo_epi64(in3, inB3); + // a00 a10 a20 a30 b00 b10 b20 b30 + // a01 a11 a21 a31 b01 b11 b21 b31 + // a02 a12 a22 a32 b02 b12 b22 b32 + // a03 a13 a23 a33 b03 b13 b23 b33 + } + } + + // Vertical pass and subsequent transpose. + { + // First pass, c and d calculations are longer because of the "trick" + // multiplications. + const __m128i a = _mm_add_epi16(in0, in2); + const __m128i b = _mm_sub_epi16(in0, in2); + // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3 + const __m128i c1 = _mm_mulhi_epi16(in1, k2); + const __m128i c2 = _mm_mulhi_epi16(in3, k1); + const __m128i c3 = _mm_sub_epi16(in1, in3); + const __m128i c4 = _mm_sub_epi16(c1, c2); + const __m128i c = _mm_add_epi16(c3, c4); + // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3 + const __m128i d1 = _mm_mulhi_epi16(in1, k1); + const __m128i d2 = _mm_mulhi_epi16(in3, k2); + const __m128i d3 = _mm_add_epi16(in1, in3); + const __m128i d4 = _mm_add_epi16(d1, d2); + const __m128i d = _mm_add_epi16(d3, d4); + + // Second pass. + const __m128i tmp0 = _mm_add_epi16(a, d); + const __m128i tmp1 = _mm_add_epi16(b, c); + const __m128i tmp2 = _mm_sub_epi16(b, c); + const __m128i tmp3 = _mm_sub_epi16(a, d); + + // Transpose the two 4x4. + VP8Transpose_2_4x4_16b(&tmp0, &tmp1, &tmp2, &tmp3, &T0, &T1, &T2, &T3); + } + + // Horizontal pass and subsequent transpose. + { + // First pass, c and d calculations are longer because of the "trick" + // multiplications. + const __m128i four = _mm_set1_epi16(4); + const __m128i dc = _mm_add_epi16(T0, four); + const __m128i a = _mm_add_epi16(dc, T2); + const __m128i b = _mm_sub_epi16(dc, T2); + // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3 + const __m128i c1 = _mm_mulhi_epi16(T1, k2); + const __m128i c2 = _mm_mulhi_epi16(T3, k1); + const __m128i c3 = _mm_sub_epi16(T1, T3); + const __m128i c4 = _mm_sub_epi16(c1, c2); + const __m128i c = _mm_add_epi16(c3, c4); + // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3 + const __m128i d1 = _mm_mulhi_epi16(T1, k1); + const __m128i d2 = _mm_mulhi_epi16(T3, k2); + const __m128i d3 = _mm_add_epi16(T1, T3); + const __m128i d4 = _mm_add_epi16(d1, d2); + const __m128i d = _mm_add_epi16(d3, d4); + + // Second pass. + const __m128i tmp0 = _mm_add_epi16(a, d); + const __m128i tmp1 = _mm_add_epi16(b, c); + const __m128i tmp2 = _mm_sub_epi16(b, c); + const __m128i tmp3 = _mm_sub_epi16(a, d); + const __m128i shifted0 = _mm_srai_epi16(tmp0, 3); + const __m128i shifted1 = _mm_srai_epi16(tmp1, 3); + const __m128i shifted2 = _mm_srai_epi16(tmp2, 3); + const __m128i shifted3 = _mm_srai_epi16(tmp3, 3); + + // Transpose the two 4x4. + VP8Transpose_2_4x4_16b(&shifted0, &shifted1, &shifted2, &shifted3, &T0, &T1, + &T2, &T3); + } + + // Add inverse transform to 'ref' and store. + { + const __m128i zero = _mm_setzero_si128(); + // Load the reference(s). + __m128i ref0, ref1, ref2, ref3; + if (do_two) { + // Load eight bytes/pixels per line. + ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]); + ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]); + ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]); + ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]); + } else { + // Load four bytes/pixels per line. + ref0 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[0 * BPS])); + ref1 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[1 * BPS])); + ref2 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[2 * BPS])); + ref3 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[3 * BPS])); + } + // Convert to 16b. + ref0 = _mm_unpacklo_epi8(ref0, zero); + ref1 = _mm_unpacklo_epi8(ref1, zero); + ref2 = _mm_unpacklo_epi8(ref2, zero); + ref3 = _mm_unpacklo_epi8(ref3, zero); + // Add the inverse transform(s). + ref0 = _mm_add_epi16(ref0, T0); + ref1 = _mm_add_epi16(ref1, T1); + ref2 = _mm_add_epi16(ref2, T2); + ref3 = _mm_add_epi16(ref3, T3); + // Unsigned saturate to 8b. + ref0 = _mm_packus_epi16(ref0, ref0); + ref1 = _mm_packus_epi16(ref1, ref1); + ref2 = _mm_packus_epi16(ref2, ref2); + ref3 = _mm_packus_epi16(ref3, ref3); + // Store the results. + if (do_two) { + // Store eight bytes/pixels per line. + _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0); + _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1); + _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2); + _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3); + } else { + // Store four bytes/pixels per line. + WebPUint32ToMem(&dst[0 * BPS], _mm_cvtsi128_si32(ref0)); + WebPUint32ToMem(&dst[1 * BPS], _mm_cvtsi128_si32(ref1)); + WebPUint32ToMem(&dst[2 * BPS], _mm_cvtsi128_si32(ref2)); + WebPUint32ToMem(&dst[3 * BPS], _mm_cvtsi128_si32(ref3)); + } + } +} + +static void FTransformPass1_SSE2(const __m128i* const in01, + const __m128i* const in23, + __m128i* const out01, + __m128i* const out32) { + const __m128i k937 = _mm_set1_epi32(937); + const __m128i k1812 = _mm_set1_epi32(1812); + + const __m128i k88p = _mm_set_epi16(8, 8, 8, 8, 8, 8, 8, 8); + const __m128i k88m = _mm_set_epi16(-8, 8, -8, 8, -8, 8, -8, 8); + const __m128i k5352_2217p = _mm_set_epi16(2217, 5352, 2217, 5352, + 2217, 5352, 2217, 5352); + const __m128i k5352_2217m = _mm_set_epi16(-5352, 2217, -5352, 2217, + -5352, 2217, -5352, 2217); + + // *in01 = 00 01 10 11 02 03 12 13 + // *in23 = 20 21 30 31 22 23 32 33 + const __m128i shuf01_p = _mm_shufflehi_epi16(*in01, _MM_SHUFFLE(2, 3, 0, 1)); + const __m128i shuf23_p = _mm_shufflehi_epi16(*in23, _MM_SHUFFLE(2, 3, 0, 1)); + // 00 01 10 11 03 02 13 12 + // 20 21 30 31 23 22 33 32 + const __m128i s01 = _mm_unpacklo_epi64(shuf01_p, shuf23_p); + const __m128i s32 = _mm_unpackhi_epi64(shuf01_p, shuf23_p); + // 00 01 10 11 20 21 30 31 + // 03 02 13 12 23 22 33 32 + const __m128i a01 = _mm_add_epi16(s01, s32); + const __m128i a32 = _mm_sub_epi16(s01, s32); + // [d0 + d3 | d1 + d2 | ...] = [a0 a1 | a0' a1' | ... ] + // [d0 - d3 | d1 - d2 | ...] = [a3 a2 | a3' a2' | ... ] + + const __m128i tmp0 = _mm_madd_epi16(a01, k88p); // [ (a0 + a1) << 3, ... ] + const __m128i tmp2 = _mm_madd_epi16(a01, k88m); // [ (a0 - a1) << 3, ... ] + const __m128i tmp1_1 = _mm_madd_epi16(a32, k5352_2217p); + const __m128i tmp3_1 = _mm_madd_epi16(a32, k5352_2217m); + const __m128i tmp1_2 = _mm_add_epi32(tmp1_1, k1812); + const __m128i tmp3_2 = _mm_add_epi32(tmp3_1, k937); + const __m128i tmp1 = _mm_srai_epi32(tmp1_2, 9); + const __m128i tmp3 = _mm_srai_epi32(tmp3_2, 9); + const __m128i s03 = _mm_packs_epi32(tmp0, tmp2); + const __m128i s12 = _mm_packs_epi32(tmp1, tmp3); + const __m128i s_lo = _mm_unpacklo_epi16(s03, s12); // 0 1 0 1 0 1... + const __m128i s_hi = _mm_unpackhi_epi16(s03, s12); // 2 3 2 3 2 3 + const __m128i v23 = _mm_unpackhi_epi32(s_lo, s_hi); + *out01 = _mm_unpacklo_epi32(s_lo, s_hi); + *out32 = _mm_shuffle_epi32(v23, _MM_SHUFFLE(1, 0, 3, 2)); // 3 2 3 2 3 2.. +} + +static void FTransformPass2_SSE2(const __m128i* const v01, + const __m128i* const v32, + int16_t* out) { + const __m128i zero = _mm_setzero_si128(); + const __m128i seven = _mm_set1_epi16(7); + const __m128i k5352_2217 = _mm_set_epi16(5352, 2217, 5352, 2217, + 5352, 2217, 5352, 2217); + const __m128i k2217_5352 = _mm_set_epi16(2217, -5352, 2217, -5352, + 2217, -5352, 2217, -5352); + const __m128i k12000_plus_one = _mm_set1_epi32(12000 + (1 << 16)); + const __m128i k51000 = _mm_set1_epi32(51000); + + // Same operations are done on the (0,3) and (1,2) pairs. + // a3 = v0 - v3 + // a2 = v1 - v2 + const __m128i a32 = _mm_sub_epi16(*v01, *v32); + const __m128i a22 = _mm_unpackhi_epi64(a32, a32); + + 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); + const __m128i d1 = _mm_add_epi32(c1, k12000_plus_one); + 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); + // 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. + // -> 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); + _mm_storeu_si128((__m128i*)&out[8], d2_f3); +} + +static void FTransform_SSE2(const uint8_t* src, const uint8_t* ref, + int16_t* out) { + const __m128i zero = _mm_setzero_si128(); + // Load src. + const __m128i src0 = _mm_loadl_epi64((const __m128i*)&src[0 * BPS]); + const __m128i src1 = _mm_loadl_epi64((const __m128i*)&src[1 * BPS]); + const __m128i src2 = _mm_loadl_epi64((const __m128i*)&src[2 * BPS]); + const __m128i src3 = _mm_loadl_epi64((const __m128i*)&src[3 * BPS]); + // 00 01 02 03 * + // 10 11 12 13 * + // 20 21 22 23 * + // 30 31 32 33 * + // Shuffle. + const __m128i src_0 = _mm_unpacklo_epi16(src0, src1); + const __m128i src_1 = _mm_unpacklo_epi16(src2, src3); + // 00 01 10 11 02 03 12 13 * * ... + // 20 21 30 31 22 22 32 33 * * ... + + // Load ref. + const __m128i ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]); + const __m128i ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]); + const __m128i ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]); + const __m128i ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]); + const __m128i ref_0 = _mm_unpacklo_epi16(ref0, ref1); + const __m128i ref_1 = _mm_unpacklo_epi16(ref2, ref3); + + // Convert both to 16 bit. + const __m128i src_0_16b = _mm_unpacklo_epi8(src_0, zero); + const __m128i src_1_16b = _mm_unpacklo_epi8(src_1, zero); + const __m128i ref_0_16b = _mm_unpacklo_epi8(ref_0, zero); + const __m128i ref_1_16b = _mm_unpacklo_epi8(ref_1, zero); + + // Compute the difference. + const __m128i row01 = _mm_sub_epi16(src_0_16b, ref_0_16b); + const __m128i row23 = _mm_sub_epi16(src_1_16b, ref_1_16b); + __m128i v01, v32; + + // First pass + FTransformPass1_SSE2(&row01, &row23, &v01, &v32); + + // Second pass + FTransformPass2_SSE2(&v01, &v32, out); +} + +static void FTransform2_SSE2(const uint8_t* src, const uint8_t* ref, + int16_t* out) { + const __m128i zero = _mm_setzero_si128(); + + // Load src and convert to 16b. + const __m128i src0 = _mm_loadl_epi64((const __m128i*)&src[0 * BPS]); + const __m128i src1 = _mm_loadl_epi64((const __m128i*)&src[1 * BPS]); + const __m128i src2 = _mm_loadl_epi64((const __m128i*)&src[2 * BPS]); + const __m128i src3 = _mm_loadl_epi64((const __m128i*)&src[3 * BPS]); + const __m128i src_0 = _mm_unpacklo_epi8(src0, zero); + const __m128i src_1 = _mm_unpacklo_epi8(src1, zero); + const __m128i src_2 = _mm_unpacklo_epi8(src2, zero); + const __m128i src_3 = _mm_unpacklo_epi8(src3, zero); + // Load ref and convert to 16b. + const __m128i ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]); + const __m128i ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]); + const __m128i ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]); + const __m128i ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]); + const __m128i ref_0 = _mm_unpacklo_epi8(ref0, zero); + const __m128i ref_1 = _mm_unpacklo_epi8(ref1, zero); + const __m128i ref_2 = _mm_unpacklo_epi8(ref2, zero); + const __m128i ref_3 = _mm_unpacklo_epi8(ref3, zero); + // Compute difference. -> 00 01 02 03 00' 01' 02' 03' + const __m128i diff0 = _mm_sub_epi16(src_0, ref_0); + const __m128i diff1 = _mm_sub_epi16(src_1, ref_1); + const __m128i diff2 = _mm_sub_epi16(src_2, ref_2); + const __m128i diff3 = _mm_sub_epi16(src_3, ref_3); + + // Unpack and shuffle + // 00 01 02 03 0 0 0 0 + // 10 11 12 13 0 0 0 0 + // 20 21 22 23 0 0 0 0 + // 30 31 32 33 0 0 0 0 + const __m128i shuf01l = _mm_unpacklo_epi32(diff0, diff1); + const __m128i shuf23l = _mm_unpacklo_epi32(diff2, diff3); + const __m128i shuf01h = _mm_unpackhi_epi32(diff0, diff1); + const __m128i shuf23h = _mm_unpackhi_epi32(diff2, diff3); + __m128i v01l, v32l; + __m128i v01h, v32h; + + // First pass + FTransformPass1_SSE2(&shuf01l, &shuf23l, &v01l, &v32l); + FTransformPass1_SSE2(&shuf01h, &shuf23h, &v01h, &v32h); + + // Second pass + FTransformPass2_SSE2(&v01l, &v32l, out + 0); + FTransformPass2_SSE2(&v01h, &v32h, out + 16); +} + +static void FTransformWHTRow_SSE2(const int16_t* const in, __m128i* const out) { + const __m128i kMult = _mm_set_epi16(-1, 1, -1, 1, 1, 1, 1, 1); + const __m128i src0 = _mm_loadl_epi64((__m128i*)&in[0 * 16]); + const __m128i src1 = _mm_loadl_epi64((__m128i*)&in[1 * 16]); + const __m128i src2 = _mm_loadl_epi64((__m128i*)&in[2 * 16]); + const __m128i src3 = _mm_loadl_epi64((__m128i*)&in[3 * 16]); + const __m128i A01 = _mm_unpacklo_epi16(src0, src1); // A0 A1 | ... + const __m128i A23 = _mm_unpacklo_epi16(src2, src3); // A2 A3 | ... + const __m128i B0 = _mm_adds_epi16(A01, A23); // a0 | a1 | ... + const __m128i B1 = _mm_subs_epi16(A01, A23); // a3 | a2 | ... + const __m128i C0 = _mm_unpacklo_epi32(B0, B1); // a0 | a1 | a3 | a2 | ... + const __m128i C1 = _mm_unpacklo_epi32(B1, B0); // a3 | a2 | a0 | a1 | ... + const __m128i D = _mm_unpacklo_epi64(C0, C1); // a0 a1 a3 a2 a3 a2 a0 a1 + *out = _mm_madd_epi16(D, kMult); +} + +static void FTransformWHT_SSE2(const int16_t* in, int16_t* out) { + // Input is 12b signed. + __m128i row0, row1, row2, row3; + // Rows are 14b signed. + FTransformWHTRow_SSE2(in + 0 * 64, &row0); + FTransformWHTRow_SSE2(in + 1 * 64, &row1); + FTransformWHTRow_SSE2(in + 2 * 64, &row2); + FTransformWHTRow_SSE2(in + 3 * 64, &row3); + + { + // The a* are 15b signed. + const __m128i a0 = _mm_add_epi32(row0, row2); + const __m128i a1 = _mm_add_epi32(row1, row3); + const __m128i a2 = _mm_sub_epi32(row1, row3); + const __m128i a3 = _mm_sub_epi32(row0, row2); + const __m128i a0a3 = _mm_packs_epi32(a0, a3); + const __m128i a1a2 = _mm_packs_epi32(a1, a2); + + // The b* are 16b signed. + const __m128i b0b1 = _mm_add_epi16(a0a3, a1a2); + const __m128i b3b2 = _mm_sub_epi16(a0a3, a1a2); + const __m128i tmp_b2b3 = _mm_unpackhi_epi64(b3b2, b3b2); + const __m128i b2b3 = _mm_unpacklo_epi64(tmp_b2b3, b3b2); + + _mm_storeu_si128((__m128i*)&out[0], _mm_srai_epi16(b0b1, 1)); + _mm_storeu_si128((__m128i*)&out[8], _mm_srai_epi16(b2b3, 1)); + } +} + +//------------------------------------------------------------------------------ +// Compute susceptibility based on DCT-coeff histograms: +// the higher, the "easier" the macroblock is to compress. + +static void CollectHistogram_SSE2(const uint8_t* ref, const uint8_t* pred, + int start_block, int end_block, + VP8Histogram* const histo) { + const __m128i zero = _mm_setzero_si128(); + const __m128i max_coeff_thresh = _mm_set1_epi16(MAX_COEFF_THRESH); + int j; + int distribution[MAX_COEFF_THRESH + 1] = { 0 }; + for (j = start_block; j < end_block; ++j) { + int16_t out[16]; + int k; + + FTransform_SSE2(ref + VP8DspScan[j], pred + VP8DspScan[j], out); + + // Convert coefficients to bin (within out[]). + { + // Load. + const __m128i out0 = _mm_loadu_si128((__m128i*)&out[0]); + const __m128i out1 = _mm_loadu_si128((__m128i*)&out[8]); + const __m128i d0 = _mm_sub_epi16(zero, out0); + const __m128i d1 = _mm_sub_epi16(zero, out1); + const __m128i abs0 = _mm_max_epi16(out0, d0); // abs(v), 16b + const __m128i abs1 = _mm_max_epi16(out1, d1); + // v = abs(out) >> 3 + const __m128i v0 = _mm_srai_epi16(abs0, 3); + const __m128i v1 = _mm_srai_epi16(abs1, 3); + // bin = min(v, MAX_COEFF_THRESH) + const __m128i bin0 = _mm_min_epi16(v0, max_coeff_thresh); + const __m128i bin1 = _mm_min_epi16(v1, max_coeff_thresh); + // Store. + _mm_storeu_si128((__m128i*)&out[0], bin0); + _mm_storeu_si128((__m128i*)&out[8], bin1); + } + + // Convert coefficients to bin. + for (k = 0; k < 16; ++k) { + ++distribution[out[k]]; + } + } + VP8SetHistogramData(distribution, histo); +} + +//------------------------------------------------------------------------------ +// Intra predictions + +// helper for chroma-DC predictions +static WEBP_INLINE void Put8x8uv_SSE2(uint8_t v, uint8_t* dst) { + int j; + const __m128i values = _mm_set1_epi8(v); + for (j = 0; j < 8; ++j) { + _mm_storel_epi64((__m128i*)(dst + j * BPS), values); + } +} + +static WEBP_INLINE void Put16_SSE2(uint8_t v, uint8_t* dst) { + int j; + const __m128i values = _mm_set1_epi8(v); + for (j = 0; j < 16; ++j) { + _mm_store_si128((__m128i*)(dst + j * BPS), values); + } +} + +static WEBP_INLINE void Fill_SSE2(uint8_t* dst, int value, int size) { + if (size == 4) { + int j; + for (j = 0; j < 4; ++j) { + memset(dst + j * BPS, value, 4); + } + } else if (size == 8) { + Put8x8uv_SSE2(value, dst); + } else { + Put16_SSE2(value, dst); + } +} + +static WEBP_INLINE void VE8uv_SSE2(uint8_t* dst, const uint8_t* top) { + int j; + const __m128i top_values = _mm_loadl_epi64((const __m128i*)top); + for (j = 0; j < 8; ++j) { + _mm_storel_epi64((__m128i*)(dst + j * BPS), top_values); + } +} + +static WEBP_INLINE void VE16_SSE2(uint8_t* dst, const uint8_t* top) { + const __m128i top_values = _mm_load_si128((const __m128i*)top); + int j; + for (j = 0; j < 16; ++j) { + _mm_store_si128((__m128i*)(dst + j * BPS), top_values); + } +} + +static WEBP_INLINE void VerticalPred_SSE2(uint8_t* dst, + const uint8_t* top, int size) { + if (top != NULL) { + if (size == 8) { + VE8uv_SSE2(dst, top); + } else { + VE16_SSE2(dst, top); + } + } else { + Fill_SSE2(dst, 127, size); + } +} + +static WEBP_INLINE void HE8uv_SSE2(uint8_t* dst, const uint8_t* left) { + int j; + for (j = 0; j < 8; ++j) { + const __m128i values = _mm_set1_epi8(left[j]); + _mm_storel_epi64((__m128i*)dst, values); + dst += BPS; + } +} + +static WEBP_INLINE void HE16_SSE2(uint8_t* dst, const uint8_t* left) { + int j; + for (j = 0; j < 16; ++j) { + const __m128i values = _mm_set1_epi8(left[j]); + _mm_store_si128((__m128i*)dst, values); + dst += BPS; + } +} + +static WEBP_INLINE void HorizontalPred_SSE2(uint8_t* dst, + const uint8_t* left, int size) { + if (left != NULL) { + if (size == 8) { + HE8uv_SSE2(dst, left); + } else { + HE16_SSE2(dst, left); + } + } else { + Fill_SSE2(dst, 129, size); + } +} + +static WEBP_INLINE void TM_SSE2(uint8_t* dst, const uint8_t* left, + const uint8_t* top, int size) { + const __m128i zero = _mm_setzero_si128(); + int y; + if (size == 8) { + const __m128i top_values = _mm_loadl_epi64((const __m128i*)top); + const __m128i top_base = _mm_unpacklo_epi8(top_values, zero); + for (y = 0; y < 8; ++y, dst += BPS) { + const int val = left[y] - left[-1]; + const __m128i base = _mm_set1_epi16(val); + const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero); + _mm_storel_epi64((__m128i*)dst, out); + } + } else { + const __m128i top_values = _mm_load_si128((const __m128i*)top); + const __m128i top_base_0 = _mm_unpacklo_epi8(top_values, zero); + const __m128i top_base_1 = _mm_unpackhi_epi8(top_values, zero); + for (y = 0; y < 16; ++y, dst += BPS) { + const int val = left[y] - left[-1]; + const __m128i base = _mm_set1_epi16(val); + const __m128i out_0 = _mm_add_epi16(base, top_base_0); + const __m128i out_1 = _mm_add_epi16(base, top_base_1); + const __m128i out = _mm_packus_epi16(out_0, out_1); + _mm_store_si128((__m128i*)dst, out); + } + } +} + +static WEBP_INLINE void TrueMotion_SSE2(uint8_t* dst, const uint8_t* left, + const uint8_t* top, int size) { + if (left != NULL) { + if (top != NULL) { + TM_SSE2(dst, left, top, size); + } else { + HorizontalPred_SSE2(dst, left, size); + } + } else { + // true motion without left samples (hence: with default 129 value) + // is equivalent to VE prediction where you just copy the top samples. + // Note that if top samples are not available, the default value is + // then 129, and not 127 as in the VerticalPred case. + if (top != NULL) { + VerticalPred_SSE2(dst, top, size); + } else { + Fill_SSE2(dst, 129, size); + } + } +} + +static WEBP_INLINE void DC8uv_SSE2(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + const __m128i top_values = _mm_loadl_epi64((const __m128i*)top); + const __m128i left_values = _mm_loadl_epi64((const __m128i*)left); + const __m128i combined = _mm_unpacklo_epi64(top_values, left_values); + const int DC = VP8HorizontalAdd8b(&combined) + 8; + Put8x8uv_SSE2(DC >> 4, dst); +} + +static WEBP_INLINE void DC8uvNoLeft_SSE2(uint8_t* dst, const uint8_t* top) { + const __m128i zero = _mm_setzero_si128(); + const __m128i top_values = _mm_loadl_epi64((const __m128i*)top); + const __m128i sum = _mm_sad_epu8(top_values, zero); + const int DC = _mm_cvtsi128_si32(sum) + 4; + Put8x8uv_SSE2(DC >> 3, dst); +} + +static WEBP_INLINE void DC8uvNoTop_SSE2(uint8_t* dst, const uint8_t* left) { + // 'left' is contiguous so we can reuse the top summation. + DC8uvNoLeft_SSE2(dst, left); +} + +static WEBP_INLINE void DC8uvNoTopLeft_SSE2(uint8_t* dst) { + Put8x8uv_SSE2(0x80, dst); +} + +static WEBP_INLINE void DC8uvMode_SSE2(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + if (top != NULL) { + if (left != NULL) { // top and left present + DC8uv_SSE2(dst, left, top); + } else { // top, but no left + DC8uvNoLeft_SSE2(dst, top); + } + } else if (left != NULL) { // left but no top + DC8uvNoTop_SSE2(dst, left); + } else { // no top, no left, nothing. + DC8uvNoTopLeft_SSE2(dst); + } +} + +static WEBP_INLINE void DC16_SSE2(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + const __m128i top_row = _mm_load_si128((const __m128i*)top); + const __m128i left_row = _mm_load_si128((const __m128i*)left); + const int DC = + VP8HorizontalAdd8b(&top_row) + VP8HorizontalAdd8b(&left_row) + 16; + Put16_SSE2(DC >> 5, dst); +} + +static WEBP_INLINE void DC16NoLeft_SSE2(uint8_t* dst, const uint8_t* top) { + const __m128i top_row = _mm_load_si128((const __m128i*)top); + const int DC = VP8HorizontalAdd8b(&top_row) + 8; + Put16_SSE2(DC >> 4, dst); +} + +static WEBP_INLINE void DC16NoTop_SSE2(uint8_t* dst, const uint8_t* left) { + // 'left' is contiguous so we can reuse the top summation. + DC16NoLeft_SSE2(dst, left); +} + +static WEBP_INLINE void DC16NoTopLeft_SSE2(uint8_t* dst) { + Put16_SSE2(0x80, dst); +} + +static WEBP_INLINE void DC16Mode_SSE2(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + if (top != NULL) { + if (left != NULL) { // top and left present + DC16_SSE2(dst, left, top); + } else { // top, but no left + DC16NoLeft_SSE2(dst, top); + } + } else if (left != NULL) { // left but no top + DC16NoTop_SSE2(dst, left); + } else { // no top, no left, nothing. + DC16NoTopLeft_SSE2(dst); + } +} + +//------------------------------------------------------------------------------ +// 4x4 predictions + +#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) + +// We use the following 8b-arithmetic tricks: +// (a + 2 * b + c + 2) >> 2 = (AC + b + 1) >> 1 +// where: AC = (a + c) >> 1 = [(a + c + 1) >> 1] - [(a^c) & 1] +// and: +// (a + 2 * b + c + 2) >> 2 = (AB + BC + 1) >> 1 - (ab|bc)&lsb +// where: AC = (a + b + 1) >> 1, BC = (b + c + 1) >> 1 +// and ab = a ^ b, bc = b ^ c, lsb = (AC^BC)&1 + +static WEBP_INLINE void VE4_SSE2(uint8_t* dst, + const uint8_t* top) { // vertical + const __m128i one = _mm_set1_epi8(1); + const __m128i ABCDEFGH = _mm_loadl_epi64((__m128i*)(top - 1)); + const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1); + const __m128i CDEFGH00 = _mm_srli_si128(ABCDEFGH, 2); + const __m128i a = _mm_avg_epu8(ABCDEFGH, CDEFGH00); + const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGH00), one); + const __m128i b = _mm_subs_epu8(a, lsb); + const __m128i avg = _mm_avg_epu8(b, BCDEFGH0); + const uint32_t vals = _mm_cvtsi128_si32(avg); + int i; + for (i = 0; i < 4; ++i) { + WebPUint32ToMem(dst + i * BPS, vals); + } +} + +static WEBP_INLINE void HE4_SSE2(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_SSE2(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]; + Fill_SSE2(dst, dc >> 3, 4); +} + +static WEBP_INLINE void LD4_SSE2(uint8_t* dst, + const uint8_t* top) { // Down-Left + const __m128i one = _mm_set1_epi8(1); + const __m128i ABCDEFGH = _mm_loadl_epi64((const __m128i*)top); + const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1); + const __m128i CDEFGH00 = _mm_srli_si128(ABCDEFGH, 2); + const __m128i CDEFGHH0 = _mm_insert_epi16(CDEFGH00, top[7], 3); + const __m128i avg1 = _mm_avg_epu8(ABCDEFGH, CDEFGHH0); + const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one); + const __m128i avg2 = _mm_subs_epu8(avg1, lsb); + const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1))); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3))); +} + +static WEBP_INLINE void VR4_SSE2(uint8_t* dst, + const uint8_t* top) { // Vertical-Right + const __m128i one = _mm_set1_epi8(1); + const int I = top[-2]; + const int J = top[-3]; + const int K = top[-4]; + const int X = top[-1]; + const __m128i XABCD = _mm_loadl_epi64((const __m128i*)(top - 1)); + const __m128i ABCD0 = _mm_srli_si128(XABCD, 1); + const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0); + const __m128i _XABCD = _mm_slli_si128(XABCD, 1); + const __m128i IXABCD = _mm_insert_epi16(_XABCD, (short)(I | (X << 8)), 0); + const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0); + const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one); + const __m128i avg2 = _mm_subs_epu8(avg1, lsb); + const __m128i efgh = _mm_avg_epu8(avg2, XABCD); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1))); + + // these two are hard to implement in SSE2, so we keep the C-version: + DST(0, 2) = AVG3(J, I, X); + DST(0, 3) = AVG3(K, J, I); +} + +static WEBP_INLINE void VL4_SSE2(uint8_t* dst, + const uint8_t* top) { // Vertical-Left + const __m128i one = _mm_set1_epi8(1); + const __m128i ABCDEFGH = _mm_loadl_epi64((const __m128i*)top); + const __m128i BCDEFGH_ = _mm_srli_si128(ABCDEFGH, 1); + const __m128i CDEFGH__ = _mm_srli_si128(ABCDEFGH, 2); + const __m128i avg1 = _mm_avg_epu8(ABCDEFGH, BCDEFGH_); + const __m128i avg2 = _mm_avg_epu8(CDEFGH__, BCDEFGH_); + const __m128i avg3 = _mm_avg_epu8(avg1, avg2); + const __m128i lsb1 = _mm_and_si128(_mm_xor_si128(avg1, avg2), one); + const __m128i ab = _mm_xor_si128(ABCDEFGH, BCDEFGH_); + const __m128i bc = _mm_xor_si128(CDEFGH__, BCDEFGH_); + const __m128i abbc = _mm_or_si128(ab, bc); + const __m128i lsb2 = _mm_and_si128(abbc, lsb1); + const __m128i avg4 = _mm_subs_epu8(avg3, lsb2); + const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4)); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1))); + + // these two are hard to get and irregular + DST(3, 2) = (extra_out >> 0) & 0xff; + DST(3, 3) = (extra_out >> 8) & 0xff; +} + +static WEBP_INLINE void RD4_SSE2(uint8_t* dst, + const uint8_t* top) { // Down-right + const __m128i one = _mm_set1_epi8(1); + const __m128i LKJIXABC = _mm_loadl_epi64((const __m128i*)(top - 5)); + const __m128i LKJIXABCD = _mm_insert_epi16(LKJIXABC, top[3], 4); + const __m128i KJIXABCD_ = _mm_srli_si128(LKJIXABCD, 1); + const __m128i JIXABCD__ = _mm_srli_si128(LKJIXABCD, 2); + const __m128i avg1 = _mm_avg_epu8(JIXABCD__, LKJIXABCD); + const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one); + const __m128i avg2 = _mm_subs_epu8(avg1, lsb); + const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1))); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2))); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3))); +} + +static WEBP_INLINE void HU4_SSE2(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_SSE2(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_SSE2(uint8_t* dst, const uint8_t* top) { + const __m128i zero = _mm_setzero_si128(); + const __m128i top_values = _mm_cvtsi32_si128(WebPMemToUint32(top)); + const __m128i top_base = _mm_unpacklo_epi8(top_values, zero); + int y; + for (y = 0; y < 4; ++y, dst += BPS) { + const int val = top[-2 - y] - top[-1]; + const __m128i base = _mm_set1_epi16(val); + const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero); + WebPUint32ToMem(dst, _mm_cvtsi128_si32(out)); + } +} + +#undef DST +#undef AVG3 +#undef AVG2 + +//------------------------------------------------------------------------------ +// luma 4x4 prediction + +// Left samples are top[-5 .. -2], top_left is top[-1], top are +// located at top[0..3], and top right is top[4..7] +static void Intra4Preds_SSE2(uint8_t* dst, const uint8_t* top) { + DC4_SSE2(I4DC4 + dst, top); + TM4_SSE2(I4TM4 + dst, top); + VE4_SSE2(I4VE4 + dst, top); + HE4_SSE2(I4HE4 + dst, top); + RD4_SSE2(I4RD4 + dst, top); + VR4_SSE2(I4VR4 + dst, top); + LD4_SSE2(I4LD4 + dst, top); + VL4_SSE2(I4VL4 + dst, top); + HD4_SSE2(I4HD4 + dst, top); + HU4_SSE2(I4HU4 + dst, top); +} + +//------------------------------------------------------------------------------ +// Chroma 8x8 prediction (paragraph 12.2) + +static void IntraChromaPreds_SSE2(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + // U block + DC8uvMode_SSE2(C8DC8 + dst, left, top); + VerticalPred_SSE2(C8VE8 + dst, top, 8); + HorizontalPred_SSE2(C8HE8 + dst, left, 8); + TrueMotion_SSE2(C8TM8 + dst, left, top, 8); + // V block + dst += 8; + if (top != NULL) top += 8; + if (left != NULL) left += 16; + DC8uvMode_SSE2(C8DC8 + dst, left, top); + VerticalPred_SSE2(C8VE8 + dst, top, 8); + HorizontalPred_SSE2(C8HE8 + dst, left, 8); + TrueMotion_SSE2(C8TM8 + dst, left, top, 8); +} + +//------------------------------------------------------------------------------ +// luma 16x16 prediction (paragraph 12.3) + +static void Intra16Preds_SSE2(uint8_t* dst, + const uint8_t* left, const uint8_t* top) { + DC16Mode_SSE2(I16DC16 + dst, left, top); + VerticalPred_SSE2(I16VE16 + dst, top, 16); + HorizontalPred_SSE2(I16HE16 + dst, left, 16); + TrueMotion_SSE2(I16TM16 + dst, left, top, 16); +} + +//------------------------------------------------------------------------------ +// Metric + +static WEBP_INLINE void SubtractAndAccumulate_SSE2(const __m128i a, + const __m128i b, + __m128i* const sum) { + // take abs(a-b) in 8b + const __m128i a_b = _mm_subs_epu8(a, b); + const __m128i b_a = _mm_subs_epu8(b, a); + const __m128i abs_a_b = _mm_or_si128(a_b, b_a); + // zero-extend to 16b + const __m128i zero = _mm_setzero_si128(); + const __m128i C0 = _mm_unpacklo_epi8(abs_a_b, zero); + const __m128i C1 = _mm_unpackhi_epi8(abs_a_b, zero); + // multiply with self + const __m128i sum1 = _mm_madd_epi16(C0, C0); + const __m128i sum2 = _mm_madd_epi16(C1, C1); + *sum = _mm_add_epi32(sum1, sum2); +} + +static WEBP_INLINE int SSE_16xN_SSE2(const uint8_t* a, const uint8_t* b, + int num_pairs) { + __m128i sum = _mm_setzero_si128(); + int32_t tmp[4]; + int i; + + for (i = 0; i < num_pairs; ++i) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[BPS * 0]); + const __m128i b0 = _mm_loadu_si128((const __m128i*)&b[BPS * 0]); + const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[BPS * 1]); + const __m128i b1 = _mm_loadu_si128((const __m128i*)&b[BPS * 1]); + __m128i sum1, sum2; + SubtractAndAccumulate_SSE2(a0, b0, &sum1); + SubtractAndAccumulate_SSE2(a1, b1, &sum2); + sum = _mm_add_epi32(sum, _mm_add_epi32(sum1, sum2)); + a += 2 * BPS; + b += 2 * BPS; + } + _mm_storeu_si128((__m128i*)tmp, sum); + return (tmp[3] + tmp[2] + tmp[1] + tmp[0]); +} + +static int SSE16x16_SSE2(const uint8_t* a, const uint8_t* b) { + return SSE_16xN_SSE2(a, b, 8); +} + +static int SSE16x8_SSE2(const uint8_t* a, const uint8_t* b) { + return SSE_16xN_SSE2(a, b, 4); +} + +#define LOAD_8x16b(ptr) \ + _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(ptr)), zero) + +static int SSE8x8_SSE2(const uint8_t* a, const uint8_t* b) { + const __m128i zero = _mm_setzero_si128(); + int num_pairs = 4; + __m128i sum = zero; + int32_t tmp[4]; + while (num_pairs-- > 0) { + const __m128i a0 = LOAD_8x16b(&a[BPS * 0]); + const __m128i a1 = LOAD_8x16b(&a[BPS * 1]); + const __m128i b0 = LOAD_8x16b(&b[BPS * 0]); + const __m128i b1 = LOAD_8x16b(&b[BPS * 1]); + // subtract + const __m128i c0 = _mm_subs_epi16(a0, b0); + const __m128i c1 = _mm_subs_epi16(a1, b1); + // multiply/accumulate with self + const __m128i d0 = _mm_madd_epi16(c0, c0); + const __m128i d1 = _mm_madd_epi16(c1, c1); + // collect + const __m128i sum01 = _mm_add_epi32(d0, d1); + sum = _mm_add_epi32(sum, sum01); + a += 2 * BPS; + b += 2 * BPS; + } + _mm_storeu_si128((__m128i*)tmp, sum); + return (tmp[3] + tmp[2] + tmp[1] + tmp[0]); +} +#undef LOAD_8x16b + +static int SSE4x4_SSE2(const uint8_t* a, const uint8_t* b) { + const __m128i zero = _mm_setzero_si128(); + + // Load values. Note that we read 8 pixels instead of 4, + // but the a/b buffers are over-allocated to that effect. + const __m128i a0 = _mm_loadl_epi64((const __m128i*)&a[BPS * 0]); + const __m128i a1 = _mm_loadl_epi64((const __m128i*)&a[BPS * 1]); + const __m128i a2 = _mm_loadl_epi64((const __m128i*)&a[BPS * 2]); + const __m128i a3 = _mm_loadl_epi64((const __m128i*)&a[BPS * 3]); + const __m128i b0 = _mm_loadl_epi64((const __m128i*)&b[BPS * 0]); + const __m128i b1 = _mm_loadl_epi64((const __m128i*)&b[BPS * 1]); + const __m128i b2 = _mm_loadl_epi64((const __m128i*)&b[BPS * 2]); + const __m128i b3 = _mm_loadl_epi64((const __m128i*)&b[BPS * 3]); + // Combine pair of lines. + const __m128i a01 = _mm_unpacklo_epi32(a0, a1); + const __m128i a23 = _mm_unpacklo_epi32(a2, a3); + const __m128i b01 = _mm_unpacklo_epi32(b0, b1); + const __m128i b23 = _mm_unpacklo_epi32(b2, b3); + // Convert to 16b. + const __m128i a01s = _mm_unpacklo_epi8(a01, zero); + const __m128i a23s = _mm_unpacklo_epi8(a23, zero); + const __m128i b01s = _mm_unpacklo_epi8(b01, zero); + const __m128i b23s = _mm_unpacklo_epi8(b23, zero); + // subtract, square and accumulate + const __m128i d0 = _mm_subs_epi16(a01s, b01s); + const __m128i d1 = _mm_subs_epi16(a23s, b23s); + const __m128i e0 = _mm_madd_epi16(d0, d0); + const __m128i e1 = _mm_madd_epi16(d1, d1); + const __m128i sum = _mm_add_epi32(e0, e1); + + int32_t tmp[4]; + _mm_storeu_si128((__m128i*)tmp, sum); + return (tmp[3] + tmp[2] + tmp[1] + tmp[0]); +} + +//------------------------------------------------------------------------------ + +static void Mean16x4_SSE2(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 +// +// We try to match the spectral content (weighted) between source and +// reconstructed samples. + +// Hadamard transform +// Returns the weighted sum of the absolute value of transformed coefficients. +// w[] contains a row-major 4 by 4 symmetric matrix. +static int TTransform_SSE2(const uint8_t* inA, const uint8_t* inB, + const uint16_t* const w) { + int32_t sum[4]; + __m128i tmp_0, tmp_1, tmp_2, tmp_3; + const __m128i zero = _mm_setzero_si128(); + + // Load and combine inputs. + { + const __m128i inA_0 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 0]); + const __m128i inA_1 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 1]); + const __m128i inA_2 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 2]); + const __m128i inA_3 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 3]); + const __m128i inB_0 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 0]); + const __m128i inB_1 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 1]); + const __m128i inB_2 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 2]); + const __m128i inB_3 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 3]); + + // Combine inA and inB (we'll do two transforms in parallel). + const __m128i inAB_0 = _mm_unpacklo_epi32(inA_0, inB_0); + const __m128i inAB_1 = _mm_unpacklo_epi32(inA_1, inB_1); + const __m128i inAB_2 = _mm_unpacklo_epi32(inA_2, inB_2); + const __m128i inAB_3 = _mm_unpacklo_epi32(inA_3, inB_3); + tmp_0 = _mm_unpacklo_epi8(inAB_0, zero); + tmp_1 = _mm_unpacklo_epi8(inAB_1, zero); + tmp_2 = _mm_unpacklo_epi8(inAB_2, zero); + tmp_3 = _mm_unpacklo_epi8(inAB_3, zero); + // a00 a01 a02 a03 b00 b01 b02 b03 + // a10 a11 a12 a13 b10 b11 b12 b13 + // a20 a21 a22 a23 b20 b21 b22 b23 + // a30 a31 a32 a33 b30 b31 b32 b33 + } + + // Vertical pass first to avoid a transpose (vertical and horizontal passes + // are commutative because w/kWeightY is symmetric) and subsequent transpose. + { + // Calculate a and b (two 4x4 at once). + const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2); + const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3); + const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3); + const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2); + const __m128i b0 = _mm_add_epi16(a0, a1); + const __m128i b1 = _mm_add_epi16(a3, a2); + const __m128i b2 = _mm_sub_epi16(a3, a2); + const __m128i b3 = _mm_sub_epi16(a0, a1); + // a00 a01 a02 a03 b00 b01 b02 b03 + // a10 a11 a12 a13 b10 b11 b12 b13 + // a20 a21 a22 a23 b20 b21 b22 b23 + // a30 a31 a32 a33 b30 b31 b32 b33 + + // Transpose the two 4x4. + VP8Transpose_2_4x4_16b(&b0, &b1, &b2, &b3, &tmp_0, &tmp_1, &tmp_2, &tmp_3); + } + + // Horizontal pass and difference of weighted sums. + { + // Load all inputs. + const __m128i w_0 = _mm_loadu_si128((const __m128i*)&w[0]); + const __m128i w_8 = _mm_loadu_si128((const __m128i*)&w[8]); + + // Calculate a and b (two 4x4 at once). + const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2); + const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3); + const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3); + const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2); + const __m128i b0 = _mm_add_epi16(a0, a1); + const __m128i b1 = _mm_add_epi16(a3, a2); + const __m128i b2 = _mm_sub_epi16(a3, a2); + const __m128i b3 = _mm_sub_epi16(a0, a1); + + // Separate the transforms of inA and inB. + __m128i A_b0 = _mm_unpacklo_epi64(b0, b1); + __m128i A_b2 = _mm_unpacklo_epi64(b2, b3); + __m128i B_b0 = _mm_unpackhi_epi64(b0, b1); + __m128i B_b2 = _mm_unpackhi_epi64(b2, b3); + + { + const __m128i d0 = _mm_sub_epi16(zero, A_b0); + const __m128i d1 = _mm_sub_epi16(zero, A_b2); + const __m128i d2 = _mm_sub_epi16(zero, B_b0); + const __m128i d3 = _mm_sub_epi16(zero, B_b2); + A_b0 = _mm_max_epi16(A_b0, d0); // abs(v), 16b + A_b2 = _mm_max_epi16(A_b2, d1); + B_b0 = _mm_max_epi16(B_b0, d2); + B_b2 = _mm_max_epi16(B_b2, d3); + } + + // weighted sums + A_b0 = _mm_madd_epi16(A_b0, w_0); + A_b2 = _mm_madd_epi16(A_b2, w_8); + B_b0 = _mm_madd_epi16(B_b0, w_0); + B_b2 = _mm_madd_epi16(B_b2, w_8); + A_b0 = _mm_add_epi32(A_b0, A_b2); + B_b0 = _mm_add_epi32(B_b0, B_b2); + + // difference of weighted sums + A_b0 = _mm_sub_epi32(A_b0, B_b0); + _mm_storeu_si128((__m128i*)&sum[0], A_b0); + } + return sum[0] + sum[1] + sum[2] + sum[3]; +} + +static int Disto4x4_SSE2(const uint8_t* const a, const uint8_t* const b, + const uint16_t* const w) { + const int diff_sum = TTransform_SSE2(a, b, w); + return abs(diff_sum) >> 5; +} + +static int Disto16x16_SSE2(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_SSE2(a + x + y, b + x + y, w); + } + } + return D; +} + +//------------------------------------------------------------------------------ +// Quantization +// + +static WEBP_INLINE int DoQuantizeBlock_SSE2(int16_t in[16], int16_t out[16], + const uint16_t* const sharpen, + const VP8Matrix* const mtx) { + const __m128i max_coeff_2047 = _mm_set1_epi16(MAX_LEVEL); + const __m128i zero = _mm_setzero_si128(); + __m128i coeff0, coeff8; + __m128i out0, out8; + __m128i packed_out; + + // Load all inputs. + __m128i in0 = _mm_loadu_si128((__m128i*)&in[0]); + __m128i in8 = _mm_loadu_si128((__m128i*)&in[8]); + const __m128i iq0 = _mm_loadu_si128((const __m128i*)&mtx->iq_[0]); + const __m128i iq8 = _mm_loadu_si128((const __m128i*)&mtx->iq_[8]); + const __m128i q0 = _mm_loadu_si128((const __m128i*)&mtx->q_[0]); + const __m128i q8 = _mm_loadu_si128((const __m128i*)&mtx->q_[8]); + + // extract sign(in) (0x0000 if positive, 0xffff if negative) + const __m128i sign0 = _mm_cmpgt_epi16(zero, in0); + const __m128i sign8 = _mm_cmpgt_epi16(zero, in8); + + // coeff = abs(in) = (in ^ sign) - sign + coeff0 = _mm_xor_si128(in0, sign0); + coeff8 = _mm_xor_si128(in8, sign8); + coeff0 = _mm_sub_epi16(coeff0, sign0); + coeff8 = _mm_sub_epi16(coeff8, sign8); + + // coeff = abs(in) + sharpen + if (sharpen != NULL) { + const __m128i sharpen0 = _mm_loadu_si128((const __m128i*)&sharpen[0]); + const __m128i sharpen8 = _mm_loadu_si128((const __m128i*)&sharpen[8]); + coeff0 = _mm_add_epi16(coeff0, sharpen0); + coeff8 = _mm_add_epi16(coeff8, sharpen8); + } + + // out = (coeff * iQ + B) >> QFIX + { + // doing calculations with 32b precision (QFIX=17) + // out = (coeff * iQ) + const __m128i coeff_iQ0H = _mm_mulhi_epu16(coeff0, iq0); + const __m128i coeff_iQ0L = _mm_mullo_epi16(coeff0, iq0); + const __m128i coeff_iQ8H = _mm_mulhi_epu16(coeff8, iq8); + const __m128i coeff_iQ8L = _mm_mullo_epi16(coeff8, iq8); + __m128i out_00 = _mm_unpacklo_epi16(coeff_iQ0L, coeff_iQ0H); + __m128i out_04 = _mm_unpackhi_epi16(coeff_iQ0L, coeff_iQ0H); + __m128i out_08 = _mm_unpacklo_epi16(coeff_iQ8L, coeff_iQ8H); + __m128i out_12 = _mm_unpackhi_epi16(coeff_iQ8L, coeff_iQ8H); + // out = (coeff * iQ + B) + const __m128i bias_00 = _mm_loadu_si128((const __m128i*)&mtx->bias_[0]); + const __m128i bias_04 = _mm_loadu_si128((const __m128i*)&mtx->bias_[4]); + const __m128i bias_08 = _mm_loadu_si128((const __m128i*)&mtx->bias_[8]); + const __m128i bias_12 = _mm_loadu_si128((const __m128i*)&mtx->bias_[12]); + out_00 = _mm_add_epi32(out_00, bias_00); + out_04 = _mm_add_epi32(out_04, bias_04); + out_08 = _mm_add_epi32(out_08, bias_08); + out_12 = _mm_add_epi32(out_12, bias_12); + // out = QUANTDIV(coeff, iQ, B, QFIX) + out_00 = _mm_srai_epi32(out_00, QFIX); + out_04 = _mm_srai_epi32(out_04, QFIX); + out_08 = _mm_srai_epi32(out_08, QFIX); + out_12 = _mm_srai_epi32(out_12, QFIX); + + // pack result as 16b + out0 = _mm_packs_epi32(out_00, out_04); + out8 = _mm_packs_epi32(out_08, out_12); + + // if (coeff > 2047) coeff = 2047 + out0 = _mm_min_epi16(out0, max_coeff_2047); + out8 = _mm_min_epi16(out8, max_coeff_2047); + } + + // get sign back (if (sign[j]) out_n = -out_n) + out0 = _mm_xor_si128(out0, sign0); + out8 = _mm_xor_si128(out8, sign8); + out0 = _mm_sub_epi16(out0, sign0); + out8 = _mm_sub_epi16(out8, sign8); + + // in = out * Q + in0 = _mm_mullo_epi16(out0, q0); + in8 = _mm_mullo_epi16(out8, q8); + + _mm_storeu_si128((__m128i*)&in[0], in0); + _mm_storeu_si128((__m128i*)&in[8], in8); + + // zigzag the output before storing it. + // + // The zigzag pattern can almost be reproduced with a small sequence of + // shuffles. After it, we only need to swap the 7th (ending up in third + // position instead of twelfth) and 8th values. + { + __m128i outZ0, outZ8; + outZ0 = _mm_shufflehi_epi16(out0, _MM_SHUFFLE(2, 1, 3, 0)); + outZ0 = _mm_shuffle_epi32 (outZ0, _MM_SHUFFLE(3, 1, 2, 0)); + outZ0 = _mm_shufflehi_epi16(outZ0, _MM_SHUFFLE(3, 1, 0, 2)); + outZ8 = _mm_shufflelo_epi16(out8, _MM_SHUFFLE(3, 0, 2, 1)); + outZ8 = _mm_shuffle_epi32 (outZ8, _MM_SHUFFLE(3, 1, 2, 0)); + outZ8 = _mm_shufflelo_epi16(outZ8, _MM_SHUFFLE(1, 3, 2, 0)); + _mm_storeu_si128((__m128i*)&out[0], outZ0); + _mm_storeu_si128((__m128i*)&out[8], outZ8); + packed_out = _mm_packs_epi16(outZ0, outZ8); + } + { + const int16_t outZ_12 = out[12]; + const int16_t outZ_3 = out[3]; + out[3] = outZ_12; + out[12] = outZ_3; + } + + // detect if all 'out' values are zeroes or not + return (_mm_movemask_epi8(_mm_cmpeq_epi8(packed_out, zero)) != 0xffff); +} + +static int QuantizeBlock_SSE2(int16_t in[16], int16_t out[16], + const VP8Matrix* const mtx) { + return DoQuantizeBlock_SSE2(in, out, &mtx->sharpen_[0], mtx); +} + +static int QuantizeBlockWHT_SSE2(int16_t in[16], int16_t out[16], + const VP8Matrix* const mtx) { + return DoQuantizeBlock_SSE2(in, out, NULL, mtx); +} + +static int Quantize2Blocks_SSE2(int16_t in[32], int16_t out[32], + const VP8Matrix* const mtx) { + int nz; + const uint16_t* const sharpen = &mtx->sharpen_[0]; + nz = DoQuantizeBlock_SSE2(in + 0 * 16, out + 0 * 16, sharpen, mtx) << 0; + nz |= DoQuantizeBlock_SSE2(in + 1 * 16, out + 1 * 16, sharpen, mtx) << 1; + return nz; +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspInitSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitSSE2(void) { + VP8CollectHistogram = CollectHistogram_SSE2; + VP8EncPredLuma16 = Intra16Preds_SSE2; + VP8EncPredChroma8 = IntraChromaPreds_SSE2; + VP8EncPredLuma4 = Intra4Preds_SSE2; + VP8EncQuantizeBlock = QuantizeBlock_SSE2; + VP8EncQuantize2Blocks = Quantize2Blocks_SSE2; + VP8EncQuantizeBlockWHT = QuantizeBlockWHT_SSE2; + VP8ITransform = ITransform_SSE2; + VP8FTransform = FTransform_SSE2; + VP8FTransform2 = FTransform2_SSE2; + VP8FTransformWHT = FTransformWHT_SSE2; + VP8SSE16x16 = SSE16x16_SSE2; + VP8SSE16x8 = SSE16x8_SSE2; + VP8SSE8x8 = SSE8x8_SSE2; + VP8SSE4x4 = SSE4x4_SSE2; + VP8TDisto4x4 = Disto4x4_SSE2; + VP8TDisto16x16 = Disto16x16_SSE2; + VP8Mean16x4 = Mean16x4_SSE2; +} + +#else // !WEBP_USE_SSE2 + +WEBP_DSP_INIT_STUB(VP8EncDspInitSSE2) + +#endif // WEBP_USE_SSE2 diff --git a/internal/libwebp-1.2.1/src/dsp/enc_sse41.c b/internal/libwebp-1.2.1/src/dsp/enc_sse41.c new file mode 100644 index 0000000..924035a --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/enc_sse41.c @@ -0,0 +1,339 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE4 version of some encoding functions. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE41) +#include +#include // for abs() + +#include "src/dsp/common_sse2.h" +#include "src/enc/vp8i_enc.h" + +//------------------------------------------------------------------------------ +// Compute susceptibility based on DCT-coeff histograms. + +static void CollectHistogram_SSE41(const uint8_t* ref, const uint8_t* pred, + int start_block, int end_block, + VP8Histogram* const histo) { + const __m128i max_coeff_thresh = _mm_set1_epi16(MAX_COEFF_THRESH); + int j; + int distribution[MAX_COEFF_THRESH + 1] = { 0 }; + for (j = start_block; j < end_block; ++j) { + int16_t out[16]; + int k; + + VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out); + + // Convert coefficients to bin (within out[]). + { + // Load. + const __m128i out0 = _mm_loadu_si128((__m128i*)&out[0]); + const __m128i out1 = _mm_loadu_si128((__m128i*)&out[8]); + // v = abs(out) >> 3 + const __m128i abs0 = _mm_abs_epi16(out0); + const __m128i abs1 = _mm_abs_epi16(out1); + const __m128i v0 = _mm_srai_epi16(abs0, 3); + const __m128i v1 = _mm_srai_epi16(abs1, 3); + // bin = min(v, MAX_COEFF_THRESH) + const __m128i bin0 = _mm_min_epi16(v0, max_coeff_thresh); + const __m128i bin1 = _mm_min_epi16(v1, max_coeff_thresh); + // Store. + _mm_storeu_si128((__m128i*)&out[0], bin0); + _mm_storeu_si128((__m128i*)&out[8], bin1); + } + + // Convert coefficients to bin. + for (k = 0; k < 16; ++k) { + ++distribution[out[k]]; + } + } + VP8SetHistogramData(distribution, histo); +} + +//------------------------------------------------------------------------------ +// Texture distortion +// +// We try to match the spectral content (weighted) between source and +// reconstructed samples. + +// Hadamard transform +// Returns the weighted sum of the absolute value of transformed coefficients. +// w[] contains a row-major 4 by 4 symmetric matrix. +static int TTransform_SSE41(const uint8_t* inA, const uint8_t* inB, + const uint16_t* const w) { + int32_t sum[4]; + __m128i tmp_0, tmp_1, tmp_2, tmp_3; + + // Load and combine inputs. + { + const __m128i inA_0 = _mm_loadu_si128((const __m128i*)&inA[BPS * 0]); + const __m128i inA_1 = _mm_loadu_si128((const __m128i*)&inA[BPS * 1]); + const __m128i inA_2 = _mm_loadu_si128((const __m128i*)&inA[BPS * 2]); + // In SSE4.1, with gcc 4.8 at least (maybe other versions), + // _mm_loadu_si128 is faster than _mm_loadl_epi64. But for the last lump + // of inA and inB, _mm_loadl_epi64 is still used not to have an out of + // bound read. + const __m128i inA_3 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 3]); + const __m128i inB_0 = _mm_loadu_si128((const __m128i*)&inB[BPS * 0]); + const __m128i inB_1 = _mm_loadu_si128((const __m128i*)&inB[BPS * 1]); + const __m128i inB_2 = _mm_loadu_si128((const __m128i*)&inB[BPS * 2]); + const __m128i inB_3 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 3]); + + // Combine inA and inB (we'll do two transforms in parallel). + const __m128i inAB_0 = _mm_unpacklo_epi32(inA_0, inB_0); + const __m128i inAB_1 = _mm_unpacklo_epi32(inA_1, inB_1); + const __m128i inAB_2 = _mm_unpacklo_epi32(inA_2, inB_2); + const __m128i inAB_3 = _mm_unpacklo_epi32(inA_3, inB_3); + tmp_0 = _mm_cvtepu8_epi16(inAB_0); + tmp_1 = _mm_cvtepu8_epi16(inAB_1); + tmp_2 = _mm_cvtepu8_epi16(inAB_2); + tmp_3 = _mm_cvtepu8_epi16(inAB_3); + // a00 a01 a02 a03 b00 b01 b02 b03 + // a10 a11 a12 a13 b10 b11 b12 b13 + // a20 a21 a22 a23 b20 b21 b22 b23 + // a30 a31 a32 a33 b30 b31 b32 b33 + } + + // Vertical pass first to avoid a transpose (vertical and horizontal passes + // are commutative because w/kWeightY is symmetric) and subsequent transpose. + { + // Calculate a and b (two 4x4 at once). + const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2); + const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3); + const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3); + const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2); + const __m128i b0 = _mm_add_epi16(a0, a1); + const __m128i b1 = _mm_add_epi16(a3, a2); + const __m128i b2 = _mm_sub_epi16(a3, a2); + const __m128i b3 = _mm_sub_epi16(a0, a1); + // a00 a01 a02 a03 b00 b01 b02 b03 + // a10 a11 a12 a13 b10 b11 b12 b13 + // a20 a21 a22 a23 b20 b21 b22 b23 + // a30 a31 a32 a33 b30 b31 b32 b33 + + // Transpose the two 4x4. + VP8Transpose_2_4x4_16b(&b0, &b1, &b2, &b3, &tmp_0, &tmp_1, &tmp_2, &tmp_3); + } + + // Horizontal pass and difference of weighted sums. + { + // Load all inputs. + const __m128i w_0 = _mm_loadu_si128((const __m128i*)&w[0]); + const __m128i w_8 = _mm_loadu_si128((const __m128i*)&w[8]); + + // Calculate a and b (two 4x4 at once). + const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2); + const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3); + const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3); + const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2); + const __m128i b0 = _mm_add_epi16(a0, a1); + const __m128i b1 = _mm_add_epi16(a3, a2); + const __m128i b2 = _mm_sub_epi16(a3, a2); + const __m128i b3 = _mm_sub_epi16(a0, a1); + + // Separate the transforms of inA and inB. + __m128i A_b0 = _mm_unpacklo_epi64(b0, b1); + __m128i A_b2 = _mm_unpacklo_epi64(b2, b3); + __m128i B_b0 = _mm_unpackhi_epi64(b0, b1); + __m128i B_b2 = _mm_unpackhi_epi64(b2, b3); + + A_b0 = _mm_abs_epi16(A_b0); + A_b2 = _mm_abs_epi16(A_b2); + B_b0 = _mm_abs_epi16(B_b0); + B_b2 = _mm_abs_epi16(B_b2); + + // weighted sums + A_b0 = _mm_madd_epi16(A_b0, w_0); + A_b2 = _mm_madd_epi16(A_b2, w_8); + B_b0 = _mm_madd_epi16(B_b0, w_0); + B_b2 = _mm_madd_epi16(B_b2, w_8); + A_b0 = _mm_add_epi32(A_b0, A_b2); + B_b0 = _mm_add_epi32(B_b0, B_b2); + + // difference of weighted sums + A_b2 = _mm_sub_epi32(A_b0, B_b0); + _mm_storeu_si128((__m128i*)&sum[0], A_b2); + } + return sum[0] + sum[1] + sum[2] + sum[3]; +} + +static int Disto4x4_SSE41(const uint8_t* const a, const uint8_t* const b, + const uint16_t* const w) { + const int diff_sum = TTransform_SSE41(a, b, w); + return abs(diff_sum) >> 5; +} + +static int Disto16x16_SSE41(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_SSE41(a + x + y, b + x + y, w); + } + } + return D; +} + +//------------------------------------------------------------------------------ +// Quantization +// + +// Generates a pshufb constant for shuffling 16b words. +#define PSHUFB_CST(A,B,C,D,E,F,G,H) \ + _mm_set_epi8(2 * (H) + 1, 2 * (H) + 0, 2 * (G) + 1, 2 * (G) + 0, \ + 2 * (F) + 1, 2 * (F) + 0, 2 * (E) + 1, 2 * (E) + 0, \ + 2 * (D) + 1, 2 * (D) + 0, 2 * (C) + 1, 2 * (C) + 0, \ + 2 * (B) + 1, 2 * (B) + 0, 2 * (A) + 1, 2 * (A) + 0) + +static WEBP_INLINE int DoQuantizeBlock_SSE41(int16_t in[16], int16_t out[16], + const uint16_t* const sharpen, + const VP8Matrix* const mtx) { + const __m128i max_coeff_2047 = _mm_set1_epi16(MAX_LEVEL); + const __m128i zero = _mm_setzero_si128(); + __m128i out0, out8; + __m128i packed_out; + + // Load all inputs. + __m128i in0 = _mm_loadu_si128((__m128i*)&in[0]); + __m128i in8 = _mm_loadu_si128((__m128i*)&in[8]); + const __m128i iq0 = _mm_loadu_si128((const __m128i*)&mtx->iq_[0]); + const __m128i iq8 = _mm_loadu_si128((const __m128i*)&mtx->iq_[8]); + const __m128i q0 = _mm_loadu_si128((const __m128i*)&mtx->q_[0]); + const __m128i q8 = _mm_loadu_si128((const __m128i*)&mtx->q_[8]); + + // coeff = abs(in) + __m128i coeff0 = _mm_abs_epi16(in0); + __m128i coeff8 = _mm_abs_epi16(in8); + + // coeff = abs(in) + sharpen + if (sharpen != NULL) { + const __m128i sharpen0 = _mm_loadu_si128((const __m128i*)&sharpen[0]); + const __m128i sharpen8 = _mm_loadu_si128((const __m128i*)&sharpen[8]); + coeff0 = _mm_add_epi16(coeff0, sharpen0); + coeff8 = _mm_add_epi16(coeff8, sharpen8); + } + + // out = (coeff * iQ + B) >> QFIX + { + // doing calculations with 32b precision (QFIX=17) + // out = (coeff * iQ) + const __m128i coeff_iQ0H = _mm_mulhi_epu16(coeff0, iq0); + const __m128i coeff_iQ0L = _mm_mullo_epi16(coeff0, iq0); + const __m128i coeff_iQ8H = _mm_mulhi_epu16(coeff8, iq8); + const __m128i coeff_iQ8L = _mm_mullo_epi16(coeff8, iq8); + __m128i out_00 = _mm_unpacklo_epi16(coeff_iQ0L, coeff_iQ0H); + __m128i out_04 = _mm_unpackhi_epi16(coeff_iQ0L, coeff_iQ0H); + __m128i out_08 = _mm_unpacklo_epi16(coeff_iQ8L, coeff_iQ8H); + __m128i out_12 = _mm_unpackhi_epi16(coeff_iQ8L, coeff_iQ8H); + // out = (coeff * iQ + B) + const __m128i bias_00 = _mm_loadu_si128((const __m128i*)&mtx->bias_[0]); + const __m128i bias_04 = _mm_loadu_si128((const __m128i*)&mtx->bias_[4]); + const __m128i bias_08 = _mm_loadu_si128((const __m128i*)&mtx->bias_[8]); + const __m128i bias_12 = _mm_loadu_si128((const __m128i*)&mtx->bias_[12]); + out_00 = _mm_add_epi32(out_00, bias_00); + out_04 = _mm_add_epi32(out_04, bias_04); + out_08 = _mm_add_epi32(out_08, bias_08); + out_12 = _mm_add_epi32(out_12, bias_12); + // out = QUANTDIV(coeff, iQ, B, QFIX) + out_00 = _mm_srai_epi32(out_00, QFIX); + out_04 = _mm_srai_epi32(out_04, QFIX); + out_08 = _mm_srai_epi32(out_08, QFIX); + out_12 = _mm_srai_epi32(out_12, QFIX); + + // pack result as 16b + out0 = _mm_packs_epi32(out_00, out_04); + out8 = _mm_packs_epi32(out_08, out_12); + + // if (coeff > 2047) coeff = 2047 + out0 = _mm_min_epi16(out0, max_coeff_2047); + out8 = _mm_min_epi16(out8, max_coeff_2047); + } + + // put sign back + out0 = _mm_sign_epi16(out0, in0); + out8 = _mm_sign_epi16(out8, in8); + + // in = out * Q + in0 = _mm_mullo_epi16(out0, q0); + in8 = _mm_mullo_epi16(out8, q8); + + _mm_storeu_si128((__m128i*)&in[0], in0); + _mm_storeu_si128((__m128i*)&in[8], in8); + + // zigzag the output before storing it. The re-ordering is: + // 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15 + // -> 0 1 4[8]5 2 3 6 | 9 12 13 10 [7]11 14 15 + // There's only two misplaced entries ([8] and [7]) that are crossing the + // reg's boundaries. + // We use pshufb instead of pshuflo/pshufhi. + { + const __m128i kCst_lo = PSHUFB_CST(0, 1, 4, -1, 5, 2, 3, 6); + const __m128i kCst_7 = PSHUFB_CST(-1, -1, -1, -1, 7, -1, -1, -1); + const __m128i tmp_lo = _mm_shuffle_epi8(out0, kCst_lo); + const __m128i tmp_7 = _mm_shuffle_epi8(out0, kCst_7); // extract #7 + const __m128i kCst_hi = PSHUFB_CST(1, 4, 5, 2, -1, 3, 6, 7); + const __m128i kCst_8 = PSHUFB_CST(-1, -1, -1, 0, -1, -1, -1, -1); + const __m128i tmp_hi = _mm_shuffle_epi8(out8, kCst_hi); + const __m128i tmp_8 = _mm_shuffle_epi8(out8, kCst_8); // extract #8 + const __m128i out_z0 = _mm_or_si128(tmp_lo, tmp_8); + const __m128i out_z8 = _mm_or_si128(tmp_hi, tmp_7); + _mm_storeu_si128((__m128i*)&out[0], out_z0); + _mm_storeu_si128((__m128i*)&out[8], out_z8); + packed_out = _mm_packs_epi16(out_z0, out_z8); + } + + // detect if all 'out' values are zeroes or not + return (_mm_movemask_epi8(_mm_cmpeq_epi8(packed_out, zero)) != 0xffff); +} + +#undef PSHUFB_CST + +static int QuantizeBlock_SSE41(int16_t in[16], int16_t out[16], + const VP8Matrix* const mtx) { + return DoQuantizeBlock_SSE41(in, out, &mtx->sharpen_[0], mtx); +} + +static int QuantizeBlockWHT_SSE41(int16_t in[16], int16_t out[16], + const VP8Matrix* const mtx) { + return DoQuantizeBlock_SSE41(in, out, NULL, mtx); +} + +static int Quantize2Blocks_SSE41(int16_t in[32], int16_t out[32], + const VP8Matrix* const mtx) { + int nz; + const uint16_t* const sharpen = &mtx->sharpen_[0]; + nz = DoQuantizeBlock_SSE41(in + 0 * 16, out + 0 * 16, sharpen, mtx) << 0; + nz |= DoQuantizeBlock_SSE41(in + 1 * 16, out + 1 * 16, sharpen, mtx) << 1; + return nz; +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8EncDspInitSSE41(void); +WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitSSE41(void) { + VP8CollectHistogram = CollectHistogram_SSE41; + VP8EncQuantizeBlock = QuantizeBlock_SSE41; + VP8EncQuantize2Blocks = Quantize2Blocks_SSE41; + VP8EncQuantizeBlockWHT = QuantizeBlockWHT_SSE41; + VP8TDisto4x4 = Disto4x4_SSE41; + VP8TDisto16x16 = Disto16x16_SSE41; +} + +#else // !WEBP_USE_SSE41 + +WEBP_DSP_INIT_STUB(VP8EncDspInitSSE41) + +#endif // WEBP_USE_SSE41 diff --git a/internal/libwebp-1.2.1/src/dsp/filters.c b/internal/libwebp-1.2.1/src/dsp/filters.c new file mode 100644 index 0000000..4506567 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/filters.c @@ -0,0 +1,287 @@ +// 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) + +#include "src/dsp/dsp.h" +#include +#include +#include + +//------------------------------------------------------------------------------ +// Helpful macro. + +# 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. + +#if !WEBP_NEON_OMIT_C_CODE +static WEBP_INLINE void PredictLine_C(const uint8_t* src, const uint8_t* pred, + uint8_t* dst, int length, int inverse) { + int i; + if (inverse) { + for (i = 0; i < length; ++i) dst[i] = (uint8_t)(src[i] + pred[i]); + } else { + for (i = 0; i < length; ++i) dst[i] = (uint8_t)(src[i] - pred[i]); + } +} + +//------------------------------------------------------------------------------ +// Horizontal filter. + +static WEBP_INLINE void DoHorizontalFilter_C(const uint8_t* in, + int width, int height, int stride, + int row, int num_rows, + int inverse, uint8_t* out) { + const uint8_t* preds; + const size_t start_offset = row * stride; + const int last_row = row + num_rows; + SANITY_CHECK(in, out); + in += start_offset; + out += start_offset; + preds = inverse ? out : in; + + if (row == 0) { + // Leftmost pixel is the same as input for topmost scanline. + out[0] = in[0]; + PredictLine_C(in + 1, preds, out + 1, width - 1, inverse); + row = 1; + preds += stride; + in += stride; + out += stride; + } + + // Filter line-by-line. + while (row < last_row) { + // Leftmost pixel is predicted from above. + PredictLine_C(in, preds - stride, out, 1, inverse); + PredictLine_C(in + 1, preds, out + 1, width - 1, inverse); + ++row; + preds += stride; + in += stride; + out += stride; + } +} + +//------------------------------------------------------------------------------ +// Vertical filter. + +static WEBP_INLINE void DoVerticalFilter_C(const uint8_t* in, + int width, int height, int stride, + int row, int num_rows, + int inverse, uint8_t* out) { + const uint8_t* preds; + const size_t start_offset = row * stride; + const int last_row = row + num_rows; + SANITY_CHECK(in, out); + in += start_offset; + out += start_offset; + preds = inverse ? out : in; + + if (row == 0) { + // Very first top-left pixel is copied. + out[0] = in[0]; + // Rest of top scan-line is left-predicted. + PredictLine_C(in + 1, preds, out + 1, width - 1, inverse); + row = 1; + in += stride; + out += stride; + } else { + // We are starting from in-between. Make sure 'preds' points to prev row. + preds -= stride; + } + + // Filter line-by-line. + while (row < last_row) { + PredictLine_C(in, preds, out, width, inverse); + ++row; + preds += stride; + in += stride; + out += stride; + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +//------------------------------------------------------------------------------ +// 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 +} + +#if !WEBP_NEON_OMIT_C_CODE +static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in, + int width, int height, int stride, + int row, int num_rows, + int inverse, uint8_t* out) { + const uint8_t* preds; + const size_t start_offset = row * stride; + const int last_row = row + num_rows; + SANITY_CHECK(in, out); + in += start_offset; + out += start_offset; + preds = inverse ? out : in; + + // left prediction for top scan-line + if (row == 0) { + out[0] = in[0]; + PredictLine_C(in + 1, preds, out + 1, width - 1, inverse); + row = 1; + preds += stride; + in += stride; + out += stride; + } + + // Filter line-by-line. + while (row < last_row) { + int w; + // leftmost pixel: predict from above. + PredictLine_C(in, preds - stride, out, 1, inverse); + for (w = 1; w < width; ++w) { + const int pred = GradientPredictor_C(preds[w - 1], + preds[w - stride], + preds[w - stride - 1]); + out[w] = (uint8_t)(in[w] + (inverse ? pred : -pred)); + } + ++row; + preds += stride; + in += stride; + out += stride; + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +#undef SANITY_CHECK + +//------------------------------------------------------------------------------ + +#if !WEBP_NEON_OMIT_C_CODE +static void HorizontalFilter_C(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoHorizontalFilter_C(data, width, height, stride, 0, height, 0, + filtered_data); +} + +static void VerticalFilter_C(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoVerticalFilter_C(data, width, height, stride, 0, height, 0, filtered_data); +} + +static void GradientFilter_C(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoGradientFilter_C(data, width, height, stride, 0, height, 0, filtered_data); +} +#endif // !WEBP_NEON_OMIT_C_CODE + +//------------------------------------------------------------------------------ + +static void HorizontalUnfilter_C(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + uint8_t pred = (prev == NULL) ? 0 : prev[0]; + int i; + for (i = 0; i < width; ++i) { + out[i] = (uint8_t)(pred + in[i]); + pred = out[i]; + } +} + +#if !WEBP_NEON_OMIT_C_CODE +static void VerticalUnfilter_C(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter_C(NULL, in, out, width); + } else { + int i; + for (i = 0; i < width; ++i) out[i] = (uint8_t)(prev[i] + in[i]); + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +static void GradientUnfilter_C(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter_C(NULL, in, out, width); + } else { + uint8_t top = prev[0], top_left = top, left = top; + int i; + for (i = 0; i < width; ++i) { + top = prev[i]; // need to read this first, in case prev==out + left = (uint8_t)(in[i] + GradientPredictor_C(left, top, top_left)); + top_left = top; + out[i] = left; + } + } +} + +//------------------------------------------------------------------------------ +// Init function + +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); + +WEBP_DSP_INIT_FUNC(VP8FiltersInit) { + WebPUnfilters[WEBP_FILTER_NONE] = NULL; +#if !WEBP_NEON_OMIT_C_CODE + WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_C; + WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_C; +#endif + WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_C; + + WebPFilters[WEBP_FILTER_NONE] = NULL; +#if !WEBP_NEON_OMIT_C_CODE + WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_C; + WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_C; + WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_C; +#endif + + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + VP8FiltersInitSSE2(); + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + VP8FiltersInitMIPSdspR2(); + } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8FiltersInitMSA(); + } +#endif + } + +#if defined(WEBP_HAVE_NEON) + if (WEBP_NEON_OMIT_C_CODE || + (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) { + VP8FiltersInitNEON(); + } +#endif + + assert(WebPUnfilters[WEBP_FILTER_HORIZONTAL] != NULL); + assert(WebPUnfilters[WEBP_FILTER_VERTICAL] != NULL); + assert(WebPUnfilters[WEBP_FILTER_GRADIENT] != NULL); + assert(WebPFilters[WEBP_FILTER_HORIZONTAL] != NULL); + assert(WebPFilters[WEBP_FILTER_VERTICAL] != NULL); + assert(WebPFilters[WEBP_FILTER_GRADIENT] != NULL); +} diff --git a/internal/libwebp-1.2.1/src/dsp/filters_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/filters_mips_dsp_r2.c new file mode 100644 index 0000000..9382b12 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/filters_mips_dsp_r2.c @@ -0,0 +1,402 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Spatial prediction using various filters +// +// Author(s): Branimir Vasic (branimir.vasic@imgtec.com) +// Djordje Pesut (djordje.pesut@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) + +#include "src/dsp/dsp.h" +#include +#include +#include + +//------------------------------------------------------------------------------ +// Helpful macro. + +# 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. + +#define DO_PREDICT_LINE(SRC, DST, LENGTH, INVERSE) do { \ + const uint8_t* psrc = (uint8_t*)(SRC); \ + uint8_t* pdst = (uint8_t*)(DST); \ + const int ilength = (int)(LENGTH); \ + int temp0, temp1, temp2, temp3, temp4, temp5, temp6; \ + __asm__ volatile ( \ + ".set push \n\t" \ + ".set noreorder \n\t" \ + "srl %[temp0], %[length], 2 \n\t" \ + "beqz %[temp0], 4f \n\t" \ + " andi %[temp6], %[length], 3 \n\t" \ + ".if " #INVERSE " \n\t" \ + "1: \n\t" \ + "lbu %[temp1], -1(%[dst]) \n\t" \ + "lbu %[temp2], 0(%[src]) \n\t" \ + "lbu %[temp3], 1(%[src]) \n\t" \ + "lbu %[temp4], 2(%[src]) \n\t" \ + "lbu %[temp5], 3(%[src]) \n\t" \ + "addu %[temp1], %[temp1], %[temp2] \n\t" \ + "addu %[temp2], %[temp1], %[temp3] \n\t" \ + "addu %[temp3], %[temp2], %[temp4] \n\t" \ + "addu %[temp4], %[temp3], %[temp5] \n\t" \ + "sb %[temp1], 0(%[dst]) \n\t" \ + "sb %[temp2], 1(%[dst]) \n\t" \ + "sb %[temp3], 2(%[dst]) \n\t" \ + "sb %[temp4], 3(%[dst]) \n\t" \ + "addiu %[src], %[src], 4 \n\t" \ + "addiu %[temp0], %[temp0], -1 \n\t" \ + "bnez %[temp0], 1b \n\t" \ + " addiu %[dst], %[dst], 4 \n\t" \ + ".else \n\t" \ + "1: \n\t" \ + "ulw %[temp1], -1(%[src]) \n\t" \ + "ulw %[temp2], 0(%[src]) \n\t" \ + "addiu %[src], %[src], 4 \n\t" \ + "addiu %[temp0], %[temp0], -1 \n\t" \ + "subu.qb %[temp3], %[temp2], %[temp1] \n\t" \ + "usw %[temp3], 0(%[dst]) \n\t" \ + "bnez %[temp0], 1b \n\t" \ + " addiu %[dst], %[dst], 4 \n\t" \ + ".endif \n\t" \ + "4: \n\t" \ + "beqz %[temp6], 3f \n\t" \ + " nop \n\t" \ + "2: \n\t" \ + "lbu %[temp2], 0(%[src]) \n\t" \ + ".if " #INVERSE " \n\t" \ + "lbu %[temp1], -1(%[dst]) \n\t" \ + "addu %[temp3], %[temp1], %[temp2] \n\t" \ + ".else \n\t" \ + "lbu %[temp1], -1(%[src]) \n\t" \ + "subu %[temp3], %[temp1], %[temp2] \n\t" \ + ".endif \n\t" \ + "addiu %[src], %[src], 1 \n\t" \ + "sb %[temp3], 0(%[dst]) \n\t" \ + "addiu %[temp6], %[temp6], -1 \n\t" \ + "bnez %[temp6], 2b \n\t" \ + " addiu %[dst], %[dst], 1 \n\t" \ + "3: \n\t" \ + ".set pop \n\t" \ + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \ + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \ + [temp6]"=&r"(temp6), [dst]"+&r"(pdst), [src]"+&r"(psrc) \ + : [length]"r"(ilength) \ + : "memory" \ + ); \ + } while (0) + +static WEBP_INLINE void PredictLine_MIPSdspR2(const uint8_t* src, uint8_t* dst, + int length) { + DO_PREDICT_LINE(src, dst, length, 0); +} + +#define DO_PREDICT_LINE_VERTICAL(SRC, PRED, DST, LENGTH, INVERSE) do { \ + const uint8_t* psrc = (uint8_t*)(SRC); \ + const uint8_t* ppred = (uint8_t*)(PRED); \ + uint8_t* pdst = (uint8_t*)(DST); \ + const int ilength = (int)(LENGTH); \ + int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; \ + __asm__ volatile ( \ + ".set push \n\t" \ + ".set noreorder \n\t" \ + "srl %[temp0], %[length], 0x3 \n\t" \ + "beqz %[temp0], 4f \n\t" \ + " andi %[temp7], %[length], 0x7 \n\t" \ + "1: \n\t" \ + "ulw %[temp1], 0(%[src]) \n\t" \ + "ulw %[temp2], 0(%[pred]) \n\t" \ + "ulw %[temp3], 4(%[src]) \n\t" \ + "ulw %[temp4], 4(%[pred]) \n\t" \ + "addiu %[src], %[src], 8 \n\t" \ + ".if " #INVERSE " \n\t" \ + "addu.qb %[temp5], %[temp1], %[temp2] \n\t" \ + "addu.qb %[temp6], %[temp3], %[temp4] \n\t" \ + ".else \n\t" \ + "subu.qb %[temp5], %[temp1], %[temp2] \n\t" \ + "subu.qb %[temp6], %[temp3], %[temp4] \n\t" \ + ".endif \n\t" \ + "addiu %[pred], %[pred], 8 \n\t" \ + "usw %[temp5], 0(%[dst]) \n\t" \ + "usw %[temp6], 4(%[dst]) \n\t" \ + "addiu %[temp0], %[temp0], -1 \n\t" \ + "bnez %[temp0], 1b \n\t" \ + " addiu %[dst], %[dst], 8 \n\t" \ + "4: \n\t" \ + "beqz %[temp7], 3f \n\t" \ + " nop \n\t" \ + "2: \n\t" \ + "lbu %[temp1], 0(%[src]) \n\t" \ + "lbu %[temp2], 0(%[pred]) \n\t" \ + "addiu %[src], %[src], 1 \n\t" \ + "addiu %[pred], %[pred], 1 \n\t" \ + ".if " #INVERSE " \n\t" \ + "addu %[temp3], %[temp1], %[temp2] \n\t" \ + ".else \n\t" \ + "subu %[temp3], %[temp1], %[temp2] \n\t" \ + ".endif \n\t" \ + "sb %[temp3], 0(%[dst]) \n\t" \ + "addiu %[temp7], %[temp7], -1 \n\t" \ + "bnez %[temp7], 2b \n\t" \ + " addiu %[dst], %[dst], 1 \n\t" \ + "3: \n\t" \ + ".set pop \n\t" \ + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \ + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \ + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [pred]"+&r"(ppred), \ + [dst]"+&r"(pdst), [src]"+&r"(psrc) \ + : [length]"r"(ilength) \ + : "memory" \ + ); \ + } while (0) + +#define PREDICT_LINE_ONE_PASS(SRC, PRED, DST) do { \ + int temp1, temp2, temp3; \ + __asm__ volatile ( \ + "lbu %[temp1], 0(%[src]) \n\t" \ + "lbu %[temp2], 0(%[pred]) \n\t" \ + "subu %[temp3], %[temp1], %[temp2] \n\t" \ + "sb %[temp3], 0(%[dst]) \n\t" \ + : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3) \ + : [pred]"r"((PRED)), [dst]"r"((DST)), [src]"r"((SRC)) \ + : "memory" \ + ); \ + } while (0) + +//------------------------------------------------------------------------------ +// Horizontal filter. + +#define FILTER_LINE_BY_LINE do { \ + while (row < last_row) { \ + PREDICT_LINE_ONE_PASS(in, preds - stride, out); \ + DO_PREDICT_LINE(in + 1, out + 1, width - 1, 0); \ + ++row; \ + preds += stride; \ + in += stride; \ + out += stride; \ + } \ + } while (0) + +static WEBP_INLINE void DoHorizontalFilter_MIPSdspR2(const uint8_t* in, + int width, int height, + int stride, + int row, int num_rows, + uint8_t* out) { + const uint8_t* preds; + const size_t start_offset = row * stride; + const int last_row = row + num_rows; + SANITY_CHECK(in, out); + in += start_offset; + out += start_offset; + preds = in; + + if (row == 0) { + // Leftmost pixel is the same as input for topmost scanline. + out[0] = in[0]; + PredictLine_MIPSdspR2(in + 1, out + 1, width - 1); + row = 1; + preds += stride; + in += stride; + out += stride; + } + + // Filter line-by-line. + FILTER_LINE_BY_LINE; +} +#undef FILTER_LINE_BY_LINE + +static void HorizontalFilter_MIPSdspR2(const uint8_t* data, + int width, int height, + int stride, uint8_t* filtered_data) { + DoHorizontalFilter_MIPSdspR2(data, width, height, stride, 0, height, + filtered_data); +} + +//------------------------------------------------------------------------------ +// Vertical filter. + +#define FILTER_LINE_BY_LINE do { \ + while (row < last_row) { \ + DO_PREDICT_LINE_VERTICAL(in, preds, out, width, 0); \ + ++row; \ + preds += stride; \ + in += stride; \ + out += stride; \ + } \ + } while (0) + +static WEBP_INLINE void DoVerticalFilter_MIPSdspR2(const uint8_t* in, + int width, int height, + int stride, + int row, int num_rows, + uint8_t* out) { + const uint8_t* preds; + const size_t start_offset = row * stride; + const int last_row = row + num_rows; + SANITY_CHECK(in, out); + in += start_offset; + out += start_offset; + preds = in; + + if (row == 0) { + // Very first top-left pixel is copied. + out[0] = in[0]; + // Rest of top scan-line is left-predicted. + PredictLine_MIPSdspR2(in + 1, out + 1, width - 1); + row = 1; + in += stride; + out += stride; + } else { + // We are starting from in-between. Make sure 'preds' points to prev row. + preds -= stride; + } + + // Filter line-by-line. + FILTER_LINE_BY_LINE; +} +#undef FILTER_LINE_BY_LINE + +static void VerticalFilter_MIPSdspR2(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoVerticalFilter_MIPSdspR2(data, width, height, stride, 0, height, + filtered_data); +} + +//------------------------------------------------------------------------------ +// Gradient filter. + +static int GradientPredictor_MIPSdspR2(uint8_t a, uint8_t b, uint8_t c) { + int temp0; + __asm__ volatile ( + "addu %[temp0], %[a], %[b] \n\t" + "subu %[temp0], %[temp0], %[c] \n\t" + "shll_s.w %[temp0], %[temp0], 23 \n\t" + "precrqu_s.qb.ph %[temp0], %[temp0], $zero \n\t" + "srl %[temp0], %[temp0], 24 \n\t" + : [temp0]"=&r"(temp0) + : [a]"r"(a),[b]"r"(b),[c]"r"(c) + ); + return temp0; +} + +#define FILTER_LINE_BY_LINE(PREDS, OPERATION) do { \ + while (row < last_row) { \ + int w; \ + PREDICT_LINE_ONE_PASS(in, PREDS - stride, out); \ + for (w = 1; w < width; ++w) { \ + const int pred = GradientPredictor_MIPSdspR2(PREDS[w - 1], \ + PREDS[w - stride], \ + PREDS[w - stride - 1]); \ + out[w] = in[w] OPERATION pred; \ + } \ + ++row; \ + in += stride; \ + out += stride; \ + } \ + } while (0) + +static void DoGradientFilter_MIPSdspR2(const uint8_t* in, + int width, int height, int stride, + int row, int num_rows, uint8_t* out) { + const uint8_t* preds; + const size_t start_offset = row * stride; + const int last_row = row + num_rows; + SANITY_CHECK(in, out); + in += start_offset; + out += start_offset; + preds = in; + + // left prediction for top scan-line + if (row == 0) { + out[0] = in[0]; + PredictLine_MIPSdspR2(in + 1, out + 1, width - 1); + row = 1; + preds += stride; + in += stride; + out += stride; + } + + // Filter line-by-line. + FILTER_LINE_BY_LINE(in, -); +} +#undef FILTER_LINE_BY_LINE + +static void GradientFilter_MIPSdspR2(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoGradientFilter_MIPSdspR2(data, width, height, stride, 0, height, + filtered_data); +} + +//------------------------------------------------------------------------------ + +static void HorizontalUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + out[0] = in[0] + (prev == NULL ? 0 : prev[0]); + DO_PREDICT_LINE(in + 1, out + 1, width - 1, 1); +} + +static void VerticalUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter_MIPSdspR2(NULL, in, out, width); + } else { + DO_PREDICT_LINE_VERTICAL(in, prev, out, width, 1); + } +} + +static void GradientUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter_MIPSdspR2(NULL, in, out, width); + } else { + uint8_t top = prev[0], top_left = top, left = top; + int i; + for (i = 0; i < width; ++i) { + top = prev[i]; // need to read this first, in case prev==dst + left = in[i] + GradientPredictor_MIPSdspR2(left, top, top_left); + top_left = top; + out[i] = left; + } + } +} + +#undef DO_PREDICT_LINE_VERTICAL +#undef PREDICT_LINE_ONE_PASS +#undef DO_PREDICT_LINE +#undef SANITY_CHECK + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8FiltersInitMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitMIPSdspR2(void) { + WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_MIPSdspR2; + WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_MIPSdspR2; + WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_MIPSdspR2; + + WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_MIPSdspR2; + WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_MIPSdspR2; + WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_MIPSdspR2; +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(VP8FiltersInitMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 diff --git a/internal/libwebp-1.2.1/src/dsp/filters_msa.c b/internal/libwebp-1.2.1/src/dsp/filters_msa.c new file mode 100644 index 0000000..14c437d --- /dev/null +++ b/internal/libwebp-1.2.1/src/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 "src/dsp/dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "src/dsp/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_MSA(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_MSA(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_MSA(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_MSA; + WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_MSA; + WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_MSA; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(VP8FiltersInitMSA) + +#endif // WEBP_USE_MSA diff --git a/internal/libwebp-1.2.1/src/dsp/filters_neon.c b/internal/libwebp-1.2.1/src/dsp/filters_neon.c new file mode 100644 index 0000000..3e6a578 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/filters_neon.c @@ -0,0 +1,329 @@ +// 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 "src/dsp/dsp.h" + +#if defined(WEBP_USE_NEON) + +#include +#include "src/dsp/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... +#if !defined(USE_GRADIENT_UNFILTER) +#define USE_GRADIENT_UNFILTER 0 // ALTERNATE_CODE +#endif + +#if (USE_GRADIENT_UNFILTER == 1) +#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 (USE_GRADIENT_UNFILTER == 1) + 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/internal/libwebp-1.2.1/src/dsp/filters_sse2.c b/internal/libwebp-1.2.1/src/dsp/filters_sse2.c new file mode 100644 index 0000000..5c33ec1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/filters_sse2.c @@ -0,0 +1,340 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE2 variant of alpha filters +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) + +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Helpful macro. + +# 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. + +static void PredictLineTop_SSE2(const uint8_t* src, const uint8_t* pred, + uint8_t* dst, int length) { + int i; + const int max_pos = length & ~31; + assert(length >= 0); + for (i = 0; i < max_pos; i += 32) { + const __m128i A0 = _mm_loadu_si128((const __m128i*)&src[i + 0]); + const __m128i A1 = _mm_loadu_si128((const __m128i*)&src[i + 16]); + const __m128i B0 = _mm_loadu_si128((const __m128i*)&pred[i + 0]); + const __m128i B1 = _mm_loadu_si128((const __m128i*)&pred[i + 16]); + const __m128i C0 = _mm_sub_epi8(A0, B0); + const __m128i C1 = _mm_sub_epi8(A1, B1); + _mm_storeu_si128((__m128i*)&dst[i + 0], C0); + _mm_storeu_si128((__m128i*)&dst[i + 16], C1); + } + 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_SSE2(const uint8_t* src, uint8_t* dst, int length) { + int i; + const int max_pos = length & ~31; + assert(length >= 0); + for (i = 0; i < max_pos; i += 32) { + const __m128i A0 = _mm_loadu_si128((const __m128i*)(src + i + 0 )); + const __m128i B0 = _mm_loadu_si128((const __m128i*)(src + i + 0 - 1)); + const __m128i A1 = _mm_loadu_si128((const __m128i*)(src + i + 16 )); + const __m128i B1 = _mm_loadu_si128((const __m128i*)(src + i + 16 - 1)); + const __m128i C0 = _mm_sub_epi8(A0, B0); + const __m128i C1 = _mm_sub_epi8(A1, B1); + _mm_storeu_si128((__m128i*)(dst + i + 0), C0); + _mm_storeu_si128((__m128i*)(dst + i + 16), C1); + } + for (; i < length; ++i) dst[i] = src[i] - src[i - 1]; +} + +//------------------------------------------------------------------------------ +// Horizontal filter. + +static WEBP_INLINE void DoHorizontalFilter_SSE2(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_SSE2(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_SSE2(in + 1, out + 1, width - 1); + ++row; + in += stride; + out += stride; + } +} + +//------------------------------------------------------------------------------ +// Vertical filter. + +static WEBP_INLINE void DoVerticalFilter_SSE2(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_SSE2(in + 1, out + 1, width - 1); + row = 1; + in += stride; + out += stride; + } + + // Filter line-by-line. + while (row < last_row) { + PredictLineTop_SSE2(in, in - stride, out, width); + ++row; + in += stride; + out += stride; + } +} + +//------------------------------------------------------------------------------ +// Gradient filter. + +static WEBP_INLINE int GradientPredictor_SSE2(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_SSE2(const uint8_t* const row, + const uint8_t* const top, + uint8_t* const out, int length) { + const int max_pos = length & ~7; + int i; + const __m128i zero = _mm_setzero_si128(); + for (i = 0; i < max_pos; i += 8) { + const __m128i A0 = _mm_loadl_epi64((const __m128i*)&row[i - 1]); + const __m128i B0 = _mm_loadl_epi64((const __m128i*)&top[i]); + const __m128i C0 = _mm_loadl_epi64((const __m128i*)&top[i - 1]); + const __m128i D = _mm_loadl_epi64((const __m128i*)&row[i]); + const __m128i A1 = _mm_unpacklo_epi8(A0, zero); + const __m128i B1 = _mm_unpacklo_epi8(B0, zero); + const __m128i C1 = _mm_unpacklo_epi8(C0, zero); + const __m128i E = _mm_add_epi16(A1, B1); + const __m128i F = _mm_sub_epi16(E, C1); + const __m128i G = _mm_packus_epi16(F, zero); + const __m128i H = _mm_sub_epi8(D, G); + _mm_storel_epi64((__m128i*)(out + i), H); + } + for (; i < length; ++i) { + const int delta = GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]); + out[i] = (uint8_t)(row[i] - delta); + } +} + +static WEBP_INLINE void DoGradientFilter_SSE2(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_SSE2(in + 1, out + 1, width - 1); + row = 1; + in += stride; + out += stride; + } + + // Filter line-by-line. + while (row < last_row) { + out[0] = (uint8_t)(in[0] - in[-stride]); + GradientPredictDirect_SSE2(in + 1, in + 1 - stride, out + 1, width - 1); + ++row; + in += stride; + out += stride; + } +} + +#undef SANITY_CHECK + +//------------------------------------------------------------------------------ + +static void HorizontalFilter_SSE2(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoHorizontalFilter_SSE2(data, width, height, stride, 0, height, + filtered_data); +} + +static void VerticalFilter_SSE2(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoVerticalFilter_SSE2(data, width, height, stride, 0, height, filtered_data); +} + +static void GradientFilter_SSE2(const uint8_t* data, int width, int height, + int stride, uint8_t* filtered_data) { + DoGradientFilter_SSE2(data, width, height, stride, 0, height, filtered_data); +} + +//------------------------------------------------------------------------------ +// Inverse transforms + +static void HorizontalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + int i; + __m128i last; + out[0] = (uint8_t)(in[0] + (prev == NULL ? 0 : prev[0])); + if (width <= 1) return; + last = _mm_set_epi32(0, 0, 0, out[0]); + for (i = 1; i + 8 <= width; i += 8) { + const __m128i A0 = _mm_loadl_epi64((const __m128i*)(in + i)); + const __m128i A1 = _mm_add_epi8(A0, last); + const __m128i A2 = _mm_slli_si128(A1, 1); + const __m128i A3 = _mm_add_epi8(A1, A2); + const __m128i A4 = _mm_slli_si128(A3, 2); + const __m128i A5 = _mm_add_epi8(A3, A4); + const __m128i A6 = _mm_slli_si128(A5, 4); + const __m128i A7 = _mm_add_epi8(A5, A6); + _mm_storel_epi64((__m128i*)(out + i), A7); + last = _mm_srli_epi64(A7, 56); + } + for (; i < width; ++i) out[i] = (uint8_t)(in[i] + out[i - 1]); +} + +static void VerticalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter_SSE2(NULL, in, out, width); + } else { + int i; + const int max_pos = width & ~31; + assert(width >= 0); + for (i = 0; i < max_pos; i += 32) { + const __m128i A0 = _mm_loadu_si128((const __m128i*)&in[i + 0]); + const __m128i A1 = _mm_loadu_si128((const __m128i*)&in[i + 16]); + const __m128i B0 = _mm_loadu_si128((const __m128i*)&prev[i + 0]); + const __m128i B1 = _mm_loadu_si128((const __m128i*)&prev[i + 16]); + const __m128i C0 = _mm_add_epi8(A0, B0); + const __m128i C1 = _mm_add_epi8(A1, B1); + _mm_storeu_si128((__m128i*)&out[i + 0], C0); + _mm_storeu_si128((__m128i*)&out[i + 16], C1); + } + for (; i < width; ++i) out[i] = (uint8_t)(in[i] + prev[i]); + } +} + +static void GradientPredictInverse_SSE2(const uint8_t* const in, + const uint8_t* const top, + uint8_t* const row, int length) { + if (length > 0) { + int i; + const int max_pos = length & ~7; + const __m128i zero = _mm_setzero_si128(); + __m128i A = _mm_set_epi32(0, 0, 0, row[-1]); // left sample + for (i = 0; i < max_pos; i += 8) { + const __m128i tmp0 = _mm_loadl_epi64((const __m128i*)&top[i]); + const __m128i tmp1 = _mm_loadl_epi64((const __m128i*)&top[i - 1]); + const __m128i B = _mm_unpacklo_epi8(tmp0, zero); + const __m128i C = _mm_unpacklo_epi8(tmp1, zero); + const __m128i D = _mm_loadl_epi64((const __m128i*)&in[i]); // base input + const __m128i E = _mm_sub_epi16(B, C); // unclipped gradient basis B - C + __m128i out = zero; // accumulator for output + __m128i mask_hi = _mm_set_epi32(0, 0, 0, 0xff); + int k = 8; + while (1) { + const __m128i tmp3 = _mm_add_epi16(A, E); // delta = A + B - C + const __m128i tmp4 = _mm_packus_epi16(tmp3, zero); // saturate delta + const __m128i tmp5 = _mm_add_epi8(tmp4, D); // add to in[] + A = _mm_and_si128(tmp5, mask_hi); // 1-complement clip + out = _mm_or_si128(out, A); // accumulate output + if (--k == 0) break; + A = _mm_slli_si128(A, 1); // rotate left sample + mask_hi = _mm_slli_si128(mask_hi, 1); // rotate mask + A = _mm_unpacklo_epi8(A, zero); // convert 8b->16b + } + A = _mm_srli_si128(A, 7); // prepare left sample for next iteration + _mm_storel_epi64((__m128i*)&row[i], out); + } + for (; i < length; ++i) { + const int delta = GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]); + row[i] = (uint8_t)(in[i] + delta); + } + } +} + +static void GradientUnfilter_SSE2(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter_SSE2(NULL, in, out, width); + } else { + out[0] = (uint8_t)(in[0] + prev[0]); // predict from above + GradientPredictInverse_SSE2(in + 1, prev + 1, out + 1, width - 1); + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8FiltersInitSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitSSE2(void) { + WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_SSE2; +#if defined(CHROMIUM) + // TODO(crbug.com/654974) + (void)VerticalUnfilter_SSE2; +#else + WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_SSE2; +#endif + WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_SSE2; + + WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_SSE2; + WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_SSE2; + WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_SSE2; +} + +#else // !WEBP_USE_SSE2 + +WEBP_DSP_INIT_STUB(VP8FiltersInitSSE2) + +#endif // WEBP_USE_SSE2 diff --git a/internal/libwebp-1.2.1/src/dsp/lossless.c b/internal/libwebp-1.2.1/src/dsp/lossless.c new file mode 100644 index 0000000..84a5429 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless.c @@ -0,0 +1,680 @@ +// 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) +// Urvang Joshi (urvang@google.com) + +#include "src/dsp/dsp.h" + +#include +#include +#include +#include "src/dec/vp8li_dec.h" +#include "src/utils/endian_inl_utils.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" + +//------------------------------------------------------------------------------ +// Image transforms. + +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__) && defined(__GNUC__) && LOCAL_GCC_VERSION <= 0x409 +# 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 + +uint32_t VP8LPredictor0_C(const uint32_t* const left, + const uint32_t* const top) { + (void)top; + (void)left; + return ARGB_BLACK; +} +uint32_t VP8LPredictor1_C(const uint32_t* const left, + const uint32_t* const top) { + (void)top; + return *left; +} +uint32_t VP8LPredictor2_C(const uint32_t* const left, + const uint32_t* const top) { + (void)left; + return top[0]; +} +uint32_t VP8LPredictor3_C(const uint32_t* const left, + const uint32_t* const top) { + (void)left; + return top[1]; +} +uint32_t VP8LPredictor4_C(const uint32_t* const left, + const uint32_t* const top) { + (void)left; + return top[-1]; +} +uint32_t VP8LPredictor5_C(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average3(*left, top[0], top[1]); + return pred; +} +uint32_t VP8LPredictor6_C(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average2(*left, top[-1]); + return pred; +} +uint32_t VP8LPredictor7_C(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average2(*left, top[0]); + return pred; +} +uint32_t VP8LPredictor8_C(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average2(top[-1], top[0]); + (void)left; + return pred; +} +uint32_t VP8LPredictor9_C(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average2(top[0], top[1]); + (void)left; + return pred; +} +uint32_t VP8LPredictor10_C(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average4(*left, top[-1], top[0], top[1]); + return pred; +} +uint32_t VP8LPredictor11_C(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Select(top[0], *left, top[-1]); + return pred; +} +uint32_t VP8LPredictor12_C(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = ClampedAddSubtractFull(*left, top[0], top[-1]); + return pred; +} +uint32_t VP8LPredictor13_C(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = ClampedAddSubtractHalf(*left, top[0], top[-1]); + return pred; +} + +static void PredictorAdd0_C(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int x; + (void)upper; + for (x = 0; x < num_pixels; ++x) out[x] = VP8LAddPixels(in[x], ARGB_BLACK); +} +static void PredictorAdd1_C(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + uint32_t left = out[-1]; + (void)upper; + for (i = 0; i < num_pixels; ++i) { + out[i] = left = VP8LAddPixels(in[i], left); + } +} +GENERATE_PREDICTOR_ADD(VP8LPredictor2_C, PredictorAdd2_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor3_C, PredictorAdd3_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor4_C, PredictorAdd4_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor5_C, PredictorAdd5_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor6_C, PredictorAdd6_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor7_C, PredictorAdd7_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor8_C, PredictorAdd8_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor9_C, PredictorAdd9_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor10_C, PredictorAdd10_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor11_C, PredictorAdd11_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor12_C, PredictorAdd12_C) +GENERATE_PREDICTOR_ADD(VP8LPredictor13_C, PredictorAdd13_C) + +//------------------------------------------------------------------------------ + +// Inverse prediction. +static void PredictorInverseTransform_C(const VP8LTransform* const transform, + 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. + PredictorAdd0_C(in, NULL, 1, out); + PredictorAdd1_C(in + 1, NULL, width - 1, out + 1); + in += width; + out += width; + ++y_start; + } + + { + int y = y_start; + const int tile_width = 1 << transform->bits_; + const int mask = tile_width - 1; + 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* pred_mode_src = pred_mode_base; + int x = 1; + // First pixel follows the T (mode=2) mode. + PredictorAdd2_C(in, out - width, 1, out); + // .. the rest: + 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; + } + in += width; + out += width; + ++y; + if ((y & mask) == 0) { // Use the same mask, since tiles are squares. + pred_mode_base += tiles_per_row; + } + } + } +} + +// Add green to blue and red channels (i.e. perform the inverse transform of +// 'subtract green'). +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 = src[i]; + const uint32_t green = ((argb >> 8) & 0xff); + uint32_t red_blue = (argb & 0x00ff00ffu); + red_blue += (green << 16) | green; + red_blue &= 0x00ff00ffu; + dst[i] = (argb & 0xff00ff00u) | red_blue; + } +} + +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, + 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; +} + +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 = src[i]; + const int8_t green = (int8_t)(argb >> 8); + const uint32_t red = argb >> 16; + int new_red = red & 0xff; + int new_blue = argb & 0xff; + 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_, (int8_t)new_red); + new_blue &= 0xff; + dst[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); + } +} + +// Color space inverse transform. +static void ColorSpaceInverseTransform_C(const VP8LTransform* const transform, + 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; + const int safe_width = width & ~mask; + const int remaining_width = width - safe_width; + const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_); + int y = y_start; + const uint32_t* pred_row = + transform->data_ + (y >> transform->bits_) * tiles_per_row; + + while (y < y_end) { + const uint32_t* pred = pred_row; + VP8LMultipliers m = { 0, 0, 0 }; + 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, src, tile_width, dst); + src += tile_width; + dst += tile_width; + } + if (src < src_end) { // Left-overs using C-version. + ColorCodeToMultipliers(*pred++, &m); + VP8LTransformColorInverse(&m, src, remaining_width, dst); + src += remaining_width; + dst += remaining_width; + } + ++y; + if ((y & mask) == 0) pred_row += tiles_per_row; + } +} + +// Separate out pixels packed together using pixel-bundling. +// We define two methods for ARGB data (uint32_t) and alpha-only data (uint8_t). +#define COLOR_INDEX_INVERSE(FUNC_NAME, F_NAME, STATIC_DECL, TYPE, BIT_SUFFIX, \ + GET_INDEX, GET_VALUE) \ +static void F_NAME(const TYPE* src, const uint32_t* const color_map, \ + TYPE* dst, int y_start, int y_end, int width) { \ + int y; \ + for (y = y_start; y < y_end; ++y) { \ + int x; \ + for (x = 0; x < width; ++x) { \ + *dst++ = GET_VALUE(color_map[GET_INDEX(*src++)]); \ + } \ + } \ +} \ +STATIC_DECL void FUNC_NAME(const VP8LTransform* const transform, \ + int y_start, int y_end, const TYPE* src, \ + TYPE* dst) { \ + int y; \ + const int bits_per_pixel = 8 >> transform->bits_; \ + const int width = transform->xsize_; \ + const uint32_t* const color_map = transform->data_; \ + if (bits_per_pixel < 8) { \ + const int pixels_per_byte = 1 << transform->bits_; \ + const int count_mask = pixels_per_byte - 1; \ + const uint32_t bit_mask = (1 << bits_per_pixel) - 1; \ + for (y = y_start; y < y_end; ++y) { \ + uint32_t packed_pixels = 0; \ + int x; \ + for (x = 0; x < width; ++x) { \ + /* We need to load fresh 'packed_pixels' once every */ \ + /* 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte */ \ + /* is a power of 2, so can just use a mask for that, instead of */ \ + /* decrementing a counter. */ \ + if ((x & count_mask) == 0) packed_pixels = GET_INDEX(*src++); \ + *dst++ = GET_VALUE(color_map[packed_pixels & bit_mask]); \ + packed_pixels >>= bits_per_pixel; \ + } \ + } \ + } else { \ + VP8LMapColor##BIT_SUFFIX(src, color_map, dst, y_start, y_end, width); \ + } \ +} + +COLOR_INDEX_INVERSE(ColorIndexInverseTransform_C, MapARGB_C, static, + uint32_t, 32b, VP8GetARGBIndex, VP8GetARGBValue) +COLOR_INDEX_INVERSE(VP8LColorIndexInverseTransformAlpha, MapAlpha_C, , + uint8_t, 8b, VP8GetAlphaIndex, VP8GetAlphaValue) + +#undef COLOR_INDEX_INVERSE + +void VP8LInverseTransform(const VP8LTransform* const transform, + int row_start, int row_end, + const uint32_t* const in, uint32_t* const out) { + const int width = transform->xsize_; + assert(row_start < row_end); + assert(row_end <= transform->ysize_); + switch (transform->type_) { + case SUBTRACT_GREEN: + VP8LAddGreenToBlueAndRed(in, (row_end - row_start) * width, out); + break; + case PREDICTOR_TRANSFORM: + PredictorInverseTransform_C(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. + memcpy(out - width, out + (row_end - row_start - 1) * width, + width * sizeof(*out)); + } + break; + case CROSS_COLOR_TRANSFORM: + ColorSpaceInverseTransform_C(transform, row_start, row_end, in, out); + break; + case COLOR_INDEXING_TRANSFORM: + if (in == out && transform->bits_ > 0) { + // Move packed pixels to the end of unpacked region, so that unpacking + // can occur seamlessly. + // Also, note that this is the only transform that applies on + // the effective width of VP8LSubSampleSize(xsize_, bits_). All other + // transforms work on effective width of xsize_. + const int out_stride = (row_end - row_start) * width; + const int in_stride = (row_end - row_start) * + VP8LSubSampleSize(transform->xsize_, transform->bits_); + uint32_t* const src = out + out_stride - in_stride; + memmove(src, out, in_stride * sizeof(*src)); + ColorIndexInverseTransform_C(transform, row_start, row_end, src, out); + } else { + ColorIndexInverseTransform_C(transform, row_start, row_end, in, out); + } + break; + } +} + +//------------------------------------------------------------------------------ +// Color space conversion. + +static int is_big_endian(void) { + static const union { + uint16_t w; + uint8_t b[2]; + } tmp = { 1 }; + return (tmp.b[0] != 1); +} + +void VP8LConvertBGRAToRGB_C(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const src_end = src + num_pixels; + while (src < src_end) { + const uint32_t argb = *src++; + *dst++ = (argb >> 16) & 0xff; + *dst++ = (argb >> 8) & 0xff; + *dst++ = (argb >> 0) & 0xff; + } +} + +void VP8LConvertBGRAToRGBA_C(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const src_end = src + num_pixels; + while (src < src_end) { + const uint32_t argb = *src++; + *dst++ = (argb >> 16) & 0xff; + *dst++ = (argb >> 8) & 0xff; + *dst++ = (argb >> 0) & 0xff; + *dst++ = (argb >> 24) & 0xff; + } +} + +void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const src_end = src + num_pixels; + while (src < src_end) { + const uint32_t argb = *src++; + const uint8_t rg = ((argb >> 16) & 0xf0) | ((argb >> 12) & 0xf); + const uint8_t ba = ((argb >> 0) & 0xf0) | ((argb >> 28) & 0xf); +#if (WEBP_SWAP_16BIT_CSP == 1) + *dst++ = ba; + *dst++ = rg; +#else + *dst++ = rg; + *dst++ = ba; +#endif + } +} + +void VP8LConvertBGRAToRGB565_C(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const src_end = src + num_pixels; + while (src < src_end) { + const uint32_t argb = *src++; + const uint8_t rg = ((argb >> 16) & 0xf8) | ((argb >> 13) & 0x7); + const uint8_t gb = ((argb >> 5) & 0xe0) | ((argb >> 3) & 0x1f); +#if (WEBP_SWAP_16BIT_CSP == 1) + *dst++ = gb; + *dst++ = rg; +#else + *dst++ = rg; + *dst++ = gb; +#endif + } +} + +void VP8LConvertBGRAToBGR_C(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const src_end = src + num_pixels; + while (src < src_end) { + const uint32_t argb = *src++; + *dst++ = (argb >> 0) & 0xff; + *dst++ = (argb >> 8) & 0xff; + *dst++ = (argb >> 16) & 0xff; + } +} + +static void CopyOrSwap(const uint32_t* src, int num_pixels, uint8_t* dst, + int swap_on_big_endian) { + if (is_big_endian() == swap_on_big_endian) { + const uint32_t* const src_end = src + num_pixels; + while (src < src_end) { + const uint32_t argb = *src++; + WebPUint32ToMem(dst, BSwap32(argb)); + dst += sizeof(argb); + } + } else { + memcpy(dst, src, num_pixels * sizeof(*src)); + } +} + +void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, + WEBP_CSP_MODE out_colorspace, uint8_t* const rgba) { + switch (out_colorspace) { + case MODE_RGB: + VP8LConvertBGRAToRGB(in_data, num_pixels, rgba); + break; + case MODE_RGBA: + VP8LConvertBGRAToRGBA(in_data, num_pixels, rgba); + break; + case MODE_rgbA: + VP8LConvertBGRAToRGBA(in_data, num_pixels, rgba); + WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0); + break; + case MODE_BGR: + VP8LConvertBGRAToBGR(in_data, num_pixels, rgba); + break; + case MODE_BGRA: + CopyOrSwap(in_data, num_pixels, rgba, 1); + break; + case MODE_bgrA: + CopyOrSwap(in_data, num_pixels, rgba, 1); + WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0); + break; + case MODE_ARGB: + CopyOrSwap(in_data, num_pixels, rgba, 0); + break; + case MODE_Argb: + CopyOrSwap(in_data, num_pixels, rgba, 0); + WebPApplyAlphaMultiply(rgba, 1, num_pixels, 1, 0); + break; + case MODE_RGBA_4444: + VP8LConvertBGRAToRGBA4444(in_data, num_pixels, rgba); + break; + case MODE_rgbA_4444: + VP8LConvertBGRAToRGBA4444(in_data, num_pixels, rgba); + WebPApplyAlphaMultiply4444(rgba, num_pixels, 1, 0); + break; + case MODE_RGB_565: + VP8LConvertBGRAToRGB565(in_data, num_pixels, rgba); + break; + default: + assert(0); // Code flow should not reach here. + } +} + +//------------------------------------------------------------------------------ + +VP8LProcessDecBlueAndRedFunc VP8LAddGreenToBlueAndRed; +VP8LPredictorAddSubFunc VP8LPredictorsAdd[16]; +VP8LPredictorFunc VP8LPredictors[16]; + +// exposed plain-C implementations +VP8LPredictorAddSubFunc VP8LPredictorsAdd_C[16]; + +VP8LTransformColorInverseFunc VP8LTransformColorInverse; + +VP8LConvertFunc VP8LConvertBGRAToRGB; +VP8LConvertFunc VP8LConvertBGRAToRGBA; +VP8LConvertFunc VP8LConvertBGRAToRGBA4444; +VP8LConvertFunc VP8LConvertBGRAToRGB565; +VP8LConvertFunc VP8LConvertBGRAToBGR; + +VP8LMapARGBFunc VP8LMapColor32b; +VP8LMapAlphaFunc VP8LMapColor8b; + +extern void VP8LDspInitSSE2(void); +extern void VP8LDspInitSSE41(void); +extern void VP8LDspInitNEON(void); +extern void VP8LDspInitMIPSdspR2(void); +extern void VP8LDspInitMSA(void); + +#define COPY_PREDICTOR_ARRAY(IN, OUT) do { \ + (OUT)[0] = IN##0_C; \ + (OUT)[1] = IN##1_C; \ + (OUT)[2] = IN##2_C; \ + (OUT)[3] = IN##3_C; \ + (OUT)[4] = IN##4_C; \ + (OUT)[5] = IN##5_C; \ + (OUT)[6] = IN##6_C; \ + (OUT)[7] = IN##7_C; \ + (OUT)[8] = IN##8_C; \ + (OUT)[9] = IN##9_C; \ + (OUT)[10] = IN##10_C; \ + (OUT)[11] = IN##11_C; \ + (OUT)[12] = IN##12_C; \ + (OUT)[13] = IN##13_C; \ + (OUT)[14] = IN##0_C; /* <- padding security sentinels*/ \ + (OUT)[15] = IN##0_C; \ +} while (0); + +WEBP_DSP_INIT_FUNC(VP8LDspInit) { + COPY_PREDICTOR_ARRAY(VP8LPredictor, VP8LPredictors) + COPY_PREDICTOR_ARRAY(PredictorAdd, VP8LPredictorsAdd) + COPY_PREDICTOR_ARRAY(PredictorAdd, VP8LPredictorsAdd_C) + +#if !WEBP_NEON_OMIT_C_CODE + VP8LAddGreenToBlueAndRed = VP8LAddGreenToBlueAndRed_C; + + VP8LTransformColorInverse = VP8LTransformColorInverse_C; + + VP8LConvertBGRAToRGBA = VP8LConvertBGRAToRGBA_C; + VP8LConvertBGRAToRGB = VP8LConvertBGRAToRGB_C; + VP8LConvertBGRAToBGR = VP8LConvertBGRAToBGR_C; +#endif + + VP8LConvertBGRAToRGBA4444 = VP8LConvertBGRAToRGBA4444_C; + VP8LConvertBGRAToRGB565 = VP8LConvertBGRAToRGB565_C; + + VP8LMapColor32b = MapARGB_C; + VP8LMapColor8b = MapAlpha_C; + + // If defined, use CPUInfo() to overwrite some pointers with faster versions. + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + VP8LDspInitSSE2(); +#if defined(WEBP_HAVE_SSE41) + if (VP8GetCPUInfo(kSSE4_1)) { + VP8LDspInitSSE41(); + } +#endif + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + VP8LDspInitMIPSdspR2(); + } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8LDspInitMSA(); + } +#endif + } + +#if defined(WEBP_HAVE_NEON) + if (WEBP_NEON_OMIT_C_CODE || + (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) { + VP8LDspInitNEON(); + } +#endif + + assert(VP8LAddGreenToBlueAndRed != NULL); + assert(VP8LTransformColorInverse != NULL); + assert(VP8LConvertBGRAToRGBA != NULL); + assert(VP8LConvertBGRAToRGB != NULL); + assert(VP8LConvertBGRAToBGR != NULL); + assert(VP8LConvertBGRAToRGBA4444 != NULL); + assert(VP8LConvertBGRAToRGB565 != NULL); + assert(VP8LMapColor32b != NULL); + assert(VP8LMapColor8b != NULL); +} +#undef COPY_PREDICTOR_ARRAY + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/src/dsp/lossless.h b/internal/libwebp-1.2.1/src/dsp/lossless.h new file mode 100644 index 0000000..c26c6bc --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless.h @@ -0,0 +1,259 @@ +// 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) + +#ifndef WEBP_DSP_LOSSLESS_H_ +#define WEBP_DSP_LOSSLESS_H_ + +#include "src/webp/types.h" +#include "src/webp/decode.h" + +#include "src/enc/histogram_enc.h" +#include "src/utils/utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Decoding + +typedef uint32_t (*VP8LPredictorFunc)(const uint32_t* const left, + const uint32_t* const top); +extern VP8LPredictorFunc VP8LPredictors[16]; + +uint32_t VP8LPredictor0_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor1_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor2_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor3_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor4_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor5_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor6_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor7_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor8_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor9_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor10_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor11_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor12_C(const uint32_t* const left, + const uint32_t* const top); +uint32_t VP8LPredictor13_C(const uint32_t* const left, + const uint32_t* const top); + +// 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 + // automatically converted to "mod 256" values. + uint8_t green_to_red_; + uint8_t green_to_blue_; + uint8_t red_to_blue_; +} VP8LMultipliers; +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. + +// Performs inverse transform of data given transform information, start and end +// rows. Transform will be applied to rows [row_start, row_end[. +// The *in and *out pointers refer to source and destination data respectively +// corresponding to the intermediate row (row_start). +void VP8LInverseTransform(const struct VP8LTransform* const transform, + int row_start, int row_end, + const uint32_t* const in, uint32_t* const out); + +// Color space conversion. +typedef void (*VP8LConvertFunc)(const uint32_t* src, int num_pixels, + uint8_t* dst); +extern VP8LConvertFunc VP8LConvertBGRAToRGB; +extern VP8LConvertFunc VP8LConvertBGRAToRGBA; +extern VP8LConvertFunc VP8LConvertBGRAToRGBA4444; +extern VP8LConvertFunc VP8LConvertBGRAToRGB565; +extern VP8LConvertFunc VP8LConvertBGRAToBGR; + +// Converts from BGRA to other color spaces. +void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, + WEBP_CSP_MODE out_colorspace, uint8_t* const rgba); + +typedef void (*VP8LMapARGBFunc)(const uint32_t* src, + const uint32_t* const color_map, + uint32_t* dst, int y_start, + int y_end, int width); +typedef void (*VP8LMapAlphaFunc)(const uint8_t* src, + const uint32_t* const color_map, + uint8_t* dst, int y_start, + int y_end, int width); + +extern VP8LMapARGBFunc VP8LMapColor32b; +extern VP8LMapAlphaFunc VP8LMapColor8b; + +// Similar to the static method ColorIndexInverseTransform() that is part of +// lossless.c, but used only for alpha decoding. It takes uint8_t (rather than +// uint32_t) arguments for 'src' and 'dst'. +void VP8LColorIndexInverseTransformAlpha( + const struct VP8LTransform* const transform, int y_start, int y_end, + const uint8_t* src, uint8_t* dst); + +// Expose some C-only fallback functions +void VP8LTransformColorInverse_C(const VP8LMultipliers* const m, + 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); +void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src, + int num_pixels, uint8_t* dst); +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(const uint32_t* src, int num_pixels, + uint32_t* dst); + +// Must be called before calling any of the above methods. +void VP8LDspInit(void); + +//------------------------------------------------------------------------------ +// Encoding + +typedef void (*VP8LProcessEncBlueAndRedFunc)(uint32_t* dst, int num_pixels); +extern VP8LProcessEncBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed; +typedef void (*VP8LTransformColorFunc)(const VP8LMultipliers* const m, + uint32_t* dst, int num_pixels); +extern VP8LTransformColorFunc VP8LTransformColor; +typedef void (*VP8LCollectColorBlueTransformsFunc)( + const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_blue, int red_to_blue, int histo[]); +extern VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms; + +typedef void (*VP8LCollectColorRedTransformsFunc)( + const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_red, int histo[]); +extern VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms; + +// Expose some C-only fallback functions +void VP8LTransformColor_C(const VP8LMultipliers* const m, + uint32_t* data, int num_pixels); +void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels); +void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_red, int histo[]); +void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_blue, int red_to_blue, + int histo[]); + +extern VP8LPredictorAddSubFunc VP8LPredictorsSub[16]; +extern VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16]; + +// ----------------------------------------------------------------------------- +// Huffman-cost related functions. + +typedef double (*VP8LCostFunc)(const uint32_t* population, int length); +typedef double (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y, + int length); +typedef float (*VP8LCombinedShannonEntropyFunc)(const int X[256], + const int Y[256]); + +extern VP8LCostFunc VP8LExtraCost; +extern VP8LCostCombinedFunc VP8LExtraCostCombined; +extern VP8LCombinedShannonEntropyFunc VP8LCombinedShannonEntropy; + +typedef struct { // small struct to hold counters + int counts[2]; // index: 0=zero streak, 1=non-zero streak + int streaks[2][2]; // [zero/non-zero][streak<3 / streak>=3] +} VP8LStreaks; + +typedef struct { // small struct to hold bit entropy results + double entropy; // entropy + uint32_t sum; // sum of the population + int nonzeros; // number of non-zero elements in the population + uint32_t max_val; // maximum value in the population + uint32_t nonzero_code; // index of the last non-zero in the population +} VP8LBitEntropy; + +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. +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'. +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 (*VP8LAddVectorFunc)(const uint32_t* a, const uint32_t* b, + uint32_t* out, int size); +extern VP8LAddVectorFunc VP8LAddVector; +typedef void (*VP8LAddVectorEqFunc)(const uint32_t* a, uint32_t* out, int size); +extern VP8LAddVectorEqFunc VP8LAddVectorEq; +void VP8LHistogramAdd(const VP8LHistogram* const a, + const VP8LHistogram* const b, + VP8LHistogram* const out); + +// ----------------------------------------------------------------------------- +// PrefixEncode() + +typedef int (*VP8LVectorMismatchFunc)(const uint32_t* const array1, + const uint32_t* const array2, int length); +// Returns the first index where array1 and array2 are different. +extern VP8LVectorMismatchFunc VP8LVectorMismatch; + +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); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_DSP_LOSSLESS_H_ diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_common.h b/internal/libwebp-1.2.1/src/dsp/lossless_common.h new file mode 100644 index 0000000..6a2f736 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_common.h @@ -0,0 +1,191 @@ +// 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 "src/webp/types.h" + +#include "src/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() + +// 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; \ + assert(upper != NULL); \ + for (x = 0; x < num_pixels; ++x) { \ + const uint32_t pred = (PREDICTOR)(&out[x - 1], upper + x); \ + out[x] = VP8LAddPixels(in[x], pred); \ + } \ +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_DSP_LOSSLESS_COMMON_H_ diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_enc.c b/internal/libwebp-1.2.1/src/dsp/lossless_enc.c new file mode 100644 index 0000000..1580631 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_enc.c @@ -0,0 +1,948 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Image transform methods for lossless encoder. +// +// Authors: Vikas Arora (vikaas.arora@gmail.com) +// Jyrki Alakuijala (jyrki@google.com) +// Urvang Joshi (urvang@google.com) + +#include "src/dsp/dsp.h" + +#include +#include +#include +#include "src/dec/vp8li_dec.h" +#include "src/utils/endian_inl_utils.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" +#include "src/dsp/yuv.h" + +// lookup table for small values of log2(int) +const float kLog2Table[LOG_LOOKUP_IDX_MAX] = { + 0.0000000000000000f, 0.0000000000000000f, + 1.0000000000000000f, 1.5849625007211560f, + 2.0000000000000000f, 2.3219280948873621f, + 2.5849625007211560f, 2.8073549220576041f, + 3.0000000000000000f, 3.1699250014423121f, + 3.3219280948873621f, 3.4594316186372973f, + 3.5849625007211560f, 3.7004397181410921f, + 3.8073549220576041f, 3.9068905956085187f, + 4.0000000000000000f, 4.0874628412503390f, + 4.1699250014423121f, 4.2479275134435852f, + 4.3219280948873626f, 4.3923174227787606f, + 4.4594316186372973f, 4.5235619560570130f, + 4.5849625007211560f, 4.6438561897747243f, + 4.7004397181410917f, 4.7548875021634682f, + 4.8073549220576037f, 4.8579809951275718f, + 4.9068905956085187f, 4.9541963103868749f, + 5.0000000000000000f, 5.0443941193584533f, + 5.0874628412503390f, 5.1292830169449663f, + 5.1699250014423121f, 5.2094533656289501f, + 5.2479275134435852f, 5.2854022188622487f, + 5.3219280948873626f, 5.3575520046180837f, + 5.3923174227787606f, 5.4262647547020979f, + 5.4594316186372973f, 5.4918530963296747f, + 5.5235619560570130f, 5.5545888516776376f, + 5.5849625007211560f, 5.6147098441152083f, + 5.6438561897747243f, 5.6724253419714951f, + 5.7004397181410917f, 5.7279204545631987f, + 5.7548875021634682f, 5.7813597135246599f, + 5.8073549220576037f, 5.8328900141647412f, + 5.8579809951275718f, 5.8826430493618415f, + 5.9068905956085187f, 5.9307373375628866f, + 5.9541963103868749f, 5.9772799234999167f, + 6.0000000000000000f, 6.0223678130284543f, + 6.0443941193584533f, 6.0660891904577720f, + 6.0874628412503390f, 6.1085244567781691f, + 6.1292830169449663f, 6.1497471195046822f, + 6.1699250014423121f, 6.1898245588800175f, + 6.2094533656289501f, 6.2288186904958804f, + 6.2479275134435852f, 6.2667865406949010f, + 6.2854022188622487f, 6.3037807481771030f, + 6.3219280948873626f, 6.3398500028846243f, + 6.3575520046180837f, 6.3750394313469245f, + 6.3923174227787606f, 6.4093909361377017f, + 6.4262647547020979f, 6.4429434958487279f, + 6.4594316186372973f, 6.4757334309663976f, + 6.4918530963296747f, 6.5077946401986963f, + 6.5235619560570130f, 6.5391588111080309f, + 6.5545888516776376f, 6.5698556083309478f, + 6.5849625007211560f, 6.5999128421871278f, + 6.6147098441152083f, 6.6293566200796094f, + 6.6438561897747243f, 6.6582114827517946f, + 6.6724253419714951f, 6.6865005271832185f, + 6.7004397181410917f, 6.7142455176661224f, + 6.7279204545631987f, 6.7414669864011464f, + 6.7548875021634682f, 6.7681843247769259f, + 6.7813597135246599f, 6.7944158663501061f, + 6.8073549220576037f, 6.8201789624151878f, + 6.8328900141647412f, 6.8454900509443747f, + 6.8579809951275718f, 6.8703647195834047f, + 6.8826430493618415f, 6.8948177633079437f, + 6.9068905956085187f, 6.9188632372745946f, + 6.9307373375628866f, 6.9425145053392398f, + 6.9541963103868749f, 6.9657842846620869f, + 6.9772799234999167f, 6.9886846867721654f, + 7.0000000000000000f, 7.0112272554232539f, + 7.0223678130284543f, 7.0334230015374501f, + 7.0443941193584533f, 7.0552824355011898f, + 7.0660891904577720f, 7.0768155970508308f, + 7.0874628412503390f, 7.0980320829605263f, + 7.1085244567781691f, 7.1189410727235076f, + 7.1292830169449663f, 7.1395513523987936f, + 7.1497471195046822f, 7.1598713367783890f, + 7.1699250014423121f, 7.1799090900149344f, + 7.1898245588800175f, 7.1996723448363644f, + 7.2094533656289501f, 7.2191685204621611f, + 7.2288186904958804f, 7.2384047393250785f, + 7.2479275134435852f, 7.2573878426926521f, + 7.2667865406949010f, 7.2761244052742375f, + 7.2854022188622487f, 7.2946207488916270f, + 7.3037807481771030f, 7.3128829552843557f, + 7.3219280948873626f, 7.3309168781146167f, + 7.3398500028846243f, 7.3487281542310771f, + 7.3575520046180837f, 7.3663222142458160f, + 7.3750394313469245f, 7.3837042924740519f, + 7.3923174227787606f, 7.4008794362821843f, + 7.4093909361377017f, 7.4178525148858982f, + 7.4262647547020979f, 7.4346282276367245f, + 7.4429434958487279f, 7.4512111118323289f, + 7.4594316186372973f, 7.4676055500829976f, + 7.4757334309663976f, 7.4838157772642563f, + 7.4918530963296747f, 7.4998458870832056f, + 7.5077946401986963f, 7.5156998382840427f, + 7.5235619560570130f, 7.5313814605163118f, + 7.5391588111080309f, 7.5468944598876364f, + 7.5545888516776376f, 7.5622424242210728f, + 7.5698556083309478f, 7.5774288280357486f, + 7.5849625007211560f, 7.5924570372680806f, + 7.5999128421871278f, 7.6073303137496104f, + 7.6147098441152083f, 7.6220518194563764f, + 7.6293566200796094f, 7.6366246205436487f, + 7.6438561897747243f, 7.6510516911789281f, + 7.6582114827517946f, 7.6653359171851764f, + 7.6724253419714951f, 7.6794800995054464f, + 7.6865005271832185f, 7.6934869574993252f, + 7.7004397181410917f, 7.7073591320808825f, + 7.7142455176661224f, 7.7210991887071855f, + 7.7279204545631987f, 7.7347096202258383f, + 7.7414669864011464f, 7.7481928495894605f, + 7.7548875021634682f, 7.7615512324444795f, + 7.7681843247769259f, 7.7747870596011736f, + 7.7813597135246599f, 7.7879025593914317f, + 7.7944158663501061f, 7.8008998999203047f, + 7.8073549220576037f, 7.8137811912170374f, + 7.8201789624151878f, 7.8265484872909150f, + 7.8328900141647412f, 7.8392037880969436f, + 7.8454900509443747f, 7.8517490414160571f, + 7.8579809951275718f, 7.8641861446542797f, + 7.8703647195834047f, 7.8765169465649993f, + 7.8826430493618415f, 7.8887432488982591f, + 7.8948177633079437f, 7.9008668079807486f, + 7.9068905956085187f, 7.9128893362299619f, + 7.9188632372745946f, 7.9248125036057812f, + 7.9307373375628866f, 7.9366379390025709f, + 7.9425145053392398f, 7.9483672315846778f, + 7.9541963103868749f, 7.9600019320680805f, + 7.9657842846620869f, 7.9715435539507719f, + 7.9772799234999167f, 7.9829935746943103f, + 7.9886846867721654f, 7.9943534368588577f +}; + +const float kSLog2Table[LOG_LOOKUP_IDX_MAX] = { + 0.00000000f, 0.00000000f, 2.00000000f, 4.75488750f, + 8.00000000f, 11.60964047f, 15.50977500f, 19.65148445f, + 24.00000000f, 28.52932501f, 33.21928095f, 38.05374781f, + 43.01955001f, 48.10571634f, 53.30296891f, 58.60335893f, + 64.00000000f, 69.48686830f, 75.05865003f, 80.71062276f, + 86.43856190f, 92.23866588f, 98.10749561f, 104.04192499f, + 110.03910002f, 116.09640474f, 122.21143267f, 128.38196256f, + 134.60593782f, 140.88144886f, 147.20671787f, 153.58008562f, + 160.00000000f, 166.46500594f, 172.97373660f, 179.52490559f, + 186.11730005f, 192.74977453f, 199.42124551f, 206.13068654f, + 212.87712380f, 219.65963219f, 226.47733176f, 233.32938445f, + 240.21499122f, 247.13338933f, 254.08384998f, 261.06567603f, + 268.07820003f, 275.12078236f, 282.19280949f, 289.29369244f, + 296.42286534f, 303.57978409f, 310.76392512f, 317.97478424f, + 325.21187564f, 332.47473081f, 339.76289772f, 347.07593991f, + 354.41343574f, 361.77497759f, 369.16017124f, 376.56863518f, + 384.00000000f, 391.45390785f, 398.93001188f, 406.42797576f, + 413.94747321f, 421.48818752f, 429.04981119f, 436.63204548f, + 444.23460010f, 451.85719280f, 459.49954906f, 467.16140179f, + 474.84249102f, 482.54256363f, 490.26137307f, 497.99867911f, + 505.75424759f, 513.52785023f, 521.31926438f, 529.12827280f, + 536.95466351f, 544.79822957f, 552.65876890f, 560.53608414f, + 568.42998244f, 576.34027536f, 584.26677867f, 592.20931226f, + 600.16769996f, 608.14176943f, 616.13135206f, 624.13628279f, + 632.15640007f, 640.19154569f, 648.24156472f, 656.30630539f, + 664.38561898f, 672.47935976f, 680.58738488f, 688.70955430f, + 696.84573069f, 704.99577935f, 713.15956818f, 721.33696754f, + 729.52785023f, 737.73209140f, 745.94956849f, 754.18016116f, + 762.42375127f, 770.68022275f, 778.94946161f, 787.23135586f, + 795.52579543f, 803.83267219f, 812.15187982f, 820.48331383f, + 828.82687147f, 837.18245171f, 845.54995518f, 853.92928416f, + 862.32034249f, 870.72303558f, 879.13727036f, 887.56295522f, + 896.00000000f, 904.44831595f, 912.90781569f, 921.37841320f, + 929.86002376f, 938.35256392f, 946.85595152f, 955.37010560f, + 963.89494641f, 972.43039537f, 980.97637504f, 989.53280911f, + 998.09962237f, 1006.67674069f, 1015.26409097f, 1023.86160116f, + 1032.46920021f, 1041.08681805f, 1049.71438560f, 1058.35183469f, + 1066.99909811f, 1075.65610955f, 1084.32280357f, 1092.99911564f, + 1101.68498204f, 1110.38033993f, 1119.08512727f, 1127.79928282f, + 1136.52274614f, 1145.25545758f, 1153.99735821f, 1162.74838989f, + 1171.50849518f, 1180.27761738f, 1189.05570047f, 1197.84268914f, + 1206.63852876f, 1215.44316535f, 1224.25654560f, 1233.07861684f, + 1241.90932703f, 1250.74862473f, 1259.59645914f, 1268.45278005f, + 1277.31753781f, 1286.19068338f, 1295.07216828f, 1303.96194457f, + 1312.85996488f, 1321.76618236f, 1330.68055071f, 1339.60302413f, + 1348.53355734f, 1357.47210556f, 1366.41862452f, 1375.37307041f, + 1384.33539991f, 1393.30557020f, 1402.28353887f, 1411.26926400f, + 1420.26270412f, 1429.26381818f, 1438.27256558f, 1447.28890615f, + 1456.31280014f, 1465.34420819f, 1474.38309138f, 1483.42941118f, + 1492.48312945f, 1501.54420843f, 1510.61261078f, 1519.68829949f, + 1528.77123795f, 1537.86138993f, 1546.95871952f, 1556.06319119f, + 1565.17476976f, 1574.29342040f, 1583.41910860f, 1592.55180020f, + 1601.69146137f, 1610.83805860f, 1619.99155871f, 1629.15192882f, + 1638.31913637f, 1647.49314911f, 1656.67393509f, 1665.86146266f, + 1675.05570047f, 1684.25661744f, 1693.46418280f, 1702.67836605f, + 1711.89913698f, 1721.12646563f, 1730.36032233f, 1739.60067768f, + 1748.84750254f, 1758.10076802f, 1767.36044551f, 1776.62650662f, + 1785.89892323f, 1795.17766747f, 1804.46271172f, 1813.75402857f, + 1823.05159087f, 1832.35537170f, 1841.66534438f, 1850.98148244f, + 1860.30375965f, 1869.63214999f, 1878.96662767f, 1888.30716711f, + 1897.65374295f, 1907.00633003f, 1916.36490342f, 1925.72943838f, + 1935.09991037f, 1944.47629506f, 1953.85856831f, 1963.24670620f, + 1972.64068498f, 1982.04048108f, 1991.44607117f, 2000.85743204f, + 2010.27454072f, 2019.69737440f, 2029.12591044f, 2038.56012640f +}; + +const VP8LPrefixCode kPrefixEncodeCode[PREFIX_LOOKUP_IDX_MAX] = { + { 0, 0}, { 0, 0}, { 1, 0}, { 2, 0}, { 3, 0}, { 4, 1}, { 4, 1}, { 5, 1}, + { 5, 1}, { 6, 2}, { 6, 2}, { 6, 2}, { 6, 2}, { 7, 2}, { 7, 2}, { 7, 2}, + { 7, 2}, { 8, 3}, { 8, 3}, { 8, 3}, { 8, 3}, { 8, 3}, { 8, 3}, { 8, 3}, + { 8, 3}, { 9, 3}, { 9, 3}, { 9, 3}, { 9, 3}, { 9, 3}, { 9, 3}, { 9, 3}, + { 9, 3}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, + {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, + {10, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, + {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, + {11, 4}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, + {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, + {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, + {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, + {12, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, + {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, + {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, + {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, + {13, 5}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, + {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, + {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, + {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, + {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, + {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, + {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, + {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, + {14, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, + {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, + {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, + {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, + {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, + {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, + {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, + {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, + {15, 6}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, + {16, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, + {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, +}; + +const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX] = { + 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 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, + 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, + 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, 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, 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, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, + 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, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126 +}; + +static float FastSLog2Slow_C(uint32_t v) { + assert(v >= LOG_LOOKUP_IDX_MAX); + if (v < APPROX_LOG_WITH_CORRECTION_MAX) { +#if !defined(WEBP_HAVE_SLOW_CLZ_CTZ) + // use clz if available + const int log_cnt = BitsLog2Floor(v) - 7; + const uint32_t y = 1 << log_cnt; + int correction = 0; + const float v_f = (float)v; + const uint32_t orig_v = v; + v >>= log_cnt; +#else + int log_cnt = 0; + uint32_t y = 1; + int correction = 0; + const float v_f = (float)v; + const uint32_t orig_v = v; + do { + ++log_cnt; + v = v >> 1; + y = y << 1; + } while (v >= LOG_LOOKUP_IDX_MAX); +#endif + // vf = (2^log_cnt) * Xf; where y = 2^log_cnt and Xf < 256 + // Xf = floor(Xf) * (1 + (v % y) / v) + // log2(Xf) = log2(floor(Xf)) + log2(1 + (v % y) / v) + // The correction factor: log(1 + d) ~ d; for very small d values, so + // log2(1 + (v % y) / v) ~ LOG_2_RECIPROCAL * (v % y)/v + // LOG_2_RECIPROCAL ~ 23/16 + correction = (23 * (orig_v & (y - 1))) >> 4; + return v_f * (kLog2Table[v] + log_cnt) + correction; + } else { + return (float)(LOG_2_RECIPROCAL * v * log((double)v)); + } +} + +static float FastLog2Slow_C(uint32_t v) { + assert(v >= LOG_LOOKUP_IDX_MAX); + if (v < APPROX_LOG_WITH_CORRECTION_MAX) { +#if !defined(WEBP_HAVE_SLOW_CLZ_CTZ) + // use clz if available + const int log_cnt = BitsLog2Floor(v) - 7; + const uint32_t y = 1 << log_cnt; + const uint32_t orig_v = v; + double log_2; + v >>= log_cnt; +#else + int log_cnt = 0; + uint32_t y = 1; + const uint32_t orig_v = v; + double log_2; + do { + ++log_cnt; + v = v >> 1; + y = y << 1; + } while (v >= LOG_LOOKUP_IDX_MAX); +#endif + log_2 = kLog2Table[v] + log_cnt; + if (orig_v >= APPROX_LOG_MAX) { + // Since the division is still expensive, add this correction factor only + // for large values of 'v'. + const int correction = (23 * (orig_v & (y - 1))) >> 4; + log_2 += (double)correction / orig_v; + } + return (float)log_2; + } else { + return (float)(LOG_2_RECIPROCAL * log((double)v)); + } +} + +//------------------------------------------------------------------------------ +// Methods to calculate Entropy (Shannon). + +// Compute the combined Shanon's entropy for distribution {X} and {X+Y} +static float CombinedShannonEntropy_C(const int X[256], const int Y[256]) { + int i; + double retval = 0.; + int sumX = 0, sumXY = 0; + for (i = 0; i < 256; ++i) { + const int x = X[i]; + if (x != 0) { + const int xy = x + Y[i]; + sumX += x; + retval -= VP8LFastSLog2(x); + sumXY += xy; + retval -= VP8LFastSLog2(xy); + } else if (Y[i] != 0) { + sumXY += Y[i]; + retval -= VP8LFastSLog2(Y[i]); + } + } + retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY); + return (float)retval; +} + +void VP8LBitEntropyInit(VP8LBitEntropy* const entropy) { + entropy->entropy = 0.; + entropy->sum = 0; + entropy->nonzeros = 0; + entropy->max_val = 0; + entropy->nonzero_code = VP8L_NON_TRIVIAL_SYM; +} + +void VP8LBitsEntropyUnrefined(const uint32_t* const array, int n, + VP8LBitEntropy* const entropy) { + int i; + + VP8LBitEntropyInit(entropy); + + for (i = 0; i < n; ++i) { + if (array[i] != 0) { + entropy->sum += array[i]; + entropy->nonzero_code = i; + ++entropy->nonzeros; + entropy->entropy -= VP8LFastSLog2(array[i]); + if (entropy->max_val < array[i]) { + entropy->max_val = array[i]; + } + } + } + entropy->entropy += VP8LFastSLog2(entropy->sum); +} + +static WEBP_INLINE void GetEntropyUnrefinedHelper( + uint32_t val, int i, uint32_t* const val_prev, int* const i_prev, + VP8LBitEntropy* const bit_entropy, VP8LStreaks* const stats) { + const int streak = i - *i_prev; + + // Gather info for the bit entropy. + if (*val_prev != 0) { + bit_entropy->sum += (*val_prev) * streak; + bit_entropy->nonzeros += streak; + bit_entropy->nonzero_code = *i_prev; + bit_entropy->entropy -= VP8LFastSLog2(*val_prev) * streak; + if (bit_entropy->max_val < *val_prev) { + bit_entropy->max_val = *val_prev; + } + } + + // Gather info for the Huffman cost. + stats->counts[*val_prev != 0] += (streak > 3); + stats->streaks[*val_prev != 0][(streak > 3)] += streak; + + *val_prev = val; + *i_prev = i; +} + +static void GetEntropyUnrefined_C(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_C(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); +} + +//------------------------------------------------------------------------------ + +void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels) { + int i; + for (i = 0; i < num_pixels; ++i) { + 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 >> 0) & 0xff) - green) & 0xff; + argb_data[i] = (argb & 0xff00ff00u) | (new_r << 16) | new_b; + } +} + +static WEBP_INLINE int ColorTransformDelta(int8_t color_pred, int8_t color) { + return ((int)color_pred * color) >> 5; +} + +static WEBP_INLINE int8_t U32ToS8(uint32_t v) { + return (int8_t)(v & 0xff); +} + +void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data, + int num_pixels) { + int i; + for (i = 0; i < num_pixels; ++i) { + const uint32_t argb = data[i]; + const int8_t green = U32ToS8(argb >> 8); + const int8_t red = U32ToS8(argb >> 16); + int new_red = red & 0xff; + int new_blue = argb & 0xff; + 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_, red); + new_blue &= 0xff; + data[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); + } +} + +static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red, + uint32_t argb) { + const int8_t green = U32ToS8(argb >> 8); + int new_red = argb >> 16; + new_red -= ColorTransformDelta(green_to_red, green); + return (new_red & 0xff); +} + +static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue, + uint8_t red_to_blue, + uint32_t argb) { + const int8_t green = U32ToS8(argb >> 8); + const int8_t red = U32ToS8(argb >> 16); + uint8_t new_blue = argb & 0xff; + new_blue -= ColorTransformDelta(green_to_blue, green); + new_blue -= ColorTransformDelta(red_to_blue, red); + return (new_blue & 0xff); +} + +void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_red, int histo[]) { + while (tile_height-- > 0) { + int x; + for (x = 0; x < tile_width; ++x) { + ++histo[TransformColorRed((uint8_t)green_to_red, argb[x])]; + } + argb += stride; + } +} + +void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_blue, int red_to_blue, + int histo[]) { + while (tile_height-- > 0) { + int x; + for (x = 0; x < tile_width; ++x) { + ++histo[TransformColorBlue((uint8_t)green_to_blue, (uint8_t)red_to_blue, + argb[x])]; + } + argb += stride; + } +} + +//------------------------------------------------------------------------------ + +static int VectorMismatch_C(const uint32_t* const array1, + const uint32_t* const array2, int length) { + int match_len = 0; + + while (match_len < length && array1[match_len] == array2[match_len]) { + ++match_len; + } + return match_len; +} + +// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel. +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); + const int mask = (1 << xbits) - 1; + uint32_t code = 0xff000000; + for (x = 0; x < width; ++x) { + const int xsub = x & mask; + if (xsub == 0) { + code = 0xff000000; + } + code |= row[x] << (8 + bit_depth * xsub); + dst[x >> xbits] = code; + } + } else { + for (x = 0; x < width; ++x) dst[x] = 0xff000000 | (row[x] << 8); + } +} + +//------------------------------------------------------------------------------ + +static double ExtraCost_C(const uint32_t* population, int length) { + int i; + double cost = 0.; + for (i = 2; i < length - 2; ++i) cost += (i >> 1) * population[i + 2]; + return cost; +} + +static double ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y, + int length) { + int i; + double cost = 0.; + for (i = 2; i < length - 2; ++i) { + const int xy = X[i + 2] + Y[i + 2]; + cost += (i >> 1) * xy; + } + return cost; +} + +//------------------------------------------------------------------------------ + +static void AddVector_C(const uint32_t* a, const uint32_t* b, uint32_t* out, + int size) { + int i; + for (i = 0; i < size; ++i) out[i] = a[i] + b[i]; +} + +static void AddVectorEq_C(const uint32_t* a, uint32_t* out, int size) { + int i; + for (i = 0; i < size; ++i) out[i] += a[i]; +} + +#define ADD(X, ARG, LEN) do { \ + if (a->is_used_[X]) { \ + if (b->is_used_[X]) { \ + VP8LAddVector(a->ARG, b->ARG, out->ARG, (LEN)); \ + } else { \ + memcpy(&out->ARG[0], &a->ARG[0], (LEN) * sizeof(out->ARG[0])); \ + } \ + } else if (b->is_used_[X]) { \ + memcpy(&out->ARG[0], &b->ARG[0], (LEN) * sizeof(out->ARG[0])); \ + } else { \ + memset(&out->ARG[0], 0, (LEN) * sizeof(out->ARG[0])); \ + } \ +} while (0) + +#define ADD_EQ(X, ARG, LEN) do { \ + if (a->is_used_[X]) { \ + if (out->is_used_[X]) { \ + VP8LAddVectorEq(a->ARG, out->ARG, (LEN)); \ + } else { \ + memcpy(&out->ARG[0], &a->ARG[0], (LEN) * sizeof(out->ARG[0])); \ + } \ + } \ +} while (0) + +void VP8LHistogramAdd(const VP8LHistogram* const a, + const VP8LHistogram* const b, VP8LHistogram* const out) { + int i; + const int literal_size = VP8LHistogramNumCodes(a->palette_code_bits_); + assert(a->palette_code_bits_ == b->palette_code_bits_); + + if (b != out) { + ADD(0, literal_, literal_size); + ADD(1, red_, NUM_LITERAL_CODES); + ADD(2, blue_, NUM_LITERAL_CODES); + ADD(3, alpha_, NUM_LITERAL_CODES); + ADD(4, distance_, NUM_DISTANCE_CODES); + for (i = 0; i < 5; ++i) { + out->is_used_[i] = (a->is_used_[i] | b->is_used_[i]); + } + } else { + ADD_EQ(0, literal_, literal_size); + ADD_EQ(1, red_, NUM_LITERAL_CODES); + ADD_EQ(2, blue_, NUM_LITERAL_CODES); + ADD_EQ(3, alpha_, NUM_LITERAL_CODES); + ADD_EQ(4, distance_, NUM_DISTANCE_CODES); + for (i = 0; i < 5; ++i) out->is_used_[i] |= a->is_used_[i]; + } +} +#undef ADD +#undef ADD_EQ + +//------------------------------------------------------------------------------ +// Image transforms. + +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; +} + +// It subtracts the prediction from the input pixel and stores the residual +// in the output pixel. +#define GENERATE_PREDICTOR_SUB(PREDICTOR_I) \ +static void PredictorSub##PREDICTOR_I##_C(const uint32_t* in, \ + const uint32_t* upper, \ + int num_pixels, uint32_t* out) { \ + int x; \ + assert(upper != NULL); \ + for (x = 0; x < num_pixels; ++x) { \ + const uint32_t pred = \ + VP8LPredictor##PREDICTOR_I##_C(&in[x - 1], upper + x); \ + out[x] = VP8LSubPixels(in[x], pred); \ + } \ +} + +GENERATE_PREDICTOR_SUB(2) +GENERATE_PREDICTOR_SUB(3) +GENERATE_PREDICTOR_SUB(4) +GENERATE_PREDICTOR_SUB(5) +GENERATE_PREDICTOR_SUB(6) +GENERATE_PREDICTOR_SUB(7) +GENERATE_PREDICTOR_SUB(8) +GENERATE_PREDICTOR_SUB(9) +GENERATE_PREDICTOR_SUB(10) +GENERATE_PREDICTOR_SUB(11) +GENERATE_PREDICTOR_SUB(12) +GENERATE_PREDICTOR_SUB(13) + +//------------------------------------------------------------------------------ + +VP8LProcessEncBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed; + +VP8LTransformColorFunc VP8LTransformColor; + +VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms; +VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms; + +VP8LFastLog2SlowFunc VP8LFastLog2Slow; +VP8LFastLog2SlowFunc VP8LFastSLog2Slow; + +VP8LCostFunc VP8LExtraCost; +VP8LCostCombinedFunc VP8LExtraCostCombined; +VP8LCombinedShannonEntropyFunc VP8LCombinedShannonEntropy; + +VP8LGetEntropyUnrefinedFunc VP8LGetEntropyUnrefined; +VP8LGetCombinedEntropyUnrefinedFunc VP8LGetCombinedEntropyUnrefined; + +VP8LAddVectorFunc VP8LAddVector; +VP8LAddVectorEqFunc VP8LAddVectorEq; + +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); + +WEBP_DSP_INIT_FUNC(VP8LEncDspInit) { + VP8LDspInit(); + +#if !WEBP_NEON_OMIT_C_CODE + VP8LSubtractGreenFromBlueAndRed = VP8LSubtractGreenFromBlueAndRed_C; + + VP8LTransformColor = VP8LTransformColor_C; +#endif + + VP8LCollectColorBlueTransforms = VP8LCollectColorBlueTransforms_C; + VP8LCollectColorRedTransforms = VP8LCollectColorRedTransforms_C; + + VP8LFastLog2Slow = FastLog2Slow_C; + VP8LFastSLog2Slow = FastSLog2Slow_C; + + VP8LExtraCost = ExtraCost_C; + VP8LExtraCostCombined = ExtraCostCombined_C; + VP8LCombinedShannonEntropy = CombinedShannonEntropy_C; + + VP8LGetEntropyUnrefined = GetEntropyUnrefined_C; + VP8LGetCombinedEntropyUnrefined = GetCombinedEntropyUnrefined_C; + + VP8LAddVector = AddVector_C; + VP8LAddVectorEq = AddVectorEq_C; + + VP8LVectorMismatch = VectorMismatch_C; + 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) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + VP8LEncDspInitSSE2(); +#if defined(WEBP_HAVE_SSE41) + if (VP8GetCPUInfo(kSSE4_1)) { + VP8LEncDspInitSSE41(); + } +#endif + } +#endif +#if defined(WEBP_USE_MIPS32) + if (VP8GetCPUInfo(kMIPS32)) { + VP8LEncDspInitMIPS32(); + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + VP8LEncDspInitMIPSdspR2(); + } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8LEncDspInitMSA(); + } +#endif + } + +#if defined(WEBP_HAVE_NEON) + if (WEBP_NEON_OMIT_C_CODE || + (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) { + VP8LEncDspInitNEON(); + } +#endif + + assert(VP8LSubtractGreenFromBlueAndRed != NULL); + assert(VP8LTransformColor != NULL); + assert(VP8LCollectColorBlueTransforms != NULL); + assert(VP8LCollectColorRedTransforms != NULL); + assert(VP8LFastLog2Slow != NULL); + assert(VP8LFastSLog2Slow != NULL); + assert(VP8LExtraCost != NULL); + assert(VP8LExtraCostCombined != NULL); + assert(VP8LCombinedShannonEntropy != NULL); + assert(VP8LGetEntropyUnrefined != NULL); + assert(VP8LGetCombinedEntropyUnrefined != NULL); + assert(VP8LAddVector != NULL); + assert(VP8LAddVectorEq != NULL); + assert(VP8LVectorMismatch != NULL); + assert(VP8LBundleColorMap != NULL); + assert(VP8LPredictorsSub[0] != NULL); + assert(VP8LPredictorsSub[1] != NULL); + assert(VP8LPredictorsSub[2] != NULL); + assert(VP8LPredictorsSub[3] != NULL); + assert(VP8LPredictorsSub[4] != NULL); + assert(VP8LPredictorsSub[5] != NULL); + assert(VP8LPredictorsSub[6] != NULL); + assert(VP8LPredictorsSub[7] != NULL); + assert(VP8LPredictorsSub[8] != NULL); + assert(VP8LPredictorsSub[9] != NULL); + assert(VP8LPredictorsSub[10] != NULL); + assert(VP8LPredictorsSub[11] != NULL); + assert(VP8LPredictorsSub[12] != NULL); + assert(VP8LPredictorsSub[13] != NULL); + assert(VP8LPredictorsSub[14] != NULL); + assert(VP8LPredictorsSub[15] != NULL); + assert(VP8LPredictorsSub_C[0] != NULL); + assert(VP8LPredictorsSub_C[1] != NULL); + assert(VP8LPredictorsSub_C[2] != NULL); + assert(VP8LPredictorsSub_C[3] != NULL); + assert(VP8LPredictorsSub_C[4] != NULL); + assert(VP8LPredictorsSub_C[5] != NULL); + assert(VP8LPredictorsSub_C[6] != NULL); + assert(VP8LPredictorsSub_C[7] != NULL); + assert(VP8LPredictorsSub_C[8] != NULL); + assert(VP8LPredictorsSub_C[9] != NULL); + assert(VP8LPredictorsSub_C[10] != NULL); + assert(VP8LPredictorsSub_C[11] != NULL); + assert(VP8LPredictorsSub_C[12] != NULL); + assert(VP8LPredictorsSub_C[13] != NULL); + assert(VP8LPredictorsSub_C[14] != NULL); + assert(VP8LPredictorsSub_C[15] != NULL); +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_enc_mips32.c b/internal/libwebp-1.2.1/src/dsp/lossless_enc_mips32.c new file mode 100644 index 0000000..0412a09 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_enc_mips32.c @@ -0,0 +1,397 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS version of lossless functions +// +// Author(s): Djordje Pesut (djordje.pesut@imgtec.com) +// Jovan Zelincevic (jovan.zelincevic@imgtec.com) + +#include "src/dsp/dsp.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" + +#if defined(WEBP_USE_MIPS32) + +#include +#include +#include +#include + +static float FastSLog2Slow_MIPS32(uint32_t v) { + assert(v >= LOG_LOOKUP_IDX_MAX); + if (v < APPROX_LOG_WITH_CORRECTION_MAX) { + uint32_t log_cnt, y, correction; + const int c24 = 24; + const float v_f = (float)v; + uint32_t temp; + + // Xf = 256 = 2^8 + // log_cnt is index of leading one in upper 24 bits + __asm__ volatile( + "clz %[log_cnt], %[v] \n\t" + "addiu %[y], $zero, 1 \n\t" + "subu %[log_cnt], %[c24], %[log_cnt] \n\t" + "sllv %[y], %[y], %[log_cnt] \n\t" + "srlv %[temp], %[v], %[log_cnt] \n\t" + : [log_cnt]"=&r"(log_cnt), [y]"=&r"(y), + [temp]"=r"(temp) + : [c24]"r"(c24), [v]"r"(v) + ); + + // vf = (2^log_cnt) * Xf; where y = 2^log_cnt and Xf < 256 + // Xf = floor(Xf) * (1 + (v % y) / v) + // log2(Xf) = log2(floor(Xf)) + log2(1 + (v % y) / v) + // The correction factor: log(1 + d) ~ d; for very small d values, so + // log2(1 + (v % y) / v) ~ LOG_2_RECIPROCAL * (v % y)/v + // LOG_2_RECIPROCAL ~ 23/16 + + // (v % y) = (v % 2^log_cnt) = v & (2^log_cnt - 1) + correction = (23 * (v & (y - 1))) >> 4; + return v_f * (kLog2Table[temp] + log_cnt) + correction; + } else { + return (float)(LOG_2_RECIPROCAL * v * log((double)v)); + } +} + +static float FastLog2Slow_MIPS32(uint32_t v) { + assert(v >= LOG_LOOKUP_IDX_MAX); + if (v < APPROX_LOG_WITH_CORRECTION_MAX) { + uint32_t log_cnt, y; + const int c24 = 24; + double log_2; + uint32_t temp; + + __asm__ volatile( + "clz %[log_cnt], %[v] \n\t" + "addiu %[y], $zero, 1 \n\t" + "subu %[log_cnt], %[c24], %[log_cnt] \n\t" + "sllv %[y], %[y], %[log_cnt] \n\t" + "srlv %[temp], %[v], %[log_cnt] \n\t" + : [log_cnt]"=&r"(log_cnt), [y]"=&r"(y), + [temp]"=r"(temp) + : [c24]"r"(c24), [v]"r"(v) + ); + + log_2 = kLog2Table[temp] + log_cnt; + if (v >= APPROX_LOG_MAX) { + // Since the division is still expensive, add this correction factor only + // for large values of 'v'. + + const uint32_t correction = (23 * (v & (y - 1))) >> 4; + log_2 += (double)correction / v; + } + return (float)log_2; + } else { + return (float)(LOG_2_RECIPROCAL * log((double)v)); + } +} + +// C version of this function: +// int i = 0; +// int64_t cost = 0; +// const uint32_t* pop = &population[4]; +// const uint32_t* LoopEnd = &population[length]; +// while (pop != LoopEnd) { +// ++i; +// cost += i * *pop; +// cost += i * *(pop + 1); +// pop += 2; +// } +// return (double)cost; +static double ExtraCost_MIPS32(const uint32_t* const population, int length) { + int i, temp0, temp1; + const uint32_t* pop = &population[4]; + const uint32_t* const LoopEnd = &population[length]; + + __asm__ volatile( + "mult $zero, $zero \n\t" + "xor %[i], %[i], %[i] \n\t" + "beq %[pop], %[LoopEnd], 2f \n\t" + "1: \n\t" + "lw %[temp0], 0(%[pop]) \n\t" + "lw %[temp1], 4(%[pop]) \n\t" + "addiu %[i], %[i], 1 \n\t" + "addiu %[pop], %[pop], 8 \n\t" + "madd %[i], %[temp0] \n\t" + "madd %[i], %[temp1] \n\t" + "bne %[pop], %[LoopEnd], 1b \n\t" + "2: \n\t" + "mfhi %[temp0] \n\t" + "mflo %[temp1] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), + [i]"=&r"(i), [pop]"+r"(pop) + : [LoopEnd]"r"(LoopEnd) + : "memory", "hi", "lo" + ); + + return (double)((int64_t)temp0 << 32 | temp1); +} + +// C version of this function: +// int i = 0; +// int64_t cost = 0; +// const uint32_t* pX = &X[4]; +// const uint32_t* pY = &Y[4]; +// const uint32_t* LoopEnd = &X[length]; +// while (pX != LoopEnd) { +// const uint32_t xy0 = *pX + *pY; +// const uint32_t xy1 = *(pX + 1) + *(pY + 1); +// ++i; +// cost += i * xy0; +// cost += i * xy1; +// pX += 2; +// pY += 2; +// } +// return (double)cost; +static double ExtraCostCombined_MIPS32(const uint32_t* const X, + const uint32_t* const Y, int length) { + int i, temp0, temp1, temp2, temp3; + const uint32_t* pX = &X[4]; + const uint32_t* pY = &Y[4]; + const uint32_t* const LoopEnd = &X[length]; + + __asm__ volatile( + "mult $zero, $zero \n\t" + "xor %[i], %[i], %[i] \n\t" + "beq %[pX], %[LoopEnd], 2f \n\t" + "1: \n\t" + "lw %[temp0], 0(%[pX]) \n\t" + "lw %[temp1], 0(%[pY]) \n\t" + "lw %[temp2], 4(%[pX]) \n\t" + "lw %[temp3], 4(%[pY]) \n\t" + "addiu %[i], %[i], 1 \n\t" + "addu %[temp0], %[temp0], %[temp1] \n\t" + "addu %[temp2], %[temp2], %[temp3] \n\t" + "addiu %[pX], %[pX], 8 \n\t" + "addiu %[pY], %[pY], 8 \n\t" + "madd %[i], %[temp0] \n\t" + "madd %[i], %[temp2] \n\t" + "bne %[pX], %[LoopEnd], 1b \n\t" + "2: \n\t" + "mfhi %[temp0] \n\t" + "mflo %[temp1] \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), + [i]"=&r"(i), [pX]"+r"(pX), [pY]"+r"(pY) + : [LoopEnd]"r"(LoopEnd) + : "memory", "hi", "lo" + ); + + return (double)((int64_t)temp0 << 32 | temp1); +} + +#define HUFFMAN_COST_PASS \ + __asm__ volatile( \ + "sll %[temp1], %[temp0], 3 \n\t" \ + "addiu %[temp3], %[streak], -3 \n\t" \ + "addu %[temp2], %[pstreaks], %[temp1] \n\t" \ + "blez %[temp3], 1f \n\t" \ + "srl %[temp1], %[temp1], 1 \n\t" \ + "addu %[temp3], %[pcnts], %[temp1] \n\t" \ + "lw %[temp0], 4(%[temp2]) \n\t" \ + "lw %[temp1], 0(%[temp3]) \n\t" \ + "addu %[temp0], %[temp0], %[streak] \n\t" \ + "addiu %[temp1], %[temp1], 1 \n\t" \ + "sw %[temp0], 4(%[temp2]) \n\t" \ + "sw %[temp1], 0(%[temp3]) \n\t" \ + "b 2f \n\t" \ + "1: \n\t" \ + "lw %[temp0], 0(%[temp2]) \n\t" \ + "addu %[temp0], %[temp0], %[streak] \n\t" \ + "sw %[temp0], 0(%[temp2]) \n\t" \ + "2: \n\t" \ + : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \ + [temp3]"=&r"(temp3), [temp0]"+r"(temp0) \ + : [pstreaks]"r"(pstreaks), [pcnts]"r"(pcnts), \ + [streak]"r"(streak) \ + : "memory" \ + ); + +// Returns the various RLE counts +static WEBP_INLINE void GetEntropyUnrefinedHelper( + uint32_t val, int i, uint32_t* const val_prev, int* const i_prev, + VP8LBitEntropy* const bit_entropy, VP8LStreaks* const stats) { + int* const pstreaks = &stats->streaks[0][0]; + int* const pcnts = &stats->counts[0]; + int temp0, temp1, temp2, temp3; + const int streak = i - *i_prev; + + // Gather info for the bit entropy. + if (*val_prev != 0) { + bit_entropy->sum += (*val_prev) * streak; + bit_entropy->nonzeros += streak; + bit_entropy->nonzero_code = *i_prev; + bit_entropy->entropy -= VP8LFastSLog2(*val_prev) * streak; + if (bit_entropy->max_val < *val_prev) { + bit_entropy->max_val = *val_prev; + } + } + + // Gather info for the Huffman cost. + temp0 = (*val_prev != 0); + HUFFMAN_COST_PASS + + *val_prev = val; + *i_prev = i; +} + +static void GetEntropyUnrefined_MIPS32(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_MIPS32(const uint32_t X[], + const uint32_t Y[], + int length, + VP8LBitEntropy* const 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(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, entropy, stats); + } + } + GetEntropyUnrefinedHelper(0, i, &xy_prev, &i_prev, entropy, stats); + + entropy->entropy += VP8LFastSLog2(entropy->sum); +} + +#define ASM_START \ + __asm__ volatile( \ + ".set push \n\t" \ + ".set at \n\t" \ + ".set macro \n\t" \ + "1: \n\t" + +// P2 = P0 + P1 +// A..D - offsets +// E - temp variable to tell macro +// if pointer should be incremented +// literal_ and successive histograms could be unaligned +// so we must use ulw and usw +#define ADD_TO_OUT(A, B, C, D, E, P0, P1, P2) \ + "ulw %[temp0], " #A "(%[" #P0 "]) \n\t" \ + "ulw %[temp1], " #B "(%[" #P0 "]) \n\t" \ + "ulw %[temp2], " #C "(%[" #P0 "]) \n\t" \ + "ulw %[temp3], " #D "(%[" #P0 "]) \n\t" \ + "ulw %[temp4], " #A "(%[" #P1 "]) \n\t" \ + "ulw %[temp5], " #B "(%[" #P1 "]) \n\t" \ + "ulw %[temp6], " #C "(%[" #P1 "]) \n\t" \ + "ulw %[temp7], " #D "(%[" #P1 "]) \n\t" \ + "addu %[temp4], %[temp4], %[temp0] \n\t" \ + "addu %[temp5], %[temp5], %[temp1] \n\t" \ + "addu %[temp6], %[temp6], %[temp2] \n\t" \ + "addu %[temp7], %[temp7], %[temp3] \n\t" \ + "addiu %[" #P0 "], %[" #P0 "], 16 \n\t" \ + ".if " #E " == 1 \n\t" \ + "addiu %[" #P1 "], %[" #P1 "], 16 \n\t" \ + ".endif \n\t" \ + "usw %[temp4], " #A "(%[" #P2 "]) \n\t" \ + "usw %[temp5], " #B "(%[" #P2 "]) \n\t" \ + "usw %[temp6], " #C "(%[" #P2 "]) \n\t" \ + "usw %[temp7], " #D "(%[" #P2 "]) \n\t" \ + "addiu %[" #P2 "], %[" #P2 "], 16 \n\t" \ + "bne %[" #P0 "], %[LoopEnd], 1b \n\t" \ + ".set pop \n\t" \ + +#define ASM_END_COMMON_0 \ + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), \ + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), \ + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \ + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), \ + [pa]"+r"(pa), [pout]"+r"(pout) + +#define ASM_END_COMMON_1 \ + : [LoopEnd]"r"(LoopEnd) \ + : "memory", "at" \ + ); + +#define ASM_END_0 \ + ASM_END_COMMON_0 \ + , [pb]"+r"(pb) \ + ASM_END_COMMON_1 + +#define ASM_END_1 \ + ASM_END_COMMON_0 \ + ASM_END_COMMON_1 + +static void AddVector_MIPS32(const uint32_t* pa, const uint32_t* pb, + uint32_t* pout, int size) { + uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + const uint32_t end = ((size) / 4) * 4; + const uint32_t* const LoopEnd = pa + end; + int i; + ASM_START + ADD_TO_OUT(0, 4, 8, 12, 1, pa, pb, pout) + ASM_END_0 + for (i = end; i < size; ++i) pout[i] = pa[i] + pb[i]; +} + +static void AddVectorEq_MIPS32(const uint32_t* pa, uint32_t* pout, int size) { + uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + const uint32_t end = ((size) / 4) * 4; + const uint32_t* const LoopEnd = pa + end; + int i; + ASM_START + ADD_TO_OUT(0, 4, 8, 12, 0, pa, pout, pout) + ASM_END_1 + for (i = end; i < size; ++i) pout[i] += pa[i]; +} + +#undef ASM_END_1 +#undef ASM_END_0 +#undef ASM_END_COMMON_1 +#undef ASM_END_COMMON_0 +#undef ADD_TO_OUT +#undef ASM_START + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LEncDspInitMIPS32(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitMIPS32(void) { + VP8LFastSLog2Slow = FastSLog2Slow_MIPS32; + VP8LFastLog2Slow = FastLog2Slow_MIPS32; + VP8LExtraCost = ExtraCost_MIPS32; + VP8LExtraCostCombined = ExtraCostCombined_MIPS32; + VP8LGetEntropyUnrefined = GetEntropyUnrefined_MIPS32; + VP8LGetCombinedEntropyUnrefined = GetCombinedEntropyUnrefined_MIPS32; + VP8LAddVector = AddVector_MIPS32; + VP8LAddVectorEq = AddVectorEq_MIPS32; +} + +#else // !WEBP_USE_MIPS32 + +WEBP_DSP_INIT_STUB(VP8LEncDspInitMIPS32) + +#endif // WEBP_USE_MIPS32 diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_enc_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/lossless_enc_mips_dsp_r2.c new file mode 100644 index 0000000..5855e6a --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_enc_mips_dsp_r2.c @@ -0,0 +1,281 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Image transform methods for lossless encoder. +// +// Author(s): Djordje Pesut (djordje.pesut@imgtec.com) +// Jovan Zelincevic (jovan.zelincevic@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) + +#include "src/dsp/lossless.h" + +static void SubtractGreenFromBlueAndRed_MIPSdspR2(uint32_t* argb_data, + int num_pixels) { + uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + uint32_t* const p_loop1_end = argb_data + (num_pixels & ~3); + uint32_t* const p_loop2_end = p_loop1_end + (num_pixels & 3); + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "beq %[argb_data], %[p_loop1_end], 3f \n\t" + " nop \n\t" + "0: \n\t" + "lw %[temp0], 0(%[argb_data]) \n\t" + "lw %[temp1], 4(%[argb_data]) \n\t" + "lw %[temp2], 8(%[argb_data]) \n\t" + "lw %[temp3], 12(%[argb_data]) \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 %[argb_data], %[argb_data], 16 \n\t" + "replv.ph %[temp4], %[temp4] \n\t" + "replv.ph %[temp5], %[temp5] \n\t" + "replv.ph %[temp6], %[temp6] \n\t" + "replv.ph %[temp7], %[temp7] \n\t" + "subu.qb %[temp0], %[temp0], %[temp4] \n\t" + "subu.qb %[temp1], %[temp1], %[temp5] \n\t" + "subu.qb %[temp2], %[temp2], %[temp6] \n\t" + "subu.qb %[temp3], %[temp3], %[temp7] \n\t" + "sw %[temp0], -16(%[argb_data]) \n\t" + "sw %[temp1], -12(%[argb_data]) \n\t" + "sw %[temp2], -8(%[argb_data]) \n\t" + "bne %[argb_data], %[p_loop1_end], 0b \n\t" + " sw %[temp3], -4(%[argb_data]) \n\t" + "3: \n\t" + "beq %[argb_data], %[p_loop2_end], 2f \n\t" + " nop \n\t" + "1: \n\t" + "lw %[temp0], 0(%[argb_data]) \n\t" + "addiu %[argb_data], %[argb_data], 4 \n\t" + "ext %[temp4], %[temp0], 8, 8 \n\t" + "replv.ph %[temp4], %[temp4] \n\t" + "subu.qb %[temp0], %[temp0], %[temp4] \n\t" + "bne %[argb_data], %[p_loop2_end], 1b \n\t" + " sw %[temp0], -4(%[argb_data]) \n\t" + "2: \n\t" + ".set pop \n\t" + : [argb_data]"+&r"(argb_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) + : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end) + : "memory" + ); +} + +static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred, + int8_t color) { + return (uint32_t)((int)(color_pred) * color) >> 5; +} + +static void TransformColor_MIPSdspR2(const VP8LMultipliers* const m, + uint32_t* data, int num_pixels) { + int temp0, temp1, temp2, temp3, temp4, temp5; + uint32_t argb, argb1, new_red, new_red1; + 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); + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \n\t" + "beq %[data], %[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" + "replv.ph %[temp2], %[R_to_B] \n\t" + "shll.ph %[temp0], %[temp0], 8 \n\t" + "shll.ph %[temp1], %[temp1], 8 \n\t" + "shll.ph %[temp2], %[temp2], 8 \n\t" + "shra.ph %[temp0], %[temp0], 8 \n\t" + "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" + "lhu %[new_red], 2(%[data]) \n\t" + "lhu %[new_red1], 6(%[data]) \n\t" + "precrq.qb.ph %[temp3], %[argb], %[argb1] \n\t" + "precr.qb.ph %[temp4], %[argb], %[argb1] \n\t" + "preceu.ph.qbra %[temp3], %[temp3] \n\t" + "preceu.ph.qbla %[temp4], %[temp4] \n\t" + "shll.ph %[temp3], %[temp3], 8 \n\t" + "shll.ph %[temp4], %[temp4], 8 \n\t" + "shra.ph %[temp3], %[temp3], 8 \n\t" + "shra.ph %[temp4], %[temp4], 8 \n\t" + "mul.ph %[temp5], %[temp3], %[temp0] \n\t" + "mul.ph %[temp3], %[temp3], %[temp1] \n\t" + "mul.ph %[temp4], %[temp4], %[temp2] \n\t" + "addiu %[data], %[data], 8 \n\t" + "ins %[new_red1], %[new_red], 16, 16 \n\t" + "ins %[argb1], %[argb], 16, 16 \n\t" + "shra.ph %[temp5], %[temp5], 5 \n\t" + "shra.ph %[temp3], %[temp3], 5 \n\t" + "shra.ph %[temp4], %[temp4], 5 \n\t" + "subu.ph %[new_red1], %[new_red1], %[temp5] \n\t" + "subu.ph %[argb1], %[argb1], %[temp3] \n\t" + "preceu.ph.qbra %[temp5], %[new_red1] \n\t" + "subu.ph %[argb1], %[argb1], %[temp4] \n\t" + "preceu.ph.qbra %[temp3], %[argb1] \n\t" + "sb %[temp5], -2(%[data]) \n\t" + "sb %[temp3], -4(%[data]) \n\t" + "sra %[temp5], %[temp5], 16 \n\t" + "sra %[temp3], %[temp3], 16 \n\t" + "sb %[temp5], -6(%[data]) \n\t" + "bne %[data], %[p_loop_end], 0b \n\t" + " sb %[temp3], -8(%[data]) \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_red1]"=&r"(new_red1), [new_red]"=&r"(new_red), + [argb]"=&r"(argb), [argb1]"=&r"(argb1), [data]"+&r"(data) + : [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" + ); + + if (num_pixels & 1) { + const uint32_t argb_ = data[0]; + const uint32_t green = argb_ >> 8; + const uint32_t red = argb_ >> 16; + uint32_t new_blue = argb_; + new_red = red; + 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_, red); + new_blue &= 0xff; + data[0] = (argb_ & 0xff00ff00u) | (new_red << 16) | (new_blue); + } +} + +static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue, + uint8_t red_to_blue, + uint32_t argb) { + const uint32_t green = argb >> 8; + const uint32_t red = argb >> 16; + uint8_t new_blue = argb; + new_blue -= ColorTransformDelta(green_to_blue, green); + new_blue -= ColorTransformDelta(red_to_blue, red); + return (new_blue & 0xff); +} + +static void CollectColorBlueTransforms_MIPSdspR2(const uint32_t* argb, + int stride, + int tile_width, + int tile_height, + int green_to_blue, + int red_to_blue, + int histo[]) { + const int rtb = (red_to_blue << 16) | (red_to_blue & 0xffff); + const int gtb = (green_to_blue << 16) | (green_to_blue & 0xffff); + const uint32_t mask = 0xff00ffu; + while (tile_height-- > 0) { + int x; + const uint32_t* p_argb = argb; + argb += stride; + for (x = 0; x < (tile_width >> 1); ++x) { + int temp0, temp1, temp2, temp3, temp4, temp5, temp6; + __asm__ volatile ( + "lw %[temp0], 0(%[p_argb]) \n\t" + "lw %[temp1], 4(%[p_argb]) \n\t" + "precr.qb.ph %[temp2], %[temp0], %[temp1] \n\t" + "ins %[temp1], %[temp0], 16, 16 \n\t" + "shra.ph %[temp2], %[temp2], 8 \n\t" + "shra.ph %[temp3], %[temp1], 8 \n\t" + "mul.ph %[temp5], %[temp2], %[rtb] \n\t" + "mul.ph %[temp6], %[temp3], %[gtb] \n\t" + "and %[temp4], %[temp1], %[mask] \n\t" + "addiu %[p_argb], %[p_argb], 8 \n\t" + "shra.ph %[temp5], %[temp5], 5 \n\t" + "shra.ph %[temp6], %[temp6], 5 \n\t" + "subu.qb %[temp2], %[temp4], %[temp5] \n\t" + "subu.qb %[temp2], %[temp2], %[temp6] \n\t" + : [p_argb]"+&r"(p_argb), [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), + [temp5]"=&r"(temp5), [temp6]"=&r"(temp6) + : [rtb]"r"(rtb), [gtb]"r"(gtb), [mask]"r"(mask) + : "memory", "hi", "lo" + ); + ++histo[(uint8_t)(temp2 >> 16)]; + ++histo[(uint8_t)temp2]; + } + if (tile_width & 1) { + ++histo[TransformColorBlue(green_to_blue, red_to_blue, *p_argb)]; + } + } +} + +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; + new_red -= ColorTransformDelta(green_to_red, green); + return (new_red & 0xff); +} + +static void CollectColorRedTransforms_MIPSdspR2(const uint32_t* argb, + int stride, + int tile_width, + int tile_height, + int green_to_red, + int histo[]) { + const int gtr = (green_to_red << 16) | (green_to_red & 0xffff); + while (tile_height-- > 0) { + int x; + const uint32_t* p_argb = argb; + argb += stride; + for (x = 0; x < (tile_width >> 1); ++x) { + int temp0, temp1, temp2, temp3, temp4; + __asm__ volatile ( + "lw %[temp0], 0(%[p_argb]) \n\t" + "lw %[temp1], 4(%[p_argb]) \n\t" + "precrq.ph.w %[temp4], %[temp0], %[temp1] \n\t" + "ins %[temp1], %[temp0], 16, 16 \n\t" + "shra.ph %[temp3], %[temp1], 8 \n\t" + "mul.ph %[temp2], %[temp3], %[gtr] \n\t" + "addiu %[p_argb], %[p_argb], 8 \n\t" + "shra.ph %[temp2], %[temp2], 5 \n\t" + "subu.qb %[temp2], %[temp4], %[temp2] \n\t" + : [p_argb]"+&r"(p_argb), [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4) + : [gtr]"r"(gtr) + : "memory", "hi", "lo" + ); + ++histo[(uint8_t)(temp2 >> 16)]; + ++histo[(uint8_t)temp2]; + } + if (tile_width & 1) { + ++histo[TransformColorRed(green_to_red, *p_argb)]; + } + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LEncDspInitMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitMIPSdspR2(void) { + VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_MIPSdspR2; + VP8LTransformColor = TransformColor_MIPSdspR2; + VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_MIPSdspR2; + VP8LCollectColorRedTransforms = CollectColorRedTransforms_MIPSdspR2; +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(VP8LEncDspInitMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_enc_msa.c b/internal/libwebp-1.2.1/src/dsp/lossless_enc_msa.c new file mode 100644 index 0000000..600dddf --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_enc_msa.c @@ -0,0 +1,148 @@ +// 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 "src/dsp/dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "src/dsp/lossless.h" +#include "src/dsp/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_MSA(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_MSA(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_MSA; + VP8LTransformColor = TransformColor_MSA; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(VP8LEncDspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_enc_neon.c b/internal/libwebp-1.2.1/src/dsp/lossless_enc_neon.c new file mode 100644 index 0000000..7c7b73f --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_enc_neon.c @@ -0,0 +1,144 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// NEON variant of methods for lossless encoder +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_NEON) + +#include + +#include "src/dsp/lossless.h" +#include "src/dsp/neon.h" + +//------------------------------------------------------------------------------ +// Subtract-Green Transform + +// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use +// non-standard versions there. +#if defined(__APPLE__) && defined(__aarch64__) && \ + defined(__apple_build_version__) && (__apple_build_version__< 6020037) +#define USE_VTBLQ +#endif + +#ifdef USE_VTBLQ +// 255 = byte will be zeroed +static const uint8_t kGreenShuffle[16] = { + 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255 +}; + +static WEBP_INLINE uint8x16_t DoGreenShuffle_NEON(const uint8x16_t argb, + const uint8x16_t shuffle) { + return vcombine_u8(vtbl1q_u8(argb, vget_low_u8(shuffle)), + vtbl1q_u8(argb, vget_high_u8(shuffle))); +} +#else // !USE_VTBLQ +// 255 = byte will be zeroed +static const uint8_t kGreenShuffle[8] = { 1, 255, 1, 255, 5, 255, 5, 255 }; + +static WEBP_INLINE uint8x16_t DoGreenShuffle_NEON(const uint8x16_t argb, + const uint8x8_t shuffle) { + return vcombine_u8(vtbl1_u8(vget_low_u8(argb), shuffle), + vtbl1_u8(vget_high_u8(argb), shuffle)); +} +#endif // USE_VTBLQ + +static void SubtractGreenFromBlueAndRed_NEON(uint32_t* argb_data, + int num_pixels) { + const uint32_t* const end = argb_data + (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); + const uint8x16_t greens = DoGreenShuffle_NEON(argb, shuffle); + vst1q_u8((uint8_t*)argb_data, vsubq_u8(argb, greens)); + } + // fallthrough and finish off with plain-C + VP8LSubtractGreenFromBlueAndRed_C(argb_data, num_pixels & 3); +} + +//------------------------------------------------------------------------------ +// Color Transform + +static void TransformColor_NEON(const VP8LMultipliers* const m, + uint32_t* argb_data, int num_pixels) { + // 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_), + CST(green_to_blue_), CST(green_to_red_), + CST(green_to_blue_), CST(green_to_red_), + CST(green_to_blue_), CST(green_to_red_) + }; + const int16x8_t mults_rb = vld1q_s16(rb); + const int16_t b2[8] = { + 0, CST(red_to_blue_), 0, CST(red_to_blue_), + 0, CST(red_to_blue_), 0, CST(red_to_blue_), + }; + const int16x8_t mults_b2 = vld1q_s16(b2); +#undef CST +#ifdef USE_VTBLQ + static const uint8_t kg0g0[16] = { + 255, 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13 + }; + const uint8x16_t shuffle = vld1q_u8(kg0g0); +#else + static const uint8_t k0g0g[8] = { 255, 1, 255, 1, 255, 5, 255, 5 }; + const uint8x8_t shuffle = vld1_u8(k0g0g); +#endif + const uint32x4_t mask_rb = vdupq_n_u32(0x00ff00ffu); // red-blue masks + int i; + for (i = 0; i + 4 <= num_pixels; i += 4) { + const uint8x16_t in = vld1q_u8((uint8_t*)(argb_data + i)); + // 0 g 0 g + const uint8x16_t greens = DoGreenShuffle_NEON(in, shuffle); + // x dr x db1 + const int16x8_t A = vqdmulhq_s16(vreinterpretq_s16_u8(greens), mults_rb); + // r 0 b 0 + const int16x8_t B = vshlq_n_s16(vreinterpretq_s16_u8(in), 8); + // x db2 0 0 + const int16x8_t C = vqdmulhq_s16(B, mults_b2); + // 0 0 x db2 + const uint32x4_t D = vshrq_n_u32(vreinterpretq_u32_s16(C), 16); + // x dr x db + const int8x16_t E = vaddq_s8(vreinterpretq_s8_u32(D), + vreinterpretq_s8_s16(A)); + // 0 dr 0 db + const uint32x4_t F = vandq_u32(vreinterpretq_u32_s8(E), mask_rb); + const int8x16_t out = vsubq_s8(vreinterpretq_s8_u8(in), + vreinterpretq_s8_u32(F)); + vst1q_s8((int8_t*)(argb_data + i), out); + } + // fallthrough and finish off with plain-C + VP8LTransformColor_C(m, argb_data + i, num_pixels - i); +} + +#undef USE_VTBLQ + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LEncDspInitNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitNEON(void) { + VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_NEON; + VP8LTransformColor = TransformColor_NEON; +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(VP8LEncDspInitNEON) + +#endif // WEBP_USE_NEON diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_enc_sse2.c b/internal/libwebp-1.2.1/src/dsp/lossless_enc_sse2.c new file mode 100644 index 0000000..b2f83b8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_enc_sse2.c @@ -0,0 +1,669 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE2 variant of methods for lossless encoder +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) +#include +#include +#include "src/dsp/lossless.h" +#include "src/dsp/common_sse2.h" +#include "src/dsp/lossless_common.h" + +// For sign-extended multiplying constants, pre-shifted by 5: +#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5) + +//------------------------------------------------------------------------------ +// Subtract-Green Transform + +static void SubtractGreenFromBlueAndRed_SSE2(uint32_t* argb_data, + int num_pixels) { + int i; + for (i = 0; i + 4 <= num_pixels; i += 4) { + const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[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_sub_epi8(in, C); + _mm_storeu_si128((__m128i*)&argb_data[i], out); + } + // fallthrough and finish off with plain-C + if (i != num_pixels) { + VP8LSubtractGreenFromBlueAndRed_C(argb_data + i, num_pixels - i); + } +} + +//------------------------------------------------------------------------------ +// Color Transform + +#define MK_CST_16(HI, LO) \ + _mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff))) + +static void TransformColor_SSE2(const VP8LMultipliers* const m, + uint32_t* argb_data, int num_pixels) { + const __m128i mults_rb = MK_CST_16(CST_5b(m->green_to_red_), + CST_5b(m->green_to_blue_)); + const __m128i mults_b2 = MK_CST_16(CST_5b(m->red_to_blue_), 0); + const __m128i mask_ag = _mm_set1_epi32(0xff00ff00); // alpha-green masks + const __m128i mask_rb = _mm_set1_epi32(0x00ff00ff); // red-blue 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 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 + const __m128i D = _mm_mulhi_epi16(C, mults_rb); // x dr x db1 + const __m128i E = _mm_slli_epi16(in, 8); // r 0 b 0 + const __m128i F = _mm_mulhi_epi16(E, mults_b2); // x db2 0 0 + const __m128i G = _mm_srli_epi32(F, 16); // 0 0 x db2 + const __m128i H = _mm_add_epi8(G, D); // x dr x db + const __m128i I = _mm_and_si128(H, mask_rb); // 0 dr 0 db + const __m128i out = _mm_sub_epi8(in, I); + _mm_storeu_si128((__m128i*)&argb_data[i], out); + } + // fallthrough and finish off with plain-C + if (i != num_pixels) { + VP8LTransformColor_C(m, argb_data + i, num_pixels - i); + } +} + +//------------------------------------------------------------------------------ +#define SPAN 8 +static void CollectColorBlueTransforms_SSE2(const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_blue, int red_to_blue, + int histo[]) { + const __m128i mults_r = MK_CST_16(CST_5b(red_to_blue), 0); + const __m128i mults_g = MK_CST_16(0, CST_5b(green_to_blue)); + const __m128i mask_g = _mm_set1_epi32(0x00ff00); // green mask + const __m128i mask_b = _mm_set1_epi32(0x0000ff); // blue mask + int y; + for (y = 0; y < tile_height; ++y) { + const uint32_t* const src = argb + y * stride; + int i, x; + for (x = 0; x + SPAN <= tile_width; x += SPAN) { + uint16_t values[SPAN]; + const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]); + const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]); + const __m128i A0 = _mm_slli_epi16(in0, 8); // r 0 | b 0 + const __m128i A1 = _mm_slli_epi16(in1, 8); + const __m128i B0 = _mm_and_si128(in0, mask_g); // 0 0 | g 0 + const __m128i B1 = _mm_and_si128(in1, mask_g); + const __m128i C0 = _mm_mulhi_epi16(A0, mults_r); // x db | 0 0 + const __m128i C1 = _mm_mulhi_epi16(A1, mults_r); + const __m128i D0 = _mm_mulhi_epi16(B0, mults_g); // 0 0 | x db + const __m128i D1 = _mm_mulhi_epi16(B1, mults_g); + const __m128i E0 = _mm_sub_epi8(in0, D0); // x x | x b' + const __m128i E1 = _mm_sub_epi8(in1, D1); + const __m128i F0 = _mm_srli_epi32(C0, 16); // 0 0 | x db + const __m128i F1 = _mm_srli_epi32(C1, 16); + const __m128i G0 = _mm_sub_epi8(E0, F0); // 0 0 | x b' + const __m128i G1 = _mm_sub_epi8(E1, F1); + const __m128i H0 = _mm_and_si128(G0, mask_b); // 0 0 | 0 b + const __m128i H1 = _mm_and_si128(G1, mask_b); + const __m128i I = _mm_packs_epi32(H0, H1); // 0 b' | 0 b' + _mm_storeu_si128((__m128i*)values, I); + for (i = 0; i < SPAN; ++i) ++histo[values[i]]; + } + } + { + const int left_over = tile_width & (SPAN - 1); + if (left_over > 0) { + VP8LCollectColorBlueTransforms_C(argb + tile_width - left_over, stride, + left_over, tile_height, + green_to_blue, red_to_blue, histo); + } + } +} + +static void CollectColorRedTransforms_SSE2(const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_red, int histo[]) { + const __m128i mults_g = MK_CST_16(0, CST_5b(green_to_red)); + const __m128i mask_g = _mm_set1_epi32(0x00ff00); // green mask + const __m128i mask = _mm_set1_epi32(0xff); + + int y; + for (y = 0; y < tile_height; ++y) { + const uint32_t* const src = argb + y * stride; + int i, x; + for (x = 0; x + SPAN <= tile_width; x += SPAN) { + uint16_t values[SPAN]; + const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]); + const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]); + const __m128i A0 = _mm_and_si128(in0, mask_g); // 0 0 | g 0 + const __m128i A1 = _mm_and_si128(in1, mask_g); + const __m128i B0 = _mm_srli_epi32(in0, 16); // 0 0 | x r + const __m128i B1 = _mm_srli_epi32(in1, 16); + const __m128i C0 = _mm_mulhi_epi16(A0, mults_g); // 0 0 | x dr + const __m128i C1 = _mm_mulhi_epi16(A1, mults_g); + const __m128i E0 = _mm_sub_epi8(B0, C0); // x x | x r' + const __m128i E1 = _mm_sub_epi8(B1, C1); + const __m128i F0 = _mm_and_si128(E0, mask); // 0 0 | 0 r' + const __m128i F1 = _mm_and_si128(E1, mask); + const __m128i I = _mm_packs_epi32(F0, F1); + _mm_storeu_si128((__m128i*)values, I); + for (i = 0; i < SPAN; ++i) ++histo[values[i]]; + } + } + { + const int left_over = tile_width & (SPAN - 1); + if (left_over > 0) { + VP8LCollectColorRedTransforms_C(argb + tile_width - left_over, stride, + left_over, tile_height, + green_to_red, histo); + } + } +} +#undef SPAN +#undef MK_CST_16 + +//------------------------------------------------------------------------------ + +// Note we are adding uint32_t's as *signed* int32's (using _mm_add_epi32). But +// that's ok since the histogram values are less than 1<<28 (max picture size). +#define LINE_SIZE 16 // 8 or 16 +static void AddVector_SSE2(const uint32_t* a, const uint32_t* b, uint32_t* out, + int size) { + int i; + for (i = 0; i + LINE_SIZE <= size; i += LINE_SIZE) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i + 0]); + const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]); +#if (LINE_SIZE == 16) + const __m128i a2 = _mm_loadu_si128((const __m128i*)&a[i + 8]); + const __m128i a3 = _mm_loadu_si128((const __m128i*)&a[i + 12]); +#endif + const __m128i b0 = _mm_loadu_si128((const __m128i*)&b[i + 0]); + const __m128i b1 = _mm_loadu_si128((const __m128i*)&b[i + 4]); +#if (LINE_SIZE == 16) + const __m128i b2 = _mm_loadu_si128((const __m128i*)&b[i + 8]); + const __m128i b3 = _mm_loadu_si128((const __m128i*)&b[i + 12]); +#endif + _mm_storeu_si128((__m128i*)&out[i + 0], _mm_add_epi32(a0, b0)); + _mm_storeu_si128((__m128i*)&out[i + 4], _mm_add_epi32(a1, b1)); +#if (LINE_SIZE == 16) + _mm_storeu_si128((__m128i*)&out[i + 8], _mm_add_epi32(a2, b2)); + _mm_storeu_si128((__m128i*)&out[i + 12], _mm_add_epi32(a3, b3)); +#endif + } + for (; i < size; ++i) { + out[i] = a[i] + b[i]; + } +} + +static void AddVectorEq_SSE2(const uint32_t* a, uint32_t* out, int size) { + int i; + for (i = 0; i + LINE_SIZE <= size; i += LINE_SIZE) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i + 0]); + const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]); +#if (LINE_SIZE == 16) + const __m128i a2 = _mm_loadu_si128((const __m128i*)&a[i + 8]); + const __m128i a3 = _mm_loadu_si128((const __m128i*)&a[i + 12]); +#endif + const __m128i b0 = _mm_loadu_si128((const __m128i*)&out[i + 0]); + const __m128i b1 = _mm_loadu_si128((const __m128i*)&out[i + 4]); +#if (LINE_SIZE == 16) + const __m128i b2 = _mm_loadu_si128((const __m128i*)&out[i + 8]); + const __m128i b3 = _mm_loadu_si128((const __m128i*)&out[i + 12]); +#endif + _mm_storeu_si128((__m128i*)&out[i + 0], _mm_add_epi32(a0, b0)); + _mm_storeu_si128((__m128i*)&out[i + 4], _mm_add_epi32(a1, b1)); +#if (LINE_SIZE == 16) + _mm_storeu_si128((__m128i*)&out[i + 8], _mm_add_epi32(a2, b2)); + _mm_storeu_si128((__m128i*)&out[i + 12], _mm_add_epi32(a3, b3)); +#endif + } + for (; i < size; ++i) { + out[i] += a[i]; + } +} +#undef LINE_SIZE + +//------------------------------------------------------------------------------ +// Entropy + +// TODO(https://crbug.com/webp/499): this function produces different results +// from the C code due to use of double/float resulting in output differences +// when compared to -noasm. +#if !(defined(WEBP_HAVE_SLOW_CLZ_CTZ) || defined(__i386__) || defined(_M_IX86)) + +static float CombinedShannonEntropy_SSE2(const int X[256], const int Y[256]) { + int i; + double retval = 0.; + int sumX = 0, sumXY = 0; + const __m128i zero = _mm_setzero_si128(); + + for (i = 0; i < 256; i += 16) { + const __m128i x0 = _mm_loadu_si128((const __m128i*)(X + i + 0)); + const __m128i y0 = _mm_loadu_si128((const __m128i*)(Y + i + 0)); + const __m128i x1 = _mm_loadu_si128((const __m128i*)(X + i + 4)); + const __m128i y1 = _mm_loadu_si128((const __m128i*)(Y + i + 4)); + const __m128i x2 = _mm_loadu_si128((const __m128i*)(X + i + 8)); + const __m128i y2 = _mm_loadu_si128((const __m128i*)(Y + i + 8)); + const __m128i x3 = _mm_loadu_si128((const __m128i*)(X + i + 12)); + const __m128i y3 = _mm_loadu_si128((const __m128i*)(Y + i + 12)); + const __m128i x4 = _mm_packs_epi16(_mm_packs_epi32(x0, x1), + _mm_packs_epi32(x2, x3)); + const __m128i y4 = _mm_packs_epi16(_mm_packs_epi32(y0, y1), + _mm_packs_epi32(y2, y3)); + const int32_t mx = _mm_movemask_epi8(_mm_cmpgt_epi8(x4, zero)); + int32_t my = _mm_movemask_epi8(_mm_cmpgt_epi8(y4, zero)) | mx; + while (my) { + const int32_t j = BitsCtz(my); + int xy; + if ((mx >> j) & 1) { + const int x = X[i + j]; + sumXY += x; + retval -= VP8LFastSLog2(x); + } + xy = X[i + j] + Y[i + j]; + sumX += xy; + retval -= VP8LFastSLog2(xy); + my &= my - 1; + } + } + retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY); + return (float)retval; +} + +#else + +#define DONT_USE_COMBINED_SHANNON_ENTROPY_SSE2_FUNC // won't be faster + +#endif + +//------------------------------------------------------------------------------ + +static int VectorMismatch_SSE2(const uint32_t* const array1, + const uint32_t* const array2, int length) { + int match_len; + + if (length >= 12) { + __m128i A0 = _mm_loadu_si128((const __m128i*)&array1[0]); + __m128i A1 = _mm_loadu_si128((const __m128i*)&array2[0]); + match_len = 0; + do { + // Loop unrolling and early load both provide a speedup of 10% for the + // current function. Also, max_limit can be MAX_LENGTH=4096 at most. + const __m128i cmpA = _mm_cmpeq_epi32(A0, A1); + const __m128i B0 = + _mm_loadu_si128((const __m128i*)&array1[match_len + 4]); + const __m128i B1 = + _mm_loadu_si128((const __m128i*)&array2[match_len + 4]); + if (_mm_movemask_epi8(cmpA) != 0xffff) break; + match_len += 4; + + { + const __m128i cmpB = _mm_cmpeq_epi32(B0, B1); + A0 = _mm_loadu_si128((const __m128i*)&array1[match_len + 4]); + A1 = _mm_loadu_si128((const __m128i*)&array2[match_len + 4]); + if (_mm_movemask_epi8(cmpB) != 0xffff) break; + match_len += 4; + } + } while (match_len + 12 < length); + } else { + match_len = 0; + // Unroll the potential first two loops. + if (length >= 4 && + _mm_movemask_epi8(_mm_cmpeq_epi32( + _mm_loadu_si128((const __m128i*)&array1[0]), + _mm_loadu_si128((const __m128i*)&array2[0]))) == 0xffff) { + match_len = 4; + if (length >= 8 && + _mm_movemask_epi8(_mm_cmpeq_epi32( + _mm_loadu_si128((const __m128i*)&array1[4]), + _mm_loadu_si128((const __m128i*)&array2[4]))) == 0xffff) { + match_len = 8; + } + } + } + + while (match_len < length && array1[match_len] == array2[match_len]) { + ++match_len; + } + 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((short)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((short)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, NULL, num_pixels - i, out + i); + } + (void)upper; +} + +#define GENERATE_PREDICTOR_1(X, IN) \ + static void PredictorSub##X##_SSE2(const uint32_t* const in, \ + const uint32_t* const upper, \ + int num_pixels, uint32_t* const 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, WEBP_OFFSET_PTR(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_SSE2(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_SSE2(&T, &TL, &pa); // pa = sum |T-TL| + GetSumAbsDiff32_SSE2(&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 + +extern void VP8LEncDspInitSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE2(void) { + VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_SSE2; + VP8LTransformColor = TransformColor_SSE2; + VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_SSE2; + VP8LCollectColorRedTransforms = CollectColorRedTransforms_SSE2; + VP8LAddVector = AddVector_SSE2; + VP8LAddVectorEq = AddVectorEq_SSE2; +#if !defined(DONT_USE_COMBINED_SHANNON_ENTROPY_SSE2_FUNC) + VP8LCombinedShannonEntropy = CombinedShannonEntropy_SSE2; +#endif + VP8LVectorMismatch = VectorMismatch_SSE2; + 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 + +WEBP_DSP_INIT_STUB(VP8LEncDspInitSSE2) + +#endif // WEBP_USE_SSE2 diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_enc_sse41.c b/internal/libwebp-1.2.1/src/dsp/lossless_enc_sse41.c new file mode 100644 index 0000000..ad358a6 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_enc_sse41.c @@ -0,0 +1,155 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE4.1 variant of methods for lossless encoder +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE41) +#include +#include +#include "src/dsp/lossless.h" + +// For sign-extended multiplying constants, pre-shifted by 5: +#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5) + +//------------------------------------------------------------------------------ +// Subtract-Green Transform + +static void SubtractGreenFromBlueAndRed_SSE41(uint32_t* argb_data, + int num_pixels) { + int i; + const __m128i kCstShuffle = _mm_set_epi8(-1, 13, -1, 13, -1, 9, -1, 9, + -1, 5, -1, 5, -1, 1, -1, 1); + for (i = 0; i + 4 <= num_pixels; i += 4) { + const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[i]); + const __m128i in_0g0g = _mm_shuffle_epi8(in, kCstShuffle); + const __m128i out = _mm_sub_epi8(in, in_0g0g); + _mm_storeu_si128((__m128i*)&argb_data[i], out); + } + // fallthrough and finish off with plain-C + if (i != num_pixels) { + VP8LSubtractGreenFromBlueAndRed_C(argb_data + i, num_pixels - i); + } +} + +//------------------------------------------------------------------------------ +// Color Transform + +#define MK_CST_16(HI, LO) \ + _mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff))) + +static void CollectColorBlueTransforms_SSE41(const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_blue, int red_to_blue, + int histo[]) { + const __m128i mult = + MK_CST_16(CST_5b(red_to_blue) + 256,CST_5b(green_to_blue)); + const __m128i perm = + _mm_setr_epi8(-1, 1, -1, 2, -1, 5, -1, 6, -1, 9, -1, 10, -1, 13, -1, 14); + if (tile_width >= 4) { + int y; + for (y = 0; y < tile_height; ++y) { + const uint32_t* const src = argb + y * stride; + const __m128i A1 = _mm_loadu_si128((const __m128i*)src); + const __m128i B1 = _mm_shuffle_epi8(A1, perm); + const __m128i C1 = _mm_mulhi_epi16(B1, mult); + const __m128i D1 = _mm_sub_epi16(A1, C1); + __m128i E = _mm_add_epi16(_mm_srli_epi32(D1, 16), D1); + int x; + for (x = 4; x + 4 <= tile_width; x += 4) { + const __m128i A2 = _mm_loadu_si128((const __m128i*)(src + x)); + __m128i B2, C2, D2; + ++histo[_mm_extract_epi8(E, 0)]; + B2 = _mm_shuffle_epi8(A2, perm); + ++histo[_mm_extract_epi8(E, 4)]; + C2 = _mm_mulhi_epi16(B2, mult); + ++histo[_mm_extract_epi8(E, 8)]; + D2 = _mm_sub_epi16(A2, C2); + ++histo[_mm_extract_epi8(E, 12)]; + E = _mm_add_epi16(_mm_srli_epi32(D2, 16), D2); + } + ++histo[_mm_extract_epi8(E, 0)]; + ++histo[_mm_extract_epi8(E, 4)]; + ++histo[_mm_extract_epi8(E, 8)]; + ++histo[_mm_extract_epi8(E, 12)]; + } + } + { + const int left_over = tile_width & 3; + if (left_over > 0) { + VP8LCollectColorBlueTransforms_C(argb + tile_width - left_over, stride, + left_over, tile_height, + green_to_blue, red_to_blue, histo); + } + } +} + +static void CollectColorRedTransforms_SSE41(const uint32_t* argb, int stride, + int tile_width, int tile_height, + int green_to_red, int histo[]) { + + const __m128i mult = MK_CST_16(0, CST_5b(green_to_red)); + const __m128i mask_g = _mm_set1_epi32(0x0000ff00); + if (tile_width >= 4) { + int y; + for (y = 0; y < tile_height; ++y) { + const uint32_t* const src = argb + y * stride; + const __m128i A1 = _mm_loadu_si128((const __m128i*)src); + const __m128i B1 = _mm_and_si128(A1, mask_g); + const __m128i C1 = _mm_madd_epi16(B1, mult); + __m128i D = _mm_sub_epi16(A1, C1); + int x; + for (x = 4; x + 4 <= tile_width; x += 4) { + const __m128i A2 = _mm_loadu_si128((const __m128i*)(src + x)); + __m128i B2, C2; + ++histo[_mm_extract_epi8(D, 2)]; + B2 = _mm_and_si128(A2, mask_g); + ++histo[_mm_extract_epi8(D, 6)]; + C2 = _mm_madd_epi16(B2, mult); + ++histo[_mm_extract_epi8(D, 10)]; + ++histo[_mm_extract_epi8(D, 14)]; + D = _mm_sub_epi16(A2, C2); + } + ++histo[_mm_extract_epi8(D, 2)]; + ++histo[_mm_extract_epi8(D, 6)]; + ++histo[_mm_extract_epi8(D, 10)]; + ++histo[_mm_extract_epi8(D, 14)]; + } + } + { + const int left_over = tile_width & 3; + if (left_over > 0) { + VP8LCollectColorRedTransforms_C(argb + tile_width - left_over, stride, + left_over, tile_height, green_to_red, + histo); + } + } +} + +#undef MK_CST_16 + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LEncDspInitSSE41(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE41(void) { + VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_SSE41; + VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_SSE41; + VP8LCollectColorRedTransforms = CollectColorRedTransforms_SSE41; +} + +#else // !WEBP_USE_SSE41 + +WEBP_DSP_INIT_STUB(VP8LEncDspInitSSE41) + +#endif // WEBP_USE_SSE41 diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/lossless_mips_dsp_r2.c new file mode 100644 index 0000000..bfe5ea6 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_mips_dsp_r2.c @@ -0,0 +1,701 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Image transforms and color space conversion methods for lossless decoder. +// +// Author(s): Djordje Pesut (djordje.pesut@imgtec.com) +// Jovan Zelincevic (jovan.zelincevic@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) + +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" + +#define MAP_COLOR_FUNCS(FUNC_NAME, TYPE, GET_INDEX, GET_VALUE) \ +static void FUNC_NAME(const TYPE* src, \ + const uint32_t* const color_map, \ + TYPE* dst, int y_start, int y_end, \ + int width) { \ + int y; \ + for (y = y_start; y < y_end; ++y) { \ + int x; \ + for (x = 0; x < (width >> 2); ++x) { \ + int tmp1, tmp2, tmp3, tmp4; \ + __asm__ volatile ( \ + ".ifc " #TYPE ", uint8_t \n\t" \ + "lbu %[tmp1], 0(%[src]) \n\t" \ + "lbu %[tmp2], 1(%[src]) \n\t" \ + "lbu %[tmp3], 2(%[src]) \n\t" \ + "lbu %[tmp4], 3(%[src]) \n\t" \ + "addiu %[src], %[src], 4 \n\t" \ + ".endif \n\t" \ + ".ifc " #TYPE ", uint32_t \n\t" \ + "lw %[tmp1], 0(%[src]) \n\t" \ + "lw %[tmp2], 4(%[src]) \n\t" \ + "lw %[tmp3], 8(%[src]) \n\t" \ + "lw %[tmp4], 12(%[src]) \n\t" \ + "ext %[tmp1], %[tmp1], 8, 8 \n\t" \ + "ext %[tmp2], %[tmp2], 8, 8 \n\t" \ + "ext %[tmp3], %[tmp3], 8, 8 \n\t" \ + "ext %[tmp4], %[tmp4], 8, 8 \n\t" \ + "addiu %[src], %[src], 16 \n\t" \ + ".endif \n\t" \ + "sll %[tmp1], %[tmp1], 2 \n\t" \ + "sll %[tmp2], %[tmp2], 2 \n\t" \ + "sll %[tmp3], %[tmp3], 2 \n\t" \ + "sll %[tmp4], %[tmp4], 2 \n\t" \ + "lwx %[tmp1], %[tmp1](%[color_map]) \n\t" \ + "lwx %[tmp2], %[tmp2](%[color_map]) \n\t" \ + "lwx %[tmp3], %[tmp3](%[color_map]) \n\t" \ + "lwx %[tmp4], %[tmp4](%[color_map]) \n\t" \ + ".ifc " #TYPE ", uint8_t \n\t" \ + "ext %[tmp1], %[tmp1], 8, 8 \n\t" \ + "ext %[tmp2], %[tmp2], 8, 8 \n\t" \ + "ext %[tmp3], %[tmp3], 8, 8 \n\t" \ + "ext %[tmp4], %[tmp4], 8, 8 \n\t" \ + "sb %[tmp1], 0(%[dst]) \n\t" \ + "sb %[tmp2], 1(%[dst]) \n\t" \ + "sb %[tmp3], 2(%[dst]) \n\t" \ + "sb %[tmp4], 3(%[dst]) \n\t" \ + "addiu %[dst], %[dst], 4 \n\t" \ + ".endif \n\t" \ + ".ifc " #TYPE ", uint32_t \n\t" \ + "sw %[tmp1], 0(%[dst]) \n\t" \ + "sw %[tmp2], 4(%[dst]) \n\t" \ + "sw %[tmp3], 8(%[dst]) \n\t" \ + "sw %[tmp4], 12(%[dst]) \n\t" \ + "addiu %[dst], %[dst], 16 \n\t" \ + ".endif \n\t" \ + : [tmp1]"=&r"(tmp1), [tmp2]"=&r"(tmp2), [tmp3]"=&r"(tmp3), \ + [tmp4]"=&r"(tmp4), [src]"+&r"(src), [dst]"+r"(dst) \ + : [color_map]"r"(color_map) \ + : "memory" \ + ); \ + } \ + for (x = 0; x < (width & 3); ++x) { \ + *dst++ = GET_VALUE(color_map[GET_INDEX(*src++)]); \ + } \ + } \ +} + +MAP_COLOR_FUNCS(MapARGB_MIPSdspR2, uint32_t, VP8GetARGBIndex, VP8GetARGBValue) +MAP_COLOR_FUNCS(MapAlpha_MIPSdspR2, uint8_t, VP8GetAlphaIndex, VP8GetAlphaValue) + +#undef MAP_COLOR_FUNCS + +static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1, + uint32_t c2) { + int temp0, temp1, temp2, temp3, temp4, temp5; + __asm__ volatile ( + "preceu.ph.qbr %[temp1], %[c0] \n\t" + "preceu.ph.qbl %[temp2], %[c0] \n\t" + "preceu.ph.qbr %[temp3], %[c1] \n\t" + "preceu.ph.qbl %[temp4], %[c1] \n\t" + "preceu.ph.qbr %[temp5], %[c2] \n\t" + "preceu.ph.qbl %[temp0], %[c2] \n\t" + "subq.ph %[temp3], %[temp3], %[temp5] \n\t" + "subq.ph %[temp4], %[temp4], %[temp0] \n\t" + "addq.ph %[temp1], %[temp1], %[temp3] \n\t" + "addq.ph %[temp2], %[temp2], %[temp4] \n\t" + "shll_s.ph %[temp1], %[temp1], 7 \n\t" + "shll_s.ph %[temp2], %[temp2], 7 \n\t" + "precrqu_s.qb.ph %[temp2], %[temp2], %[temp1] \n\t" + : [temp0]"=r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5) + : [c0]"r"(c0), [c1]"r"(c1), [c2]"r"(c2) + : "memory" + ); + return temp2; +} + +static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1, + uint32_t c2) { + int temp0, temp1, temp2, temp3, temp4, temp5; + __asm__ volatile ( + "adduh.qb %[temp5], %[c0], %[c1] \n\t" + "preceu.ph.qbr %[temp3], %[c2] \n\t" + "preceu.ph.qbr %[temp1], %[temp5] \n\t" + "preceu.ph.qbl %[temp2], %[temp5] \n\t" + "preceu.ph.qbl %[temp4], %[c2] \n\t" + "subq.ph %[temp3], %[temp1], %[temp3] \n\t" + "subq.ph %[temp4], %[temp2], %[temp4] \n\t" + "shrl.ph %[temp5], %[temp3], 15 \n\t" + "shrl.ph %[temp0], %[temp4], 15 \n\t" + "addq.ph %[temp3], %[temp3], %[temp5] \n\t" + "addq.ph %[temp4], %[temp0], %[temp4] \n\t" + "shra.ph %[temp3], %[temp3], 1 \n\t" + "shra.ph %[temp4], %[temp4], 1 \n\t" + "addq.ph %[temp1], %[temp1], %[temp3] \n\t" + "addq.ph %[temp2], %[temp2], %[temp4] \n\t" + "shll_s.ph %[temp1], %[temp1], 7 \n\t" + "shll_s.ph %[temp2], %[temp2], 7 \n\t" + "precrqu_s.qb.ph %[temp1], %[temp2], %[temp1] \n\t" + : [temp0]"=r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=r"(temp4), [temp5]"=&r"(temp5) + : [c0]"r"(c0), [c1]"r"(c1), [c2]"r"(c2) + : "memory" + ); + return temp1; +} + +static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) { + int temp0, temp1, temp2, temp3, temp4, temp5; + __asm__ volatile ( + "cmpgdu.lt.qb %[temp1], %[c], %[b] \n\t" + "pick.qb %[temp1], %[b], %[c] \n\t" + "pick.qb %[temp2], %[c], %[b] \n\t" + "cmpgdu.lt.qb %[temp4], %[c], %[a] \n\t" + "pick.qb %[temp4], %[a], %[c] \n\t" + "pick.qb %[temp5], %[c], %[a] \n\t" + "subu.qb %[temp3], %[temp1], %[temp2] \n\t" + "subu.qb %[temp0], %[temp4], %[temp5] \n\t" + "raddu.w.qb %[temp3], %[temp3] \n\t" + "raddu.w.qb %[temp0], %[temp0] \n\t" + "subu %[temp3], %[temp3], %[temp0] \n\t" + "slti %[temp0], %[temp3], 0x1 \n\t" + "movz %[a], %[b], %[temp0] \n\t" + : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [temp0]"=&r"(temp0), + [a]"+&r"(a) + : [b]"r"(b), [c]"r"(c) + ); + return a; +} + +static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) { + __asm__ volatile ( + "adduh.qb %[a0], %[a0], %[a1] \n\t" + : [a0]"+r"(a0) + : [a1]"r"(a1) + ); + return a0; +} + +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 uint32_t Predictor5_MIPSdspR2(const uint32_t* const left, + const uint32_t* const top) { + return Average3(*left, top[0], top[1]); +} + +static uint32_t Predictor6_MIPSdspR2(const uint32_t* const left, + const uint32_t* const top) { + return Average2(*left, top[-1]); +} + +static uint32_t Predictor7_MIPSdspR2(const uint32_t* const left, + const uint32_t* const top) { + return Average2(*left, top[0]); +} + +static uint32_t Predictor8_MIPSdspR2(const uint32_t* const left, + const uint32_t* const top) { + (void)left; + return Average2(top[-1], top[0]); +} + +static uint32_t Predictor9_MIPSdspR2(const uint32_t* const left, + const uint32_t* const top) { + (void)left; + return Average2(top[0], top[1]); +} + +static uint32_t Predictor10_MIPSdspR2(const uint32_t* const left, + const uint32_t* const top) { + return Average4(*left, top[-1], top[0], top[1]); +} + +static uint32_t Predictor11_MIPSdspR2(const uint32_t* const left, + const uint32_t* const top) { + return Select(top[0], *left, top[-1]); +} + +static uint32_t Predictor12_MIPSdspR2(const uint32_t* const left, + const uint32_t* const top) { + return ClampedAddSubtractFull(*left, top[0], top[-1]); +} + +static uint32_t Predictor13_MIPSdspR2(const uint32_t* const left, + const uint32_t* const top) { + return ClampedAddSubtractHalf(*left, top[0], top[-1]); +} + +// Add green to blue and red channels (i.e. perform the inverse transform of +// 'subtract green'). +static void AddGreenToBlueAndRed_MIPSdspR2(const uint32_t* src, int num_pixels, + uint32_t* dst) { + uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; + 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 %[src], %[p_loop1_end], 3f \n\t" + " nop \n\t" + "0: \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 %[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" + "replv.ph %[temp7], %[temp7] \n\t" + "addu.qb %[temp0], %[temp0], %[temp4] \n\t" + "addu.qb %[temp1], %[temp1], %[temp5] \n\t" + "addu.qb %[temp2], %[temp2], %[temp6] \n\t" + "addu.qb %[temp3], %[temp3], %[temp7] \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 %[src], %[p_loop2_end], 2f \n\t" + " nop \n\t" + "1: \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 %[src], %[p_loop2_end], 1b \n\t" + " sw %[temp0], -4(%[dst]) \n\t" + "2: \n\t" + ".set pop \n\t" + : [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_MIPSdspR2(const VP8LMultipliers* const m, + 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_; + const uint32_t* const p_loop_end = src + (num_pixels & ~1); + __asm__ volatile ( + ".set push \n\t" + ".set noreorder \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" + "replv.ph %[temp2], %[R_to_B] \n\t" + "shll.ph %[temp0], %[temp0], 8 \n\t" + "shll.ph %[temp1], %[temp1], 8 \n\t" + "shll.ph %[temp2], %[temp2], 8 \n\t" + "shra.ph %[temp0], %[temp0], 8 \n\t" + "shra.ph %[temp1], %[temp1], 8 \n\t" + "shra.ph %[temp2], %[temp2], 8 \n\t" + "0: \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" + "shra.ph %[temp3], %[temp3], 8 \n\t" + "mul.ph %[temp5], %[temp3], %[temp0] \n\t" + "mul.ph %[temp3], %[temp3], %[temp1] \n\t" + "precrq.ph.w %[new_red], %[argb], %[argb1] \n\t" + "ins %[argb1], %[argb], 16, 16 \n\t" + "shra.ph %[temp5], %[temp5], 5 \n\t" + "shra.ph %[temp3], %[temp3], 5 \n\t" + "addu.ph %[new_red], %[new_red], %[temp5] \n\t" + "addu.ph %[argb1], %[argb1], %[temp3] \n\t" + "preceu.ph.qbra %[temp5], %[new_red] \n\t" + "shll.ph %[temp4], %[temp5], 8 \n\t" + "shra.ph %[temp4], %[temp4], 8 \n\t" + "mul.ph %[temp4], %[temp4], %[temp2] \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(%[dst]) \n\t" + "sb %[temp3], -4(%[dst]) \n\t" + "sra %[temp3], %[temp3], 16 \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), [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, src, 1, dst); +} + +static void ConvertBGRAToRGB_MIPSdspR2(const uint32_t* src, + int num_pixels, uint8_t* dst) { + int temp0, temp1, temp2, temp3; + 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 %[src], %[p_loop1_end], 3f \n\t" + " nop \n\t" + "0: \n\t" + "lw %[temp3], 12(%[src]) \n\t" + "lw %[temp2], 8(%[src]) \n\t" + "lw %[temp1], 4(%[src]) \n\t" + "lw %[temp0], 0(%[src]) \n\t" + "ins %[temp3], %[temp2], 24, 8 \n\t" + "sll %[temp2], %[temp2], 8 \n\t" + "rotr %[temp3], %[temp3], 16 \n\t" + "ins %[temp2], %[temp1], 0, 16 \n\t" + "sll %[temp1], %[temp1], 8 \n\t" + "wsbh %[temp3], %[temp3] \n\t" + "balign %[temp0], %[temp1], 1 \n\t" + "wsbh %[temp2], %[temp2] \n\t" + "wsbh %[temp0], %[temp0] \n\t" + "usw %[temp3], 8(%[dst]) \n\t" + "rotr %[temp0], %[temp0], 16 \n\t" + "usw %[temp2], 4(%[dst]) \n\t" + "addiu %[src], %[src], 16 \n\t" + "usw %[temp0], 0(%[dst]) \n\t" + "bne %[src], %[p_loop1_end], 0b \n\t" + " addiu %[dst], %[dst], 12 \n\t" + "3: \n\t" + "beq %[src], %[p_loop2_end], 2f \n\t" + " nop \n\t" + "1: \n\t" + "lw %[temp0], 0(%[src]) \n\t" + "addiu %[src], %[src], 4 \n\t" + "wsbh %[temp1], %[temp0] \n\t" + "addiu %[dst], %[dst], 3 \n\t" + "ush %[temp1], -2(%[dst]) \n\t" + "sra %[temp0], %[temp0], 16 \n\t" + "bne %[src], %[p_loop2_end], 1b \n\t" + " sb %[temp0], -3(%[dst]) \n\t" + "2: \n\t" + ".set pop \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [dst]"+&r"(dst), [src]"+&r"(src) + : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end) + : "memory" + ); +} + +static void ConvertBGRAToRGBA_MIPSdspR2(const uint32_t* src, + int num_pixels, uint8_t* dst) { + int temp0, temp1, temp2, temp3; + 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 %[src], %[p_loop1_end], 3f \n\t" + " nop \n\t" + "0: \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" + "wsbh %[temp0], %[temp0] \n\t" + "wsbh %[temp1], %[temp1] \n\t" + "wsbh %[temp2], %[temp2] \n\t" + "wsbh %[temp3], %[temp3] \n\t" + "addiu %[src], %[src], 16 \n\t" + "balign %[temp0], %[temp0], 1 \n\t" + "balign %[temp1], %[temp1], 1 \n\t" + "balign %[temp2], %[temp2], 1 \n\t" + "balign %[temp3], %[temp3], 1 \n\t" + "usw %[temp0], 0(%[dst]) \n\t" + "usw %[temp1], 4(%[dst]) \n\t" + "usw %[temp2], 8(%[dst]) \n\t" + "usw %[temp3], 12(%[dst]) \n\t" + "bne %[src], %[p_loop1_end], 0b \n\t" + " addiu %[dst], %[dst], 16 \n\t" + "3: \n\t" + "beq %[src], %[p_loop2_end], 2f \n\t" + " nop \n\t" + "1: \n\t" + "lw %[temp0], 0(%[src]) \n\t" + "wsbh %[temp0], %[temp0] \n\t" + "addiu %[src], %[src], 4 \n\t" + "balign %[temp0], %[temp0], 1 \n\t" + "usw %[temp0], 0(%[dst]) \n\t" + "bne %[src], %[p_loop2_end], 1b \n\t" + " addiu %[dst], %[dst], 4 \n\t" + "2: \n\t" + ".set pop \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [dst]"+&r"(dst), [src]"+&r"(src) + : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end) + : "memory" + ); +} + +static void ConvertBGRAToRGBA4444_MIPSdspR2(const uint32_t* src, + int num_pixels, uint8_t* dst) { + int temp0, temp1, temp2, temp3, temp4, temp5; + 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 %[src], %[p_loop1_end], 3f \n\t" + " nop \n\t" + "0: \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], 28, 4 \n\t" + "ext %[temp5], %[temp0], 12, 4 \n\t" + "ins %[temp0], %[temp4], 0, 4 \n\t" + "ext %[temp4], %[temp1], 28, 4 \n\t" + "ins %[temp0], %[temp5], 16, 4 \n\t" + "ext %[temp5], %[temp1], 12, 4 \n\t" + "ins %[temp1], %[temp4], 0, 4 \n\t" + "ext %[temp4], %[temp2], 28, 4 \n\t" + "ins %[temp1], %[temp5], 16, 4 \n\t" + "ext %[temp5], %[temp2], 12, 4 \n\t" + "ins %[temp2], %[temp4], 0, 4 \n\t" + "ext %[temp4], %[temp3], 28, 4 \n\t" + "ins %[temp2], %[temp5], 16, 4 \n\t" + "ext %[temp5], %[temp3], 12, 4 \n\t" + "ins %[temp3], %[temp4], 0, 4 \n\t" + "precr.qb.ph %[temp1], %[temp1], %[temp0] \n\t" + "ins %[temp3], %[temp5], 16, 4 \n\t" + "addiu %[src], %[src], 16 \n\t" + "precr.qb.ph %[temp3], %[temp3], %[temp2] \n\t" +#if (WEBP_SWAP_16BIT_CSP == 1) + "usw %[temp1], 0(%[dst]) \n\t" + "usw %[temp3], 4(%[dst]) \n\t" +#else + "wsbh %[temp1], %[temp1] \n\t" + "wsbh %[temp3], %[temp3] \n\t" + "usw %[temp1], 0(%[dst]) \n\t" + "usw %[temp3], 4(%[dst]) \n\t" +#endif + "bne %[src], %[p_loop1_end], 0b \n\t" + " addiu %[dst], %[dst], 8 \n\t" + "3: \n\t" + "beq %[src], %[p_loop2_end], 2f \n\t" + " nop \n\t" + "1: \n\t" + "lw %[temp0], 0(%[src]) \n\t" + "ext %[temp4], %[temp0], 28, 4 \n\t" + "ext %[temp5], %[temp0], 12, 4 \n\t" + "ins %[temp0], %[temp4], 0, 4 \n\t" + "ins %[temp0], %[temp5], 16, 4 \n\t" + "addiu %[src], %[src], 4 \n\t" + "precr.qb.ph %[temp0], %[temp0], %[temp0] \n\t" +#if (WEBP_SWAP_16BIT_CSP == 1) + "ush %[temp0], 0(%[dst]) \n\t" +#else + "wsbh %[temp0], %[temp0] \n\t" + "ush %[temp0], 0(%[dst]) \n\t" +#endif + "bne %[src], %[p_loop2_end], 1b \n\t" + " addiu %[dst], %[dst], 2 \n\t" + "2: \n\t" + ".set pop \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [dst]"+&r"(dst), [src]"+&r"(src) + : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end) + : "memory" + ); +} + +static void ConvertBGRAToRGB565_MIPSdspR2(const uint32_t* src, + int num_pixels, uint8_t* dst) { + int temp0, temp1, temp2, temp3, temp4, temp5; + 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 %[src], %[p_loop1_end], 3f \n\t" + " nop \n\t" + "0: \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, 16 \n\t" + "ext %[temp5], %[temp0], 5, 11 \n\t" + "ext %[temp0], %[temp0], 3, 5 \n\t" + "ins %[temp4], %[temp5], 0, 11 \n\t" + "ext %[temp5], %[temp1], 5, 11 \n\t" + "ins %[temp4], %[temp0], 0, 5 \n\t" + "ext %[temp0], %[temp1], 8, 16 \n\t" + "ext %[temp1], %[temp1], 3, 5 \n\t" + "ins %[temp0], %[temp5], 0, 11 \n\t" + "ext %[temp5], %[temp2], 5, 11 \n\t" + "ins %[temp0], %[temp1], 0, 5 \n\t" + "ext %[temp1], %[temp2], 8, 16 \n\t" + "ext %[temp2], %[temp2], 3, 5 \n\t" + "ins %[temp1], %[temp5], 0, 11 \n\t" + "ext %[temp5], %[temp3], 5, 11 \n\t" + "ins %[temp1], %[temp2], 0, 5 \n\t" + "ext %[temp2], %[temp3], 8, 16 \n\t" + "ext %[temp3], %[temp3], 3, 5 \n\t" + "ins %[temp2], %[temp5], 0, 11 \n\t" + "append %[temp0], %[temp4], 16 \n\t" + "ins %[temp2], %[temp3], 0, 5 \n\t" + "addiu %[src], %[src], 16 \n\t" + "append %[temp2], %[temp1], 16 \n\t" +#if (WEBP_SWAP_16BIT_CSP == 1) + "usw %[temp0], 0(%[dst]) \n\t" + "usw %[temp2], 4(%[dst]) \n\t" +#else + "wsbh %[temp0], %[temp0] \n\t" + "wsbh %[temp2], %[temp2] \n\t" + "usw %[temp0], 0(%[dst]) \n\t" + "usw %[temp2], 4(%[dst]) \n\t" +#endif + "bne %[src], %[p_loop1_end], 0b \n\t" + " addiu %[dst], %[dst], 8 \n\t" + "3: \n\t" + "beq %[src], %[p_loop2_end], 2f \n\t" + " nop \n\t" + "1: \n\t" + "lw %[temp0], 0(%[src]) \n\t" + "ext %[temp4], %[temp0], 8, 16 \n\t" + "ext %[temp5], %[temp0], 5, 11 \n\t" + "ext %[temp0], %[temp0], 3, 5 \n\t" + "ins %[temp4], %[temp5], 0, 11 \n\t" + "addiu %[src], %[src], 4 \n\t" + "ins %[temp4], %[temp0], 0, 5 \n\t" +#if (WEBP_SWAP_16BIT_CSP == 1) + "ush %[temp4], 0(%[dst]) \n\t" +#else + "wsbh %[temp4], %[temp4] \n\t" + "ush %[temp4], 0(%[dst]) \n\t" +#endif + "bne %[src], %[p_loop2_end], 1b \n\t" + " addiu %[dst], %[dst], 2 \n\t" + "2: \n\t" + ".set pop \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), + [dst]"+&r"(dst), [src]"+&r"(src) + : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end) + : "memory" + ); +} + +static void ConvertBGRAToBGR_MIPSdspR2(const uint32_t* src, + int num_pixels, uint8_t* dst) { + int temp0, temp1, temp2, temp3; + 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 %[src], %[p_loop1_end], 3f \n\t" + " nop \n\t" + "0: \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" + "ins %[temp0], %[temp1], 24, 8 \n\t" + "sra %[temp1], %[temp1], 8 \n\t" + "ins %[temp1], %[temp2], 16, 16 \n\t" + "sll %[temp2], %[temp2], 8 \n\t" + "balign %[temp3], %[temp2], 1 \n\t" + "addiu %[src], %[src], 16 \n\t" + "usw %[temp0], 0(%[dst]) \n\t" + "usw %[temp1], 4(%[dst]) \n\t" + "usw %[temp3], 8(%[dst]) \n\t" + "bne %[src], %[p_loop1_end], 0b \n\t" + " addiu %[dst], %[dst], 12 \n\t" + "3: \n\t" + "beq %[src], %[p_loop2_end], 2f \n\t" + " nop \n\t" + "1: \n\t" + "lw %[temp0], 0(%[src]) \n\t" + "addiu %[src], %[src], 4 \n\t" + "addiu %[dst], %[dst], 3 \n\t" + "ush %[temp0], -3(%[dst]) \n\t" + "sra %[temp0], %[temp0], 16 \n\t" + "bne %[src], %[p_loop2_end], 1b \n\t" + " sb %[temp0], -1(%[dst]) \n\t" + "2: \n\t" + ".set pop \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), + [temp3]"=&r"(temp3), [dst]"+&r"(dst), [src]"+&r"(src) + : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end) + : "memory" + ); +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LDspInitMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitMIPSdspR2(void) { + VP8LMapColor32b = MapARGB_MIPSdspR2; + VP8LMapColor8b = MapAlpha_MIPSdspR2; + + VP8LPredictors[5] = Predictor5_MIPSdspR2; + VP8LPredictors[6] = Predictor6_MIPSdspR2; + VP8LPredictors[7] = Predictor7_MIPSdspR2; + VP8LPredictors[8] = Predictor8_MIPSdspR2; + VP8LPredictors[9] = Predictor9_MIPSdspR2; + VP8LPredictors[10] = Predictor10_MIPSdspR2; + VP8LPredictors[11] = Predictor11_MIPSdspR2; + VP8LPredictors[12] = Predictor12_MIPSdspR2; + VP8LPredictors[13] = Predictor13_MIPSdspR2; + + VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed_MIPSdspR2; + VP8LTransformColorInverse = TransformColorInverse_MIPSdspR2; + + VP8LConvertBGRAToRGB = ConvertBGRAToRGB_MIPSdspR2; + VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA_MIPSdspR2; + VP8LConvertBGRAToRGBA4444 = ConvertBGRAToRGBA4444_MIPSdspR2; + VP8LConvertBGRAToRGB565 = ConvertBGRAToRGB565_MIPSdspR2; + VP8LConvertBGRAToBGR = ConvertBGRAToBGR_MIPSdspR2; +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(VP8LDspInitMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_msa.c b/internal/libwebp-1.2.1/src/dsp/lossless_msa.c new file mode 100644 index 0000000..9f54720 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_msa.c @@ -0,0 +1,356 @@ +// 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 "src/dsp/dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "src/dsp/lossless.h" +#include "src/dsp/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 = { 0 }, 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_MSA(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_MSA(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_MSA(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_MSA(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_MSA(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_MSA; + VP8LConvertBGRAToBGR = ConvertBGRAToBGR_MSA; + VP8LConvertBGRAToRGB = ConvertBGRAToRGB_MSA; + + VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed_MSA; + VP8LTransformColorInverse = TransformColorInverse_MSA; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(VP8LDspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_neon.c b/internal/libwebp-1.2.1/src/dsp/lossless_neon.c new file mode 100644 index 0000000..89e3e01 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_neon.c @@ -0,0 +1,645 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// NEON variant of methods for lossless decoder +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_NEON) + +#include + +#include "src/dsp/lossless.h" +#include "src/dsp/neon.h" + +//------------------------------------------------------------------------------ +// Colorspace conversion functions + +#if !defined(WORK_AROUND_GCC) +// gcc 4.6.0 had some trouble (NDK-r9) with this code. We only use it for +// gcc-4.8.x at least. +static void ConvertBGRAToRGBA_NEON(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const end = src + (num_pixels & ~15); + for (; src < end; src += 16) { + uint8x16x4_t pixel = vld4q_u8((uint8_t*)src); + // swap B and R. (VSWP d0,d2 has no intrinsics equivalent!) + const uint8x16_t tmp = pixel.val[0]; + pixel.val[0] = pixel.val[2]; + pixel.val[2] = tmp; + vst4q_u8(dst, pixel); + dst += 64; + } + VP8LConvertBGRAToRGBA_C(src, num_pixels & 15, dst); // left-overs +} + +static void ConvertBGRAToBGR_NEON(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const end = src + (num_pixels & ~15); + for (; src < end; src += 16) { + const uint8x16x4_t pixel = vld4q_u8((uint8_t*)src); + const uint8x16x3_t tmp = { { pixel.val[0], pixel.val[1], pixel.val[2] } }; + vst3q_u8(dst, tmp); + dst += 48; + } + VP8LConvertBGRAToBGR_C(src, num_pixels & 15, dst); // left-overs +} + +static void ConvertBGRAToRGB_NEON(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const end = src + (num_pixels & ~15); + for (; src < end; src += 16) { + const uint8x16x4_t pixel = vld4q_u8((uint8_t*)src); + const uint8x16x3_t tmp = { { pixel.val[2], pixel.val[1], pixel.val[0] } }; + vst3q_u8(dst, tmp); + dst += 48; + } + VP8LConvertBGRAToRGB_C(src, num_pixels & 15, dst); // left-overs +} + +#else // WORK_AROUND_GCC + +// gcc-4.6.0 fallback + +static const uint8_t kRGBAShuffle[8] = { 2, 1, 0, 3, 6, 5, 4, 7 }; + +static void ConvertBGRAToRGBA_NEON(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const end = src + (num_pixels & ~1); + const uint8x8_t shuffle = vld1_u8(kRGBAShuffle); + for (; src < end; src += 2) { + const uint8x8_t pixels = vld1_u8((uint8_t*)src); + vst1_u8(dst, vtbl1_u8(pixels, shuffle)); + dst += 8; + } + VP8LConvertBGRAToRGBA_C(src, num_pixels & 1, dst); // left-overs +} + +static const uint8_t kBGRShuffle[3][8] = { + { 0, 1, 2, 4, 5, 6, 8, 9 }, + { 10, 12, 13, 14, 16, 17, 18, 20 }, + { 21, 22, 24, 25, 26, 28, 29, 30 } +}; + +static void ConvertBGRAToBGR_NEON(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const end = src + (num_pixels & ~7); + const uint8x8_t shuffle0 = vld1_u8(kBGRShuffle[0]); + const uint8x8_t shuffle1 = vld1_u8(kBGRShuffle[1]); + const uint8x8_t shuffle2 = vld1_u8(kBGRShuffle[2]); + for (; src < end; src += 8) { + uint8x8x4_t pixels; + INIT_VECTOR4(pixels, + vld1_u8((const uint8_t*)(src + 0)), + vld1_u8((const uint8_t*)(src + 2)), + vld1_u8((const uint8_t*)(src + 4)), + vld1_u8((const uint8_t*)(src + 6))); + vst1_u8(dst + 0, vtbl4_u8(pixels, shuffle0)); + vst1_u8(dst + 8, vtbl4_u8(pixels, shuffle1)); + vst1_u8(dst + 16, vtbl4_u8(pixels, shuffle2)); + dst += 8 * 3; + } + VP8LConvertBGRAToBGR_C(src, num_pixels & 7, dst); // left-overs +} + +static const uint8_t kRGBShuffle[3][8] = { + { 2, 1, 0, 6, 5, 4, 10, 9 }, + { 8, 14, 13, 12, 18, 17, 16, 22 }, + { 21, 20, 26, 25, 24, 30, 29, 28 } +}; + +static void ConvertBGRAToRGB_NEON(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const uint32_t* const end = src + (num_pixels & ~7); + const uint8x8_t shuffle0 = vld1_u8(kRGBShuffle[0]); + const uint8x8_t shuffle1 = vld1_u8(kRGBShuffle[1]); + const uint8x8_t shuffle2 = vld1_u8(kRGBShuffle[2]); + for (; src < end; src += 8) { + uint8x8x4_t pixels; + INIT_VECTOR4(pixels, + vld1_u8((const uint8_t*)(src + 0)), + vld1_u8((const uint8_t*)(src + 2)), + vld1_u8((const uint8_t*)(src + 4)), + vld1_u8((const uint8_t*)(src + 6))); + vst1_u8(dst + 0, vtbl4_u8(pixels, shuffle0)); + vst1_u8(dst + 8, vtbl4_u8(pixels, shuffle1)); + vst1_u8(dst + 16, vtbl4_u8(pixels, shuffle2)); + dst += 8 * 3; + } + VP8LConvertBGRAToRGB_C(src, num_pixels & 7, dst); // left-overs +} + +#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(const uint32_t* const left, + const uint32_t* const top) { + return Average3_NEON(*left, top[0], top[1]); +} +static uint32_t Predictor6_NEON(const uint32_t* const left, + const uint32_t* const top) { + return Average2_NEON(*left, top[-1]); +} +static uint32_t Predictor7_NEON(const uint32_t* const left, + const uint32_t* const top) { + return Average2_NEON(*left, top[0]); +} +static uint32_t Predictor13_NEON(const uint32_t* const 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 + +// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use +// non-standard versions there. +#if defined(__APPLE__) && defined(__aarch64__) && \ + defined(__apple_build_version__) && (__apple_build_version__< 6020037) +#define USE_VTBLQ +#endif + +#ifdef USE_VTBLQ +// 255 = byte will be zeroed +static const uint8_t kGreenShuffle[16] = { + 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255 +}; + +static WEBP_INLINE uint8x16_t DoGreenShuffle_NEON(const uint8x16_t argb, + const uint8x16_t shuffle) { + return vcombine_u8(vtbl1q_u8(argb, vget_low_u8(shuffle)), + vtbl1q_u8(argb, vget_high_u8(shuffle))); +} +#else // !USE_VTBLQ +// 255 = byte will be zeroed +static const uint8_t kGreenShuffle[8] = { 1, 255, 1, 255, 5, 255, 5, 255 }; + +static WEBP_INLINE uint8x16_t DoGreenShuffle_NEON(const uint8x16_t argb, + const uint8x8_t shuffle) { + return vcombine_u8(vtbl1_u8(vget_low_u8(argb), shuffle), + vtbl1_u8(vget_high_u8(argb), shuffle)); +} +#endif // USE_VTBLQ + +static void AddGreenToBlueAndRed_NEON(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 (; src < end; src += 4, dst += 4) { + const uint8x16_t argb = vld1q_u8((const uint8_t*)src); + const uint8x16_t greens = DoGreenShuffle_NEON(argb, shuffle); + vst1q_u8((uint8_t*)dst, vaddq_u8(argb, greens)); + } + // fallthrough and finish off with plain-C + VP8LAddGreenToBlueAndRed_C(src, num_pixels & 3, dst); +} + +//------------------------------------------------------------------------------ +// Color Transform + +static void TransformColorInverse_NEON(const VP8LMultipliers* const m, + 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_), + CST(green_to_blue_), CST(green_to_red_), + CST(green_to_blue_), CST(green_to_red_), + CST(green_to_blue_), CST(green_to_red_) + }; + const int16x8_t mults_rb = vld1q_s16(rb); + const int16_t b2[8] = { + 0, CST(red_to_blue_), 0, CST(red_to_blue_), + 0, CST(red_to_blue_), 0, CST(red_to_blue_), + }; + const int16x8_t mults_b2 = vld1q_s16(b2); +#undef CST +#ifdef USE_VTBLQ + static const uint8_t kg0g0[16] = { + 255, 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13 + }; + const uint8x16_t shuffle = vld1q_u8(kg0g0); +#else + static const uint8_t k0g0g[8] = { 255, 1, 255, 1, 255, 5, 255, 5 }; + const uint8x8_t shuffle = vld1_u8(k0g0g); +#endif + 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((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_NEON(in, shuffle); + // x dr x db1 + const int16x8_t A = vqdmulhq_s16(vreinterpretq_s16_u8(greens), mults_rb); + // x r' x b' + const int8x16_t B = vaddq_s8(vreinterpretq_s8_u8(in), + vreinterpretq_s8_s16(A)); + // r' 0 b' 0 + const int16x8_t C = vshlq_n_s16(vreinterpretq_s16_s8(B), 8); + // x db2 0 0 + const int16x8_t D = vqdmulhq_s16(C, mults_b2); + // 0 x db2 0 + const uint32x4_t E = vshrq_n_u32(vreinterpretq_u32_s16(D), 8); + // r' x b'' 0 + const int8x16_t F = vaddq_s8(vreinterpretq_s8_u32(E), + vreinterpretq_s8_s16(C)); + // 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(dst + i, out); + } + // Fall-back to C-version for left-overs. + VP8LTransformColorInverse_C(m, src + i, num_pixels - i, dst + i); +} + +#undef USE_VTBLQ + +//------------------------------------------------------------------------------ +// Entry point + +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_NEON; + VP8LConvertBGRAToBGR = ConvertBGRAToBGR_NEON; + VP8LConvertBGRAToRGB = ConvertBGRAToRGB_NEON; + + VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed_NEON; + VP8LTransformColorInverse = TransformColorInverse_NEON; +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(VP8LDspInitNEON) + +#endif // WEBP_USE_NEON diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_sse2.c b/internal/libwebp-1.2.1/src/dsp/lossless_sse2.c new file mode 100644 index 0000000..396cb0b --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_sse2.c @@ -0,0 +1,716 @@ +// 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 variant of methods for lossless decoder +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) + +#include "src/dsp/common_sse2.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" +#include + +//------------------------------------------------------------------------------ +// Predictor Transform + +static WEBP_INLINE uint32_t ClampedAddSubtractFull_SSE2(uint32_t c0, + uint32_t c1, + uint32_t c2) { + const __m128i zero = _mm_setzero_si128(); + const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c0), zero); + const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c1), zero); + const __m128i C2 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero); + const __m128i V1 = _mm_add_epi16(C0, C1); + const __m128i V2 = _mm_sub_epi16(V1, C2); + const __m128i b = _mm_packus_epi16(V2, V2); + const uint32_t output = _mm_cvtsi128_si32(b); + return output; +} + +static WEBP_INLINE uint32_t ClampedAddSubtractHalf_SSE2(uint32_t c0, + uint32_t c1, + uint32_t c2) { + const __m128i zero = _mm_setzero_si128(); + const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c0), zero); + const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c1), zero); + const __m128i B0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero); + const __m128i avg = _mm_add_epi16(C1, C0); + const __m128i A0 = _mm_srli_epi16(avg, 1); + const __m128i A1 = _mm_sub_epi16(A0, B0); + const __m128i BgtA = _mm_cmpgt_epi16(B0, A0); + const __m128i A2 = _mm_sub_epi16(A1, BgtA); + const __m128i A3 = _mm_srai_epi16(A2, 1); + const __m128i A4 = _mm_add_epi16(A0, A3); + const __m128i A5 = _mm_packus_epi16(A4, A4); + const uint32_t output = _mm_cvtsi128_si32(A5); + return output; +} + +static WEBP_INLINE uint32_t Select_SSE2(uint32_t a, uint32_t b, uint32_t c) { + int pa_minus_pb; + const __m128i zero = _mm_setzero_si128(); + const __m128i A0 = _mm_cvtsi32_si128(a); + const __m128i B0 = _mm_cvtsi32_si128(b); + const __m128i C0 = _mm_cvtsi32_si128(c); + const __m128i AC0 = _mm_subs_epu8(A0, C0); + const __m128i CA0 = _mm_subs_epu8(C0, A0); + const __m128i BC0 = _mm_subs_epu8(B0, C0); + const __m128i CB0 = _mm_subs_epu8(C0, B0); + const __m128i AC = _mm_or_si128(AC0, CA0); + const __m128i BC = _mm_or_si128(BC0, CB0); + const __m128i pa = _mm_unpacklo_epi8(AC, zero); // |a - c| + const __m128i pb = _mm_unpacklo_epi8(BC, zero); // |b - c| + const __m128i diff = _mm_sub_epi16(pb, pa); + { + int16_t out[8]; + _mm_storeu_si128((__m128i*)out, diff); + pa_minus_pb = out[0] + out[1] + out[2] + out[3]; + } + return (pa_minus_pb <= 0) ? a : b; +} + +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_SSE2(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_SSE2(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); + return _mm_srli_epi16(sum, 1); +} + +static WEBP_INLINE uint32_t Average2_SSE2(uint32_t a0, uint32_t a1) { + __m128i output; + Average2_uint32_SSE2(a0, a1, &output); + return _mm_cvtsi128_si32(output); +} + +static WEBP_INLINE uint32_t Average3_SSE2(uint32_t a0, uint32_t a1, + uint32_t a2) { + const __m128i zero = _mm_setzero_si128(); + const __m128i avg1 = Average2_uint32_16_SSE2(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); + const __m128i A2 = _mm_packus_epi16(avg2, avg2); + const uint32_t output = _mm_cvtsi128_si32(A2); + return output; +} + +static WEBP_INLINE uint32_t Average4_SSE2(uint32_t a0, uint32_t a1, + uint32_t a2, uint32_t a3) { + const __m128i avg1 = Average2_uint32_16_SSE2(a0, a1); + const __m128i avg2 = Average2_uint32_16_SSE2(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); + const uint32_t output = _mm_cvtsi128_si32(A0); + return output; +} + +static uint32_t Predictor5_SSE2(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average3_SSE2(*left, top[0], top[1]); + return pred; +} +static uint32_t Predictor6_SSE2(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average2_SSE2(*left, top[-1]); + return pred; +} +static uint32_t Predictor7_SSE2(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average2_SSE2(*left, top[0]); + return pred; +} +static uint32_t Predictor8_SSE2(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average2_SSE2(top[-1], top[0]); + (void)left; + return pred; +} +static uint32_t Predictor9_SSE2(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average2_SSE2(top[0], top[1]); + (void)left; + return pred; +} +static uint32_t Predictor10_SSE2(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Average4_SSE2(*left, top[-1], top[0], top[1]); + return pred; +} +static uint32_t Predictor11_SSE2(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = Select_SSE2(top[0], *left, top[-1]); + return pred; +} +static uint32_t Predictor12_SSE2(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = ClampedAddSubtractFull_SSE2(*left, top[0], top[-1]); + return pred; +} +static uint32_t Predictor13_SSE2(const uint32_t* const left, + const uint32_t* const top) { + const uint32_t pred = ClampedAddSubtractHalf_SSE2(*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, NULL, num_pixels - i, out + i); + } + (void)upper; +} + +// 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)). +#define DO_PRED10(OUT) do { \ + __m128i avgLTL, avg; \ + Average2_m128i(&L, &TL, &avgLTL); \ + Average2_m128i(&avgTTR, &avgLTL, &avg); \ + L = _mm_add_epi8(avg, src); \ + out[i + (OUT)] = _mm_cvtsi128_si32(L); \ +} while (0) + +#define DO_PRED10_SHIFT do { \ + /* 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); \ +} while (0) + +static void PredictorAdd10_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + __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); + DO_PRED10(0); + DO_PRED10_SHIFT; + DO_PRED10(1); + DO_PRED10_SHIFT; + DO_PRED10(2); + DO_PRED10_SHIFT; + DO_PRED10(3); + } + if (i != num_pixels) { + VP8LPredictorsAdd_C[10](in + i, upper + i, num_pixels - i, out + i); + } +} +#undef DO_PRED10 +#undef DO_PRED10_SHIFT + +// Predictor11: select. +#define DO_PRED11(OUT) do { \ + const __m128i L_lo = _mm_unpacklo_epi32(L, T); \ + const __m128i TL_lo = _mm_unpacklo_epi32(TL, T); \ + 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 = (pa > b)? L : T*/ \ + L = _mm_add_epi8(src, pred); \ + out[i + (OUT)] = _mm_cvtsi128_si32(L); \ +} while (0) + +#define DO_PRED11_SHIFT do { \ + /* 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); \ +} while (0) + +static void PredictorAdd11_SSE2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* out) { + int i; + __m128i pa; + __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]); + { + // We can unpack with any value on the upper 32 bits, provided it's the + // same on both operands (so that their sum of abs diff is zero). Here we + // use T. + const __m128i T_lo = _mm_unpacklo_epi32(T, T); + const __m128i TL_lo = _mm_unpacklo_epi32(TL, T); + const __m128i T_hi = _mm_unpackhi_epi32(T, T); + const __m128i TL_hi = _mm_unpackhi_epi32(TL, T); + const __m128i s_lo = _mm_sad_epu8(T_lo, TL_lo); + const __m128i s_hi = _mm_sad_epu8(T_hi, TL_hi); + pa = _mm_packs_epi32(s_lo, s_hi); // pa = sum |T-TL| + } + DO_PRED11(0); + DO_PRED11_SHIFT; + DO_PRED11(1); + DO_PRED11_SHIFT; + DO_PRED11(2); + DO_PRED11_SHIFT; + DO_PRED11(3); + } + if (i != num_pixels) { + VP8LPredictorsAdd_C[11](in + i, upper + i, num_pixels - i, out + i); + } +} +#undef DO_PRED11 +#undef DO_PRED11_SHIFT + +// 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); \ +} while (0) + +#define DO_PRED12_SHIFT(DIFF, LANE) do { \ + /* 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_SHIFT(diff_lo, 0); + DO_PRED12(diff_lo, 1, 1); + DO_PRED12_SHIFT(diff_lo, 1); + DO_PRED12(diff_hi, 0, 2); + DO_PRED12_SHIFT(diff_hi, 0); + 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 +#undef DO_PRED12_SHIFT + +// 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_SSE2(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((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*)&dst[i], out); + } + // fallthrough and finish off with plain-C + if (i != num_pixels) { + VP8LAddGreenToBlueAndRed_C(src + i, num_pixels - i, dst + i); + } +} + +//------------------------------------------------------------------------------ +// Color Transform + +static void TransformColorInverse_SSE2(const VP8LMultipliers* const m, + 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 +#define MK_CST_16(HI, LO) \ + _mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff))) + const __m128i mults_rb = MK_CST_16(CST(green_to_red_), CST(green_to_blue_)); + const __m128i mults_b2 = MK_CST_16(CST(red_to_blue_), 0); +#undef MK_CST_16 +#undef CST + 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((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 + const __m128i D = _mm_mulhi_epi16(C, mults_rb); // x dr x db1 + const __m128i E = _mm_add_epi8(in, D); // x r' x b' + const __m128i F = _mm_slli_epi16(E, 8); // r' 0 b' 0 + const __m128i G = _mm_mulhi_epi16(F, mults_b2); // x db2 0 0 + const __m128i H = _mm_srli_epi32(G, 8); // 0 x db2 0 + 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*)&dst[i], out); + } + // Fall-back to C-version for left-overs. + if (i != num_pixels) { + VP8LTransformColorInverse_C(m, src + i, num_pixels - i, dst + i); + } +} + +//------------------------------------------------------------------------------ +// Color-space conversion functions + +static void ConvertBGRAToRGB_SSE2(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_SSE2(&in0, &in1, &in2, &in3); + VP8L32bToPlanar_SSE2(&in4, &in5, &in6, &in7); + // At this points, in1/in5 contains red only, in2/in6 green only ... + // Pack the colors in 24b RGB. + VP8PlanarTo24b_SSE2(&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_SSE2(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const __m128i red_blue_mask = _mm_set1_epi32(0x00ff00ffu); + const __m128i* in = (const __m128i*)src; + __m128i* out = (__m128i*)dst; + while (num_pixels >= 8) { + const __m128i A1 = _mm_loadu_si128(in++); + const __m128i A2 = _mm_loadu_si128(in++); + const __m128i B1 = _mm_and_si128(A1, red_blue_mask); // R 0 B 0 + const __m128i B2 = _mm_and_si128(A2, red_blue_mask); // R 0 B 0 + const __m128i C1 = _mm_andnot_si128(red_blue_mask, A1); // 0 G 0 A + const __m128i C2 = _mm_andnot_si128(red_blue_mask, A2); // 0 G 0 A + const __m128i D1 = _mm_shufflelo_epi16(B1, _MM_SHUFFLE(2, 3, 0, 1)); + const __m128i D2 = _mm_shufflelo_epi16(B2, _MM_SHUFFLE(2, 3, 0, 1)); + const __m128i E1 = _mm_shufflehi_epi16(D1, _MM_SHUFFLE(2, 3, 0, 1)); + const __m128i E2 = _mm_shufflehi_epi16(D2, _MM_SHUFFLE(2, 3, 0, 1)); + const __m128i F1 = _mm_or_si128(E1, C1); + const __m128i F2 = _mm_or_si128(E2, C2); + _mm_storeu_si128(out++, F1); + _mm_storeu_si128(out++, F2); + num_pixels -= 8; + } + // left-overs + if (num_pixels > 0) { + VP8LConvertBGRAToRGBA_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + } +} + +static void ConvertBGRAToRGBA4444_SSE2(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const __m128i mask_0x0f = _mm_set1_epi8(0x0f); + const __m128i mask_0xf0 = _mm_set1_epi8(0xf0); + const __m128i* in = (const __m128i*)src; + __m128i* out = (__m128i*)dst; + while (num_pixels >= 8) { + const __m128i bgra0 = _mm_loadu_si128(in++); // bgra0|bgra1|bgra2|bgra3 + const __m128i bgra4 = _mm_loadu_si128(in++); // bgra4|bgra5|bgra6|bgra7 + const __m128i v0l = _mm_unpacklo_epi8(bgra0, bgra4); // b0b4g0g4r0r4a0a4... + const __m128i v0h = _mm_unpackhi_epi8(bgra0, bgra4); // b2b6g2g6r2r6a2a6... + const __m128i v1l = _mm_unpacklo_epi8(v0l, v0h); // b0b2b4b6g0g2g4g6... + const __m128i v1h = _mm_unpackhi_epi8(v0l, v0h); // b1b3b5b7g1g3g5g7... + const __m128i v2l = _mm_unpacklo_epi8(v1l, v1h); // b0...b7 | g0...g7 + const __m128i v2h = _mm_unpackhi_epi8(v1l, v1h); // r0...r7 | a0...a7 + const __m128i ga0 = _mm_unpackhi_epi64(v2l, v2h); // g0...g7 | a0...a7 + const __m128i rb0 = _mm_unpacklo_epi64(v2h, v2l); // r0...r7 | b0...b7 + const __m128i ga1 = _mm_srli_epi16(ga0, 4); // g0-|g1-|...|a6-|a7- + const __m128i rb1 = _mm_and_si128(rb0, mask_0xf0); // -r0|-r1|...|-b6|-a7 + const __m128i ga2 = _mm_and_si128(ga1, mask_0x0f); // g0-|g1-|...|a6-|a7- + const __m128i rgba0 = _mm_or_si128(ga2, rb1); // rg0..rg7 | ba0..ba7 + const __m128i rgba1 = _mm_srli_si128(rgba0, 8); // ba0..ba7 | 0 +#if (WEBP_SWAP_16BIT_CSP == 1) + const __m128i rgba = _mm_unpacklo_epi8(rgba1, rgba0); // barg0...barg7 +#else + const __m128i rgba = _mm_unpacklo_epi8(rgba0, rgba1); // rgba0...rgba7 +#endif + _mm_storeu_si128(out++, rgba); + num_pixels -= 8; + } + // left-overs + if (num_pixels > 0) { + VP8LConvertBGRAToRGBA4444_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + } +} + +static void ConvertBGRAToRGB565_SSE2(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const __m128i mask_0xe0 = _mm_set1_epi8(0xe0); + const __m128i mask_0xf8 = _mm_set1_epi8(0xf8); + const __m128i mask_0x07 = _mm_set1_epi8(0x07); + const __m128i* in = (const __m128i*)src; + __m128i* out = (__m128i*)dst; + while (num_pixels >= 8) { + const __m128i bgra0 = _mm_loadu_si128(in++); // bgra0|bgra1|bgra2|bgra3 + const __m128i bgra4 = _mm_loadu_si128(in++); // bgra4|bgra5|bgra6|bgra7 + const __m128i v0l = _mm_unpacklo_epi8(bgra0, bgra4); // b0b4g0g4r0r4a0a4... + const __m128i v0h = _mm_unpackhi_epi8(bgra0, bgra4); // b2b6g2g6r2r6a2a6... + const __m128i v1l = _mm_unpacklo_epi8(v0l, v0h); // b0b2b4b6g0g2g4g6... + const __m128i v1h = _mm_unpackhi_epi8(v0l, v0h); // b1b3b5b7g1g3g5g7... + const __m128i v2l = _mm_unpacklo_epi8(v1l, v1h); // b0...b7 | g0...g7 + const __m128i v2h = _mm_unpackhi_epi8(v1l, v1h); // r0...r7 | a0...a7 + const __m128i ga0 = _mm_unpackhi_epi64(v2l, v2h); // g0...g7 | a0...a7 + const __m128i rb0 = _mm_unpacklo_epi64(v2h, v2l); // r0...r7 | b0...b7 + const __m128i rb1 = _mm_and_si128(rb0, mask_0xf8); // -r0..-r7|-b0..-b7 + const __m128i g_lo1 = _mm_srli_epi16(ga0, 5); + const __m128i g_lo2 = _mm_and_si128(g_lo1, mask_0x07); // g0-...g7-|xx (3b) + const __m128i g_hi1 = _mm_slli_epi16(ga0, 3); + const __m128i g_hi2 = _mm_and_si128(g_hi1, mask_0xe0); // -g0...-g7|xx (3b) + const __m128i b0 = _mm_srli_si128(rb1, 8); // -b0...-b7|0 + const __m128i rg1 = _mm_or_si128(rb1, g_lo2); // gr0...gr7|xx + const __m128i b1 = _mm_srli_epi16(b0, 3); + const __m128i gb1 = _mm_or_si128(b1, g_hi2); // bg0...bg7|xx +#if (WEBP_SWAP_16BIT_CSP == 1) + const __m128i rgba = _mm_unpacklo_epi8(gb1, rg1); // rggb0...rggb7 +#else + const __m128i rgba = _mm_unpacklo_epi8(rg1, gb1); // bgrb0...bgrb7 +#endif + _mm_storeu_si128(out++, rgba); + num_pixels -= 8; + } + // left-overs + if (num_pixels > 0) { + VP8LConvertBGRAToRGB565_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + } +} + +static void ConvertBGRAToBGR_SSE2(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const __m128i mask_l = _mm_set_epi32(0, 0x00ffffff, 0, 0x00ffffff); + const __m128i mask_h = _mm_set_epi32(0x00ffffff, 0, 0x00ffffff, 0); + const __m128i* in = (const __m128i*)src; + const uint8_t* const end = dst + num_pixels * 3; + // the last storel_epi64 below writes 8 bytes starting at offset 18 + while (dst + 26 <= end) { + const __m128i bgra0 = _mm_loadu_si128(in++); // bgra0|bgra1|bgra2|bgra3 + const __m128i bgra4 = _mm_loadu_si128(in++); // bgra4|bgra5|bgra6|bgra7 + const __m128i a0l = _mm_and_si128(bgra0, mask_l); // bgr0|0|bgr0|0 + const __m128i a4l = _mm_and_si128(bgra4, mask_l); // bgr0|0|bgr0|0 + const __m128i a0h = _mm_and_si128(bgra0, mask_h); // 0|bgr0|0|bgr0 + const __m128i a4h = _mm_and_si128(bgra4, mask_h); // 0|bgr0|0|bgr0 + const __m128i b0h = _mm_srli_epi64(a0h, 8); // 000b|gr00|000b|gr00 + const __m128i b4h = _mm_srli_epi64(a4h, 8); // 000b|gr00|000b|gr00 + const __m128i c0 = _mm_or_si128(a0l, b0h); // rgbrgb00|rgbrgb00 + const __m128i c4 = _mm_or_si128(a4l, b4h); // rgbrgb00|rgbrgb00 + const __m128i c2 = _mm_srli_si128(c0, 8); + const __m128i c6 = _mm_srli_si128(c4, 8); + _mm_storel_epi64((__m128i*)(dst + 0), c0); + _mm_storel_epi64((__m128i*)(dst + 6), c2); + _mm_storel_epi64((__m128i*)(dst + 12), c4); + _mm_storel_epi64((__m128i*)(dst + 18), c6); + dst += 24; + num_pixels -= 8; + } + // left-overs + if (num_pixels > 0) { + VP8LConvertBGRAToBGR_C((const uint32_t*)in, num_pixels, dst); + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LDspInitSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitSSE2(void) { + 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_SSE2; + VP8LTransformColorInverse = TransformColorInverse_SSE2; + + VP8LConvertBGRAToRGB = ConvertBGRAToRGB_SSE2; + VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA_SSE2; + VP8LConvertBGRAToRGBA4444 = ConvertBGRAToRGBA4444_SSE2; + VP8LConvertBGRAToRGB565 = ConvertBGRAToRGB565_SSE2; + VP8LConvertBGRAToBGR = ConvertBGRAToBGR_SSE2; +} + +#else // !WEBP_USE_SSE2 + +WEBP_DSP_INIT_STUB(VP8LDspInitSSE2) + +#endif // WEBP_USE_SSE2 diff --git a/internal/libwebp-1.2.1/src/dsp/lossless_sse41.c b/internal/libwebp-1.2.1/src/dsp/lossless_sse41.c new file mode 100644 index 0000000..b0d6daa --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/lossless_sse41.c @@ -0,0 +1,132 @@ +// Copyright 2021 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. +// ----------------------------------------------------------------------------- +// +// SSE41 variant of methods for lossless decoder + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE41) + +#include "src/dsp/common_sse41.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" + +//------------------------------------------------------------------------------ +// Color-space conversion functions + +static void TransformColorInverse_SSE41(const VP8LMultipliers* const m, + 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_set1_epi32((uint32_t)CST(green_to_red_) << 16 | + (CST(green_to_blue_) & 0xffff)); + const __m128i mults_b2 = _mm_set1_epi32(CST(red_to_blue_)); +#undef CST + const __m128i mask_ag = _mm_set1_epi32(0xff00ff00); + const __m128i perm1 = _mm_setr_epi8(-1, 1, -1, 1, -1, 5, -1, 5, + -1, 9, -1, 9, -1, 13, -1, 13); + const __m128i perm2 = _mm_setr_epi8(-1, 2, -1, -1, -1, 6, -1, -1, + -1, 10, -1, -1, -1, 14, -1, -1); + int i; + for (i = 0; i + 4 <= num_pixels; i += 4) { + const __m128i A = _mm_loadu_si128((const __m128i*)(src + i)); + const __m128i B = _mm_shuffle_epi8(A, perm1); // argb -> g0g0 + const __m128i C = _mm_mulhi_epi16(B, mults_rb); + const __m128i D = _mm_add_epi8(A, C); + const __m128i E = _mm_shuffle_epi8(D, perm2); + const __m128i F = _mm_mulhi_epi16(E, mults_b2); + const __m128i G = _mm_add_epi8(D, F); + const __m128i out = _mm_blendv_epi8(G, A, mask_ag); + _mm_storeu_si128((__m128i*)&dst[i], out); + } + // Fall-back to C-version for left-overs. + if (i != num_pixels) { + VP8LTransformColorInverse_C(m, src + i, num_pixels - i, dst + i); + } +} + +//------------------------------------------------------------------------------ + +#define ARGB_TO_RGB_SSE41 do { \ + while (num_pixels >= 16) { \ + const __m128i in0 = _mm_loadu_si128(in + 0); \ + const __m128i in1 = _mm_loadu_si128(in + 1); \ + const __m128i in2 = _mm_loadu_si128(in + 2); \ + const __m128i in3 = _mm_loadu_si128(in + 3); \ + const __m128i a0 = _mm_shuffle_epi8(in0, perm0); \ + const __m128i a1 = _mm_shuffle_epi8(in1, perm1); \ + const __m128i a2 = _mm_shuffle_epi8(in2, perm2); \ + const __m128i a3 = _mm_shuffle_epi8(in3, perm3); \ + const __m128i b0 = _mm_blend_epi16(a0, a1, 0xc0); \ + const __m128i b1 = _mm_blend_epi16(a1, a2, 0xf0); \ + const __m128i b2 = _mm_blend_epi16(a2, a3, 0xfc); \ + _mm_storeu_si128(out + 0, b0); \ + _mm_storeu_si128(out + 1, b1); \ + _mm_storeu_si128(out + 2, b2); \ + in += 4; \ + out += 3; \ + num_pixels -= 16; \ + } \ +} while (0) + +static void ConvertBGRAToRGB_SSE41(const uint32_t* src, int num_pixels, + uint8_t* dst) { + const __m128i* in = (const __m128i*)src; + __m128i* out = (__m128i*)dst; + const __m128i perm0 = _mm_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9, + 8, 14, 13, 12, -1, -1, -1, -1); + const __m128i perm1 = _mm_shuffle_epi32(perm0, 0x39); + const __m128i perm2 = _mm_shuffle_epi32(perm0, 0x4e); + const __m128i perm3 = _mm_shuffle_epi32(perm0, 0x93); + + ARGB_TO_RGB_SSE41; + + // left-overs + if (num_pixels > 0) { + VP8LConvertBGRAToRGB_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + } +} + +static void ConvertBGRAToBGR_SSE41(const uint32_t* src, + int num_pixels, uint8_t* dst) { + const __m128i* in = (const __m128i*)src; + __m128i* out = (__m128i*)dst; + const __m128i perm0 = _mm_setr_epi8(0, 1, 2, 4, 5, 6, 8, 9, 10, + 12, 13, 14, -1, -1, -1, -1); + const __m128i perm1 = _mm_shuffle_epi32(perm0, 0x39); + const __m128i perm2 = _mm_shuffle_epi32(perm0, 0x4e); + const __m128i perm3 = _mm_shuffle_epi32(perm0, 0x93); + + ARGB_TO_RGB_SSE41; + + // left-overs + if (num_pixels > 0) { + VP8LConvertBGRAToBGR_C((const uint32_t*)in, num_pixels, (uint8_t*)out); + } +} + +#undef ARGB_TO_RGB_SSE41 + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LDspInitSSE41(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitSSE41(void) { + VP8LTransformColorInverse = TransformColorInverse_SSE41; + VP8LConvertBGRAToRGB = ConvertBGRAToRGB_SSE41; + VP8LConvertBGRAToBGR = ConvertBGRAToBGR_SSE41; +} + +#else // !WEBP_USE_SSE41 + +WEBP_DSP_INIT_STUB(VP8LDspInitSSE41) + +#endif // WEBP_USE_SSE41 diff --git a/internal/libwebp-1.2.1/src/dsp/mips_macro.h b/internal/libwebp-1.2.1/src/dsp/mips_macro.h new file mode 100644 index 0000000..44aba9b --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/mips_macro.h @@ -0,0 +1,200 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS common macros + +#ifndef WEBP_DSP_MIPS_MACRO_H_ +#define WEBP_DSP_MIPS_MACRO_H_ + +#if defined(__GNUC__) && defined(__ANDROID__) && LOCAL_GCC_VERSION == 0x409 +#define WORK_AROUND_GCC +#endif + +#define STR(s) #s +#define XSTR(s) STR(s) + +// O0[31..16 | 15..0] = I0[31..16 | 15..0] + I1[31..16 | 15..0] +// O1[31..16 | 15..0] = I0[31..16 | 15..0] - I1[31..16 | 15..0] +// O - output +// I - input (macro doesn't change it) +#define ADD_SUB_HALVES(O0, O1, \ + I0, I1) \ + "addq.ph %[" #O0 "], %[" #I0 "], %[" #I1 "] \n\t" \ + "subq.ph %[" #O1 "], %[" #I0 "], %[" #I1 "] \n\t" + +// O - output +// I - input (macro doesn't change it) +// I[0/1] - offset in bytes +#define LOAD_IN_X2(O0, O1, \ + I0, I1) \ + "lh %[" #O0 "], " #I0 "(%[in]) \n\t" \ + "lh %[" #O1 "], " #I1 "(%[in]) \n\t" + +// I0 - location +// I1..I9 - offsets in bytes +#define LOAD_WITH_OFFSET_X4(O0, O1, O2, O3, \ + I0, I1, I2, I3, I4, I5, I6, I7, I8, I9) \ + "ulw %[" #O0 "], " #I1 "+" XSTR(I9) "*" #I5 "(%[" #I0 "]) \n\t" \ + "ulw %[" #O1 "], " #I2 "+" XSTR(I9) "*" #I6 "(%[" #I0 "]) \n\t" \ + "ulw %[" #O2 "], " #I3 "+" XSTR(I9) "*" #I7 "(%[" #I0 "]) \n\t" \ + "ulw %[" #O3 "], " #I4 "+" XSTR(I9) "*" #I8 "(%[" #I0 "]) \n\t" + +// O - output +// IO - input/output +// I - input (macro doesn't change it) +#define MUL_SHIFT_SUM(O0, O1, O2, O3, O4, O5, O6, O7, \ + IO0, IO1, IO2, IO3, \ + I0, I1, I2, I3, I4, I5, I6, I7) \ + "mul %[" #O0 "], %[" #I0 "], %[kC2] \n\t" \ + "mul %[" #O1 "], %[" #I0 "], %[kC1] \n\t" \ + "mul %[" #O2 "], %[" #I1 "], %[kC2] \n\t" \ + "mul %[" #O3 "], %[" #I1 "], %[kC1] \n\t" \ + "mul %[" #O4 "], %[" #I2 "], %[kC2] \n\t" \ + "mul %[" #O5 "], %[" #I2 "], %[kC1] \n\t" \ + "mul %[" #O6 "], %[" #I3 "], %[kC2] \n\t" \ + "mul %[" #O7 "], %[" #I3 "], %[kC1] \n\t" \ + "sra %[" #O0 "], %[" #O0 "], 16 \n\t" \ + "sra %[" #O1 "], %[" #O1 "], 16 \n\t" \ + "sra %[" #O2 "], %[" #O2 "], 16 \n\t" \ + "sra %[" #O3 "], %[" #O3 "], 16 \n\t" \ + "sra %[" #O4 "], %[" #O4 "], 16 \n\t" \ + "sra %[" #O5 "], %[" #O5 "], 16 \n\t" \ + "sra %[" #O6 "], %[" #O6 "], 16 \n\t" \ + "sra %[" #O7 "], %[" #O7 "], 16 \n\t" \ + "addu %[" #IO0 "], %[" #IO0 "], %[" #I4 "] \n\t" \ + "addu %[" #IO1 "], %[" #IO1 "], %[" #I5 "] \n\t" \ + "subu %[" #IO2 "], %[" #IO2 "], %[" #I6 "] \n\t" \ + "subu %[" #IO3 "], %[" #IO3 "], %[" #I7 "] \n\t" + +// O - output +// I - input (macro doesn't change it) +#define INSERT_HALF_X2(O0, O1, \ + I0, I1) \ + "ins %[" #O0 "], %[" #I0 "], 16, 16 \n\t" \ + "ins %[" #O1 "], %[" #I1 "], 16, 16 \n\t" + +// O - output +// I - input (macro doesn't change it) +#define SRA_16(O0, O1, O2, O3, \ + I0, I1, I2, I3) \ + "sra %[" #O0 "], %[" #I0 "], 16 \n\t" \ + "sra %[" #O1 "], %[" #I1 "], 16 \n\t" \ + "sra %[" #O2 "], %[" #I2 "], 16 \n\t" \ + "sra %[" #O3 "], %[" #I3 "], 16 \n\t" + +// temp0[31..16 | 15..0] = temp8[31..16 | 15..0] + temp12[31..16 | 15..0] +// temp1[31..16 | 15..0] = temp8[31..16 | 15..0] - temp12[31..16 | 15..0] +// temp0[31..16 | 15..0] = temp0[31..16 >> 3 | 15..0 >> 3] +// temp1[31..16 | 15..0] = temp1[31..16 >> 3 | 15..0 >> 3] +// O - output +// I - input (macro doesn't change it) +#define SHIFT_R_SUM_X2(O0, O1, O2, O3, O4, O5, O6, O7, \ + I0, I1, I2, I3, I4, I5, I6, I7) \ + "addq.ph %[" #O0 "], %[" #I0 "], %[" #I4 "] \n\t" \ + "subq.ph %[" #O1 "], %[" #I0 "], %[" #I4 "] \n\t" \ + "addq.ph %[" #O2 "], %[" #I1 "], %[" #I5 "] \n\t" \ + "subq.ph %[" #O3 "], %[" #I1 "], %[" #I5 "] \n\t" \ + "addq.ph %[" #O4 "], %[" #I2 "], %[" #I6 "] \n\t" \ + "subq.ph %[" #O5 "], %[" #I2 "], %[" #I6 "] \n\t" \ + "addq.ph %[" #O6 "], %[" #I3 "], %[" #I7 "] \n\t" \ + "subq.ph %[" #O7 "], %[" #I3 "], %[" #I7 "] \n\t" \ + "shra.ph %[" #O0 "], %[" #O0 "], 3 \n\t" \ + "shra.ph %[" #O1 "], %[" #O1 "], 3 \n\t" \ + "shra.ph %[" #O2 "], %[" #O2 "], 3 \n\t" \ + "shra.ph %[" #O3 "], %[" #O3 "], 3 \n\t" \ + "shra.ph %[" #O4 "], %[" #O4 "], 3 \n\t" \ + "shra.ph %[" #O5 "], %[" #O5 "], 3 \n\t" \ + "shra.ph %[" #O6 "], %[" #O6 "], 3 \n\t" \ + "shra.ph %[" #O7 "], %[" #O7 "], 3 \n\t" + +// precrq.ph.w temp0, temp8, temp2 +// temp0 = temp8[31..16] | temp2[31..16] +// ins temp2, temp8, 16, 16 +// temp2 = temp8[31..16] | temp2[15..0] +// O - output +// IO - input/output +// I - input (macro doesn't change it) +#define PACK_2_HALVES_TO_WORD(O0, O1, O2, O3, \ + IO0, IO1, IO2, IO3, \ + I0, I1, I2, I3) \ + "precrq.ph.w %[" #O0 "], %[" #I0 "], %[" #IO0 "] \n\t" \ + "precrq.ph.w %[" #O1 "], %[" #I1 "], %[" #IO1 "] \n\t" \ + "ins %[" #IO0 "], %[" #I0 "], 16, 16 \n\t" \ + "ins %[" #IO1 "], %[" #I1 "], 16, 16 \n\t" \ + "precrq.ph.w %[" #O2 "], %[" #I2 "], %[" #IO2 "] \n\t" \ + "precrq.ph.w %[" #O3 "], %[" #I3 "], %[" #IO3 "] \n\t" \ + "ins %[" #IO2 "], %[" #I2 "], 16, 16 \n\t" \ + "ins %[" #IO3 "], %[" #I3 "], 16, 16 \n\t" + +// preceu.ph.qbr temp0, temp8 +// temp0 = 0 | 0 | temp8[23..16] | temp8[7..0] +// preceu.ph.qbl temp1, temp8 +// temp1 = temp8[23..16] | temp8[7..0] | 0 | 0 +// O - output +// I - input (macro doesn't change it) +#define CONVERT_2_BYTES_TO_HALF(O0, O1, O2, O3, O4, O5, O6, O7, \ + I0, I1, I2, I3) \ + "preceu.ph.qbr %[" #O0 "], %[" #I0 "] \n\t" \ + "preceu.ph.qbl %[" #O1 "], %[" #I0 "] \n\t" \ + "preceu.ph.qbr %[" #O2 "], %[" #I1 "] \n\t" \ + "preceu.ph.qbl %[" #O3 "], %[" #I1 "] \n\t" \ + "preceu.ph.qbr %[" #O4 "], %[" #I2 "] \n\t" \ + "preceu.ph.qbl %[" #O5 "], %[" #I2 "] \n\t" \ + "preceu.ph.qbr %[" #O6 "], %[" #I3 "] \n\t" \ + "preceu.ph.qbl %[" #O7 "], %[" #I3 "] \n\t" + +// temp0[31..16 | 15..0] = temp0[31..16 | 15..0] + temp8[31..16 | 15..0] +// temp0[31..16 | 15..0] = temp0[31..16 <<(s) 7 | 15..0 <<(s) 7] +// temp1..temp7 same as temp0 +// precrqu_s.qb.ph temp0, temp1, temp0: +// temp0 = temp1[31..24] | temp1[15..8] | temp0[31..24] | temp0[15..8] +// store temp0 to dst +// IO - input/output +// I - input (macro doesn't change it) +#define STORE_SAT_SUM_X2(IO0, IO1, IO2, IO3, IO4, IO5, IO6, IO7, \ + I0, I1, I2, I3, I4, I5, I6, I7, \ + I8, I9, I10, I11, I12, I13) \ + "addq.ph %[" #IO0 "], %[" #IO0 "], %[" #I0 "] \n\t" \ + "addq.ph %[" #IO1 "], %[" #IO1 "], %[" #I1 "] \n\t" \ + "addq.ph %[" #IO2 "], %[" #IO2 "], %[" #I2 "] \n\t" \ + "addq.ph %[" #IO3 "], %[" #IO3 "], %[" #I3 "] \n\t" \ + "addq.ph %[" #IO4 "], %[" #IO4 "], %[" #I4 "] \n\t" \ + "addq.ph %[" #IO5 "], %[" #IO5 "], %[" #I5 "] \n\t" \ + "addq.ph %[" #IO6 "], %[" #IO6 "], %[" #I6 "] \n\t" \ + "addq.ph %[" #IO7 "], %[" #IO7 "], %[" #I7 "] \n\t" \ + "shll_s.ph %[" #IO0 "], %[" #IO0 "], 7 \n\t" \ + "shll_s.ph %[" #IO1 "], %[" #IO1 "], 7 \n\t" \ + "shll_s.ph %[" #IO2 "], %[" #IO2 "], 7 \n\t" \ + "shll_s.ph %[" #IO3 "], %[" #IO3 "], 7 \n\t" \ + "shll_s.ph %[" #IO4 "], %[" #IO4 "], 7 \n\t" \ + "shll_s.ph %[" #IO5 "], %[" #IO5 "], 7 \n\t" \ + "shll_s.ph %[" #IO6 "], %[" #IO6 "], 7 \n\t" \ + "shll_s.ph %[" #IO7 "], %[" #IO7 "], 7 \n\t" \ + "precrqu_s.qb.ph %[" #IO0 "], %[" #IO1 "], %[" #IO0 "] \n\t" \ + "precrqu_s.qb.ph %[" #IO2 "], %[" #IO3 "], %[" #IO2 "] \n\t" \ + "precrqu_s.qb.ph %[" #IO4 "], %[" #IO5 "], %[" #IO4 "] \n\t" \ + "precrqu_s.qb.ph %[" #IO6 "], %[" #IO7 "], %[" #IO6 "] \n\t" \ + "usw %[" #IO0 "], " XSTR(I13) "*" #I9 "(%[" #I8 "]) \n\t" \ + "usw %[" #IO2 "], " XSTR(I13) "*" #I10 "(%[" #I8 "]) \n\t" \ + "usw %[" #IO4 "], " XSTR(I13) "*" #I11 "(%[" #I8 "]) \n\t" \ + "usw %[" #IO6 "], " XSTR(I13) "*" #I12 "(%[" #I8 "]) \n\t" + +#define OUTPUT_EARLY_CLOBBER_REGS_10() \ + : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), \ + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [temp6]"=&r"(temp6), \ + [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), [temp9]"=&r"(temp9), \ + [temp10]"=&r"(temp10) + +#define OUTPUT_EARLY_CLOBBER_REGS_18() \ + OUTPUT_EARLY_CLOBBER_REGS_10(), \ + [temp11]"=&r"(temp11), [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), \ + [temp14]"=&r"(temp14), [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), \ + [temp17]"=&r"(temp17), [temp18]"=&r"(temp18) + +#endif // WEBP_DSP_MIPS_MACRO_H_ diff --git a/internal/libwebp-1.2.1/src/dsp/msa_macro.h b/internal/libwebp-1.2.1/src/dsp/msa_macro.h new file mode 100644 index 0000000..de026a1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/msa_macro.h @@ -0,0 +1,1392 @@ +// 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 common macros +// +// Author(s): Prashant Patil (prashant.patil@imgtec.com) + +#ifndef WEBP_DSP_MSA_MACRO_H_ +#define WEBP_DSP_MSA_MACRO_H_ + +#include +#include + +#if defined(__clang__) + #define CLANG_BUILD +#endif + +#ifdef CLANG_BUILD + #define ALPHAVAL (-1) + #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 ALPHAVAL (0xff) + #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)) +#define LD_UB(...) LD_B(v16u8, __VA_ARGS__) +#define LD_SB(...) LD_B(v16i8, __VA_ARGS__) + +#define LD_H(RTYPE, psrc) *((RTYPE*)(psrc)) +#define LD_UH(...) LD_H(v8u16, __VA_ARGS__) +#define LD_SH(...) LD_H(v8i16, __VA_ARGS__) + +#define LD_W(RTYPE, psrc) *((RTYPE*)(psrc)) +#define LD_UW(...) LD_W(v4u32, __VA_ARGS__) +#define LD_SW(...) LD_W(v4i32, __VA_ARGS__) + +#define ST_B(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in +#define ST_UB(...) ST_B(v16u8, __VA_ARGS__) +#define ST_SB(...) ST_B(v16i8, __VA_ARGS__) + +#define ST_H(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in +#define ST_UH(...) ST_H(v8u16, __VA_ARGS__) +#define ST_SH(...) ST_H(v8i16, __VA_ARGS__) + +#define ST_W(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in +#define ST_UW(...) ST_W(v4u32, __VA_ARGS__) +#define ST_SW(...) ST_W(v4i32, __VA_ARGS__) + +#define MSA_LOAD_FUNC(TYPE, INSTR, FUNC_NAME) \ + static inline TYPE FUNC_NAME(const void* const psrc) { \ + const uint8_t* const psrc_m = (const uint8_t*)psrc; \ + TYPE val_m; \ + asm volatile ( \ + "" #INSTR " %[val_m], %[psrc_m] \n\t" \ + : [val_m] "=r" (val_m) \ + : [psrc_m] "m" (*psrc_m)); \ + return val_m; \ + } + +#define MSA_LOAD(psrc, FUNC_NAME) FUNC_NAME(psrc) + +#define MSA_STORE_FUNC(TYPE, INSTR, FUNC_NAME) \ + static inline void FUNC_NAME(TYPE val, void* const pdst) { \ + uint8_t* const pdst_m = (uint8_t*)pdst; \ + TYPE val_m = val; \ + asm volatile ( \ + " " #INSTR " %[val_m], %[pdst_m] \n\t" \ + : [pdst_m] "=m" (*pdst_m) \ + : [val_m] "r" (val_m)); \ + } + +#define MSA_STORE(val, pdst, FUNC_NAME) FUNC_NAME(val, pdst) + +#if (__mips_isa_rev >= 6) + MSA_LOAD_FUNC(uint16_t, lh, msa_lh); + #define LH(psrc) MSA_LOAD(psrc, msa_lh) + MSA_LOAD_FUNC(uint32_t, lw, msa_lw); + #define LW(psrc) MSA_LOAD(psrc, msa_lw) + #if (__mips == 64) + MSA_LOAD_FUNC(uint64_t, ld, msa_ld); + #define LD(psrc) MSA_LOAD(psrc, msa_ld) + #else // !(__mips == 64) + #define LD(psrc) ((((uint64_t)MSA_LOAD(psrc + 4, msa_lw)) << 32) | \ + MSA_LOAD(psrc, msa_lw)) + #endif // (__mips == 64) + + MSA_STORE_FUNC(uint16_t, sh, msa_sh); + #define SH(val, pdst) MSA_STORE(val, pdst, msa_sh) + MSA_STORE_FUNC(uint32_t, sw, msa_sw); + #define SW(val, pdst) MSA_STORE(val, pdst, msa_sw) + MSA_STORE_FUNC(uint64_t, sd, msa_sd); + #define SD(val, pdst) MSA_STORE(val, pdst, msa_sd) +#else // !(__mips_isa_rev >= 6) + MSA_LOAD_FUNC(uint16_t, ulh, msa_ulh); + #define LH(psrc) MSA_LOAD(psrc, msa_ulh) + MSA_LOAD_FUNC(uint32_t, ulw, msa_ulw); + #define LW(psrc) MSA_LOAD(psrc, msa_ulw) + #if (__mips == 64) + MSA_LOAD_FUNC(uint64_t, uld, msa_uld); + #define LD(psrc) MSA_LOAD(psrc, msa_uld) + #else // !(__mips == 64) + #define LD(psrc) ((((uint64_t)MSA_LOAD(psrc + 4, msa_ulw)) << 32) | \ + MSA_LOAD(psrc, msa_ulw)) + #endif // (__mips == 64) + + MSA_STORE_FUNC(uint16_t, ush, msa_ush); + #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) 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 + * Arguments : Inputs - psrc, stride + * Outputs - out0, out1, out2, out3 + * Details : Load word in 'out0' from (psrc) + * Load word in 'out1' from (psrc + stride) + * Load word in 'out2' from (psrc + 2 * stride) + * Load word in 'out3' from (psrc + 3 * stride) + */ +#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 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) 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 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * 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) 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_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) 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 + * GP register and stored to (pdst) + * 'Idx1' word element from input vector 'in0' is copied to the + * GP register and stored to (pdst + stride) + * 'Idx2' word element from input vector 'in0' is copied to the + * GP register and stored to (pdst + 2 * stride) + * '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) 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 + * Outputs - out + * Return Type - as per RTYPE + * Details : Byte elements from 'in1' vector are slid into 'in0' by + * value specified in the 'slide_val' + */ +#define SLDI_B(RTYPE, in0, in1, slide_val) \ + (RTYPE)__msa_sldi_b((v16i8)in0, (v16i8)in1, slide_val) \ + +#define SLDI_UB(...) SLDI_B(v16u8, __VA_ARGS__) +#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 + * Return Type - as per RTYPE + * 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) 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) 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); \ +} 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) 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 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 + * Output - out + * Return Type - as per RTYPE + * Details : Set element 0 in vector 'out' to value specified in 'in0' + */ +#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) 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 + * Return Type - as per RTYPE + * 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) 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__) +#define ILVR_B2_SH(...) ILVR_B2(v8i16, __VA_ARGS__) +#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) 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__) +#define ILVR_B4_SH(...) ILVR_B4(v8i16, __VA_ARGS__) +#define ILVR_B4_SW(...) ILVR_B4(v4i32, __VA_ARGS__) + +/* Description : Interleave right half of halfword elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * 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) 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) 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__) + +/* Description : Interleave right half of double word elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * 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) 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 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) 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 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even byte elements of 'in0' are copied to the left half of + * 'out0' & even byte elements of 'in1' are copied to the right + * half of 'out0'. + */ +#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 + * Return Type - as per input vector RTYPE + * 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) 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) 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__) + +/* Description : Arithmetic shift right all elements of half-word vector + * Arguments : Inputs - in0, in1, shift + * Outputs - in place operation + * Return Type - as per input vector RTYPE + * 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) 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__) + +/* Description : Arithmetic rounded shift right all elements of word vector + * Arguments : Inputs - in0, in1, shift + * Outputs - in place operation + * Return Type - as per input vector RTYPE + * 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) 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) 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) 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) do { \ + out0 = in0 + in1; \ + out1 = in2 + in3; \ +} while (0) + +#define ADD4(in0, in1, in2, in3, in4, in5, in6, in7, \ + 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 + * Arguments : Input - in (halfword vector) + * Outputs - out0, out1 (sign extended word vectors) + * Return Type - signed word + * Details : Sign bit of halfword elements from input vector 'in' is + * extracted and interleaved right with same vector 'in0' to + * generate 4 signed word elements in 'out0' + * Then interleaved left with same vector 'in0' to + * generate 4 signed word elements in '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) 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) 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); \ + out0 = (RTYPE)__msa_ilvr_d((v2i64)s2_m, (v2i64)s0_m); \ + 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 + * Arguments : Inputs - in0, in1, in2, in3, pdst, stride + * 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) do { \ + uint32_t src0_m, src1_m, src2_m, src3_m; \ + v8i16 inp0_m, inp1_m, res0_m, res1_m; \ + v16i8 dst0_m = { 0 }; \ + v16i8 dst1_m = { 0 }; \ + const v16i8 zero_m = { 0 }; \ + ILVR_D2_SH(in1, in0, in3, in2, inp0_m, inp1_m); \ + LW4(pdst, stride, src0_m, src1_m, src2_m, src3_m); \ + INSERT_W2_SB(src0_m, src1_m, dst0_m); \ + INSERT_W2_SB(src2_m, src3_m, dst1_m); \ + ILVR_B2_SH(zero_m, dst0_m, zero_m, dst1_m, res0_m, res1_m); \ + ADD2(res0_m, inp0_m, res1_m, inp1_m, res0_m, res1_m); \ + 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/internal/libwebp-1.2.1/src/dsp/neon.h b/internal/libwebp-1.2.1/src/dsp/neon.h new file mode 100644 index 0000000..aa1dea1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/neon.h @@ -0,0 +1,101 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// NEON common code. + +#ifndef WEBP_DSP_NEON_H_ +#define WEBP_DSP_NEON_H_ + +#include + +#include "src/dsp/dsp.h" + +// Right now, some intrinsics functions seem slower, so we disable them +// everywhere except newer clang/gcc or aarch64 where the inline assembly is +// incompatible. +#if LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,9) || defined(__aarch64__) +#define WEBP_USE_INTRINSICS // use intrinsics when possible +#endif + +#define INIT_VECTOR2(v, a, b) do { \ + v.val[0] = a; \ + v.val[1] = b; \ +} while (0) + +#define INIT_VECTOR3(v, a, b, c) do { \ + v.val[0] = a; \ + v.val[1] = b; \ + v.val[2] = c; \ +} while (0) + +#define INIT_VECTOR4(v, a, b, c, d) do { \ + v.val[0] = a; \ + v.val[1] = b; \ + v.val[2] = c; \ + v.val[3] = d; \ +} while (0) + +// if using intrinsics, this flag avoids some functions that make gcc-4.6.3 +// crash ("internal compiler error: in immed_double_const, at emit-rtl."). +// (probably similar to gcc.gnu.org/bugzilla/show_bug.cgi?id=48183) +#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__)) +#define WORK_AROUND_GCC +#endif + +static WEBP_INLINE int32x4x4_t Transpose4x4_NEON(const int32x4x4_t rows) { + uint64x2x2_t row01, row23; + + row01.val[0] = vreinterpretq_u64_s32(rows.val[0]); + row01.val[1] = vreinterpretq_u64_s32(rows.val[1]); + row23.val[0] = vreinterpretq_u64_s32(rows.val[2]); + row23.val[1] = vreinterpretq_u64_s32(rows.val[3]); + // Transpose 64-bit values (there's no vswp equivalent) + { + const uint64x1_t row0h = vget_high_u64(row01.val[0]); + const uint64x1_t row2l = vget_low_u64(row23.val[0]); + const uint64x1_t row1h = vget_high_u64(row01.val[1]); + const uint64x1_t row3l = vget_low_u64(row23.val[1]); + row01.val[0] = vcombine_u64(vget_low_u64(row01.val[0]), row2l); + row23.val[0] = vcombine_u64(row0h, vget_high_u64(row23.val[0])); + row01.val[1] = vcombine_u64(vget_low_u64(row01.val[1]), row3l); + row23.val[1] = vcombine_u64(row1h, vget_high_u64(row23.val[1])); + } + { + const int32x4x2_t out01 = vtrnq_s32(vreinterpretq_s32_u64(row01.val[0]), + vreinterpretq_s32_u64(row01.val[1])); + const int32x4x2_t out23 = vtrnq_s32(vreinterpretq_s32_u64(row23.val[0]), + vreinterpretq_s32_u64(row23.val[1])); + int32x4x4_t out; + out.val[0] = out01.val[0]; + out.val[1] = out01.val[1]; + out.val[2] = out23.val[0]; + out.val[3] = out23.val[1]; + return out; + } +} + +#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/internal/libwebp-1.2.1/src/dsp/quant.h b/internal/libwebp-1.2.1/src/dsp/quant.h new file mode 100644 index 0000000..5e8dba8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/quant.h @@ -0,0 +1,85 @@ +// Copyright 2018 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. +// ----------------------------------------------------------------------------- + +#ifndef WEBP_DSP_QUANT_H_ +#define WEBP_DSP_QUANT_H_ + +#include + +#include "src/dsp/dsp.h" +#include "src/webp/types.h" + +#if defined(WEBP_USE_NEON) && !defined(WEBP_ANDROID_NEON) && \ + !defined(WEBP_HAVE_NEON_RTCD) +#include + +#define IsFlat IsFlat_NEON + +static uint32x2_t horizontal_add_uint32x4(const uint32x4_t a) { + const uint64x2_t b = vpaddlq_u32(a); + return vadd_u32(vreinterpret_u32_u64(vget_low_u64(b)), + vreinterpret_u32_u64(vget_high_u64(b))); +} + +static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks, + int thresh) { + const int16x8_t tst_ones = vdupq_n_s16(-1); + uint32x4_t sum = vdupq_n_u32(0); + + for (int i = 0; i < num_blocks; ++i) { + // Set DC to zero. + const int16x8_t a_0 = vsetq_lane_s16(0, vld1q_s16(levels), 0); + const int16x8_t a_1 = vld1q_s16(levels + 8); + + const uint16x8_t b_0 = vshrq_n_u16(vtstq_s16(a_0, tst_ones), 15); + const uint16x8_t b_1 = vshrq_n_u16(vtstq_s16(a_1, tst_ones), 15); + + sum = vpadalq_u16(sum, b_0); + sum = vpadalq_u16(sum, b_1); + + levels += 16; + } + return thresh >= (int32_t)vget_lane_u32(horizontal_add_uint32x4(sum), 0); +} + +#else + +#define IsFlat IsFlat_C + +static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks, + int thresh) { + int 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; +} + +#endif // defined(WEBP_USE_NEON) && !defined(WEBP_ANDROID_NEON) && + // !defined(WEBP_HAVE_NEON_RTCD) + +static WEBP_INLINE int IsFlatSource16(const uint8_t* src) { + const uint32_t v = src[0] * 0x01010101u; + int i; + for (i = 0; i < 16; ++i) { + if (memcmp(src + 0, &v, 4) || memcmp(src + 4, &v, 4) || + memcmp(src + 8, &v, 4) || memcmp(src + 12, &v, 4)) { + return 0; + } + src += BPS; + } + return 1; +} + +#endif // WEBP_DSP_QUANT_H_ diff --git a/internal/libwebp-1.2.1/src/dsp/rescaler.c b/internal/libwebp-1.2.1/src/dsp/rescaler.c new file mode 100644 index 0000000..14620ce --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/rescaler.c @@ -0,0 +1,251 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Rescaling functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include + +#include "src/dsp/dsp.h" +#include "src/utils/rescaler_utils.h" + +//------------------------------------------------------------------------------ +// Implementations of critical functions ImportRow / ExportRow + +#define ROUNDER (WEBP_RESCALER_ONE >> 1) +#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX) +#define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX) + +//------------------------------------------------------------------------------ +// Row import + +void WebPRescalerImportRowExpand_C(WebPRescaler* const wrk, + const uint8_t* src) { + const int x_stride = wrk->num_channels; + const int x_out_max = wrk->dst_width * wrk->num_channels; + int channel; + assert(!WebPRescalerInputDone(wrk)); + assert(wrk->x_expand); + for (channel = 0; channel < x_stride; ++channel) { + int x_in = channel; + int x_out = channel; + // simple bilinear interpolation + int accum = wrk->x_add; + rescaler_t left = (rescaler_t)src[x_in]; + rescaler_t right = + (wrk->src_width > 1) ? (rescaler_t)src[x_in + x_stride] : left; + x_in += x_stride; + while (1) { + wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum; + x_out += x_stride; + if (x_out >= x_out_max) break; + accum -= wrk->x_sub; + if (accum < 0) { + left = right; + x_in += x_stride; + assert(x_in < wrk->src_width * x_stride); + right = (rescaler_t)src[x_in]; + accum += wrk->x_add; + } + } + assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0); + } +} + +void WebPRescalerImportRowShrink_C(WebPRescaler* const wrk, + const uint8_t* src) { + const int x_stride = wrk->num_channels; + const int x_out_max = wrk->dst_width * wrk->num_channels; + int channel; + assert(!WebPRescalerInputDone(wrk)); + assert(!wrk->x_expand); + for (channel = 0; channel < x_stride; ++channel) { + int x_in = channel; + int x_out = channel; + uint32_t sum = 0; + int accum = 0; + while (x_out < x_out_max) { + uint32_t base = 0; + accum += wrk->x_add; + while (accum > 0) { + accum -= wrk->x_sub; + assert(x_in < wrk->src_width * x_stride); + base = src[x_in]; + sum += base; + x_in += x_stride; + } + { // Emit next horizontal pixel. + const rescaler_t frac = base * (-accum); + wrk->frow[x_out] = sum * wrk->x_sub - frac; + // fresh fractional start for next pixel + sum = (int)MULT_FIX(frac, wrk->fx_scale); + } + x_out += x_stride; + } + assert(accum == 0); + } +} + +//------------------------------------------------------------------------------ +// Row export + +void WebPRescalerExportRowExpand_C(WebPRescaler* const wrk) { + int x_out; + uint8_t* const dst = wrk->dst; + rescaler_t* const irow = wrk->irow; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const rescaler_t* const 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) { + for (x_out = 0; x_out < x_out_max; ++x_out) { + const uint32_t J = frow[x_out]; + const int v = (int)MULT_FIX(J, wrk->fy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + } + } else { + const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); + const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B); + for (x_out = 0; x_out < x_out_max; ++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); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + } + } +} + +void WebPRescalerExportRowShrink_C(WebPRescaler* const wrk) { + int x_out; + uint8_t* const dst = wrk->dst; + rescaler_t* const irow = wrk->irow; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const rescaler_t* const 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) { + for (x_out = 0; x_out < x_out_max; ++x_out) { + const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(frow[x_out], yscale); + const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + irow[x_out] = frac; // new fractional start + } + } else { + for (x_out = 0; x_out < x_out_max; ++x_out) { + const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + irow[x_out] = 0; + } + } +} + +#undef MULT_FIX_FLOOR +#undef MULT_FIX +#undef ROUNDER + +//------------------------------------------------------------------------------ +// Main entry calls + +void WebPRescalerImportRow(WebPRescaler* const wrk, const uint8_t* src) { + assert(!WebPRescalerInputDone(wrk)); + if (!wrk->x_expand) { + WebPRescalerImportRowShrink(wrk, src); + } else { + WebPRescalerImportRowExpand(wrk, src); + } +} + +void WebPRescalerExportRow(WebPRescaler* const wrk) { + if (wrk->y_accum <= 0) { + assert(!WebPRescalerOutputDone(wrk)); + if (wrk->y_expand) { + WebPRescalerExportRowExpand(wrk); + } else if (wrk->fxy_scale) { + WebPRescalerExportRowShrink(wrk); + } else { // special case + int i; + assert(wrk->src_height == wrk->dst_height && wrk->x_add == 1); + assert(wrk->src_width == 1 && wrk->dst_width <= 2); + for (i = 0; i < wrk->num_channels * wrk->dst_width; ++i) { + wrk->dst[i] = wrk->irow[i]; + wrk->irow[i] = 0; + } + } + wrk->y_accum += wrk->y_add; + wrk->dst += wrk->dst_stride; + ++wrk->dst_y; + } +} + +//------------------------------------------------------------------------------ + +WebPRescalerImportRowFunc WebPRescalerImportRowExpand; +WebPRescalerImportRowFunc WebPRescalerImportRowShrink; + +WebPRescalerExportRowFunc WebPRescalerExportRowExpand; +WebPRescalerExportRowFunc WebPRescalerExportRowShrink; + +extern void WebPRescalerDspInitSSE2(void); +extern void WebPRescalerDspInitMIPS32(void); +extern void WebPRescalerDspInitMIPSdspR2(void); +extern void WebPRescalerDspInitMSA(void); +extern void WebPRescalerDspInitNEON(void); + +WEBP_DSP_INIT_FUNC(WebPRescalerDspInit) { +#if !defined(WEBP_REDUCE_SIZE) +#if !WEBP_NEON_OMIT_C_CODE + WebPRescalerExportRowExpand = WebPRescalerExportRowExpand_C; + WebPRescalerExportRowShrink = WebPRescalerExportRowShrink_C; +#endif + + WebPRescalerImportRowExpand = WebPRescalerImportRowExpand_C; + WebPRescalerImportRowShrink = WebPRescalerImportRowShrink_C; + + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + WebPRescalerDspInitSSE2(); + } +#endif +#if defined(WEBP_USE_MIPS32) + if (VP8GetCPUInfo(kMIPS32)) { + WebPRescalerDspInitMIPS32(); + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + WebPRescalerDspInitMIPSdspR2(); + } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + WebPRescalerDspInitMSA(); + } +#endif + } + +#if defined(WEBP_HAVE_NEON) + if (WEBP_NEON_OMIT_C_CODE || + (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) { + WebPRescalerDspInitNEON(); + } +#endif + + assert(WebPRescalerExportRowExpand != NULL); + assert(WebPRescalerExportRowShrink != NULL); + assert(WebPRescalerImportRowExpand != NULL); + assert(WebPRescalerImportRowShrink != NULL); +#endif // WEBP_REDUCE_SIZE +} diff --git a/internal/libwebp-1.2.1/src/dsp/rescaler_mips32.c b/internal/libwebp-1.2.1/src/dsp/rescaler_mips32.c new file mode 100644 index 0000000..61f63c6 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/rescaler_mips32.c @@ -0,0 +1,295 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS version of rescaling functions +// +// Author(s): Djordje Pesut (djordje.pesut@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS32) && !defined(WEBP_REDUCE_SIZE) + +#include +#include "src/utils/rescaler_utils.h" + +//------------------------------------------------------------------------------ +// Row import + +static void ImportRowShrink_MIPS32(WebPRescaler* const wrk, + const uint8_t* src) { + const int x_stride = wrk->num_channels; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const int fx_scale = wrk->fx_scale; + const int x_add = wrk->x_add; + const int x_sub = wrk->x_sub; + const int x_stride1 = x_stride << 2; + int channel; + assert(!wrk->x_expand); + assert(!WebPRescalerInputDone(wrk)); + + for (channel = 0; channel < x_stride; ++channel) { + const uint8_t* src1 = src + channel; + rescaler_t* frow = wrk->frow + channel; + int temp1, temp2, temp3; + int base, frac, sum; + int accum, accum1; + int loop_c = x_out_max - channel; + + __asm__ volatile ( + "li %[temp1], 0x8000 \n\t" + "li %[temp2], 0x10000 \n\t" + "li %[sum], 0 \n\t" + "li %[accum], 0 \n\t" + "1: \n\t" + "addu %[accum], %[accum], %[x_add] \n\t" + "li %[base], 0 \n\t" + "blez %[accum], 3f \n\t" + "2: \n\t" + "lbu %[base], 0(%[src1]) \n\t" + "subu %[accum], %[accum], %[x_sub] \n\t" + "addu %[src1], %[src1], %[x_stride] \n\t" + "addu %[sum], %[sum], %[base] \n\t" + "bgtz %[accum], 2b \n\t" + "3: \n\t" + "negu %[accum1], %[accum] \n\t" + "mul %[frac], %[base], %[accum1] \n\t" + "mul %[temp3], %[sum], %[x_sub] \n\t" + "subu %[loop_c], %[loop_c], %[x_stride] \n\t" + "mult %[temp1], %[temp2] \n\t" + "maddu %[frac], %[fx_scale] \n\t" + "mfhi %[sum] \n\t" + "subu %[temp3], %[temp3], %[frac] \n\t" + "sw %[temp3], 0(%[frow]) \n\t" + "addu %[frow], %[frow], %[x_stride1] \n\t" + "bgtz %[loop_c], 1b \n\t" + : [accum]"=&r"(accum), [src1]"+r"(src1), [temp3]"=&r"(temp3), + [sum]"=&r"(sum), [base]"=&r"(base), [frac]"=&r"(frac), + [frow]"+r"(frow), [accum1]"=&r"(accum1), + [temp2]"=&r"(temp2), [temp1]"=&r"(temp1) + : [x_stride]"r"(x_stride), [fx_scale]"r"(fx_scale), + [x_sub]"r"(x_sub), [x_add]"r"(x_add), + [loop_c]"r"(loop_c), [x_stride1]"r"(x_stride1) + : "memory", "hi", "lo" + ); + assert(accum == 0); + } +} + +static void ImportRowExpand_MIPS32(WebPRescaler* const wrk, + const uint8_t* src) { + const int x_stride = wrk->num_channels; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const int x_add = wrk->x_add; + const int x_sub = wrk->x_sub; + const int src_width = wrk->src_width; + const int x_stride1 = x_stride << 2; + int channel; + assert(wrk->x_expand); + assert(!WebPRescalerInputDone(wrk)); + + for (channel = 0; channel < x_stride; ++channel) { + const uint8_t* src1 = src + channel; + rescaler_t* frow = wrk->frow + channel; + int temp1, temp2, temp3, temp4; + int frac; + int accum; + int x_out = channel; + + __asm__ volatile ( + "addiu %[temp3], %[src_width], -1 \n\t" + "lbu %[temp2], 0(%[src1]) \n\t" + "addu %[src1], %[src1], %[x_stride] \n\t" + "bgtz %[temp3], 0f \n\t" + "addiu %[temp1], %[temp2], 0 \n\t" + "b 3f \n\t" + "0: \n\t" + "lbu %[temp1], 0(%[src1]) \n\t" + "3: \n\t" + "addiu %[accum], %[x_add], 0 \n\t" + "1: \n\t" + "subu %[temp3], %[temp2], %[temp1] \n\t" + "mul %[temp3], %[temp3], %[accum] \n\t" + "mul %[temp4], %[temp1], %[x_add] \n\t" + "addu %[temp3], %[temp4], %[temp3] \n\t" + "sw %[temp3], 0(%[frow]) \n\t" + "addu %[frow], %[frow], %[x_stride1] \n\t" + "addu %[x_out], %[x_out], %[x_stride] \n\t" + "subu %[temp3], %[x_out], %[x_out_max] \n\t" + "bgez %[temp3], 2f \n\t" + "subu %[accum], %[accum], %[x_sub] \n\t" + "bgez %[accum], 4f \n\t" + "addiu %[temp2], %[temp1], 0 \n\t" + "addu %[src1], %[src1], %[x_stride] \n\t" + "lbu %[temp1], 0(%[src1]) \n\t" + "addu %[accum], %[accum], %[x_add] \n\t" + "4: \n\t" + "b 1b \n\t" + "2: \n\t" + : [src1]"+r"(src1), [accum]"=&r"(accum), [temp1]"=&r"(temp1), + [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), + [x_out]"+r"(x_out), [frac]"=&r"(frac), [frow]"+r"(frow) + : [x_stride]"r"(x_stride), [x_add]"r"(x_add), [x_sub]"r"(x_sub), + [x_stride1]"r"(x_stride1), [src_width]"r"(src_width), + [x_out_max]"r"(x_out_max) + : "memory", "hi", "lo" + ); + assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0); + } +} + +//------------------------------------------------------------------------------ +// Row export + +static void ExportRowExpand_MIPS32(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; + int temp0, temp1, temp3, temp4, temp5, loop_end; + const int temp2 = (int)wrk->fy_scale; + const int temp6 = x_out_max << 2; + assert(!WebPRescalerOutputDone(wrk)); + assert(wrk->y_accum <= 0); + assert(wrk->y_expand); + assert(wrk->y_sub != 0); + if (wrk->y_accum == 0) { + __asm__ volatile ( + "li %[temp3], 0x10000 \n\t" + "li %[temp4], 0x8000 \n\t" + "addu %[loop_end], %[frow], %[temp6] \n\t" + "1: \n\t" + "lw %[temp0], 0(%[frow]) \n\t" + "addiu %[dst], %[dst], 1 \n\t" + "addiu %[frow], %[frow], 4 \n\t" + "mult %[temp3], %[temp4] \n\t" + "maddu %[temp0], %[temp2] \n\t" + "mfhi %[temp5] \n\t" + "sb %[temp5], -1(%[dst]) \n\t" + "bne %[frow], %[loop_end], 1b \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow), + [dst]"+r"(dst), [loop_end]"=&r"(loop_end) + : [temp2]"r"(temp2), [temp6]"r"(temp6) + : "memory", "hi", "lo" + ); + } else { + const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); + const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B); + __asm__ volatile ( + "li %[temp3], 0x10000 \n\t" + "li %[temp4], 0x8000 \n\t" + "addu %[loop_end], %[frow], %[temp6] \n\t" + "1: \n\t" + "lw %[temp0], 0(%[frow]) \n\t" + "lw %[temp1], 0(%[irow]) \n\t" + "addiu %[dst], %[dst], 1 \n\t" + "mult %[temp3], %[temp4] \n\t" + "maddu %[A], %[temp0] \n\t" + "maddu %[B], %[temp1] \n\t" + "addiu %[frow], %[frow], 4 \n\t" + "addiu %[irow], %[irow], 4 \n\t" + "mfhi %[temp5] \n\t" + "mult %[temp3], %[temp4] \n\t" + "maddu %[temp5], %[temp2] \n\t" + "mfhi %[temp5] \n\t" + "sb %[temp5], -1(%[dst]) \n\t" + "bne %[frow], %[loop_end], 1b \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow), + [irow]"+r"(irow), [dst]"+r"(dst), [loop_end]"=&r"(loop_end) + : [temp2]"r"(temp2), [temp6]"r"(temp6), [A]"r"(A), [B]"r"(B) + : "memory", "hi", "lo" + ); + } +} + +#if 0 // disabled for now. TODO(skal): make match the C-code +static void ExportRowShrink_MIPS32(WebPRescaler* const wrk) { + const int x_out_max = wrk->dst_width * wrk->num_channels; + uint8_t* dst = wrk->dst; + rescaler_t* irow = wrk->irow; + const rescaler_t* frow = wrk->frow; + const int yscale = wrk->fy_scale * (-wrk->y_accum); + int temp0, temp1, temp3, temp4, temp5, loop_end; + const int temp2 = (int)wrk->fxy_scale; + const int temp6 = x_out_max << 2; + + assert(!WebPRescalerOutputDone(wrk)); + assert(wrk->y_accum <= 0); + assert(!wrk->y_expand); + assert(wrk->fxy_scale != 0); + if (yscale) { + __asm__ volatile ( + "li %[temp3], 0x10000 \n\t" + "li %[temp4], 0x8000 \n\t" + "addu %[loop_end], %[frow], %[temp6] \n\t" + "1: \n\t" + "lw %[temp0], 0(%[frow]) \n\t" + "mult %[temp3], %[temp4] \n\t" + "addiu %[frow], %[frow], 4 \n\t" + "maddu %[temp0], %[yscale] \n\t" + "mfhi %[temp1] \n\t" + "lw %[temp0], 0(%[irow]) \n\t" + "addiu %[dst], %[dst], 1 \n\t" + "addiu %[irow], %[irow], 4 \n\t" + "subu %[temp0], %[temp0], %[temp1] \n\t" + "mult %[temp3], %[temp4] \n\t" + "maddu %[temp0], %[temp2] \n\t" + "mfhi %[temp5] \n\t" + "sw %[temp1], -4(%[irow]) \n\t" + "sb %[temp5], -1(%[dst]) \n\t" + "bne %[frow], %[loop_end], 1b \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow), + [irow]"+r"(irow), [dst]"+r"(dst), [loop_end]"=&r"(loop_end) + : [temp2]"r"(temp2), [yscale]"r"(yscale), [temp6]"r"(temp6) + : "memory", "hi", "lo" + ); + } else { + __asm__ volatile ( + "li %[temp3], 0x10000 \n\t" + "li %[temp4], 0x8000 \n\t" + "addu %[loop_end], %[irow], %[temp6] \n\t" + "1: \n\t" + "lw %[temp0], 0(%[irow]) \n\t" + "addiu %[dst], %[dst], 1 \n\t" + "addiu %[irow], %[irow], 4 \n\t" + "mult %[temp3], %[temp4] \n\t" + "maddu %[temp0], %[temp2] \n\t" + "mfhi %[temp5] \n\t" + "sw $zero, -4(%[irow]) \n\t" + "sb %[temp5], -1(%[dst]) \n\t" + "bne %[irow], %[loop_end], 1b \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [irow]"+r"(irow), + [dst]"+r"(dst), [loop_end]"=&r"(loop_end) + : [temp2]"r"(temp2), [temp6]"r"(temp6) + : "memory", "hi", "lo" + ); + } +} +#endif // 0 + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPRescalerDspInitMIPS32(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMIPS32(void) { + WebPRescalerImportRowExpand = ImportRowExpand_MIPS32; + WebPRescalerImportRowShrink = ImportRowShrink_MIPS32; + WebPRescalerExportRowExpand = ExportRowExpand_MIPS32; +// WebPRescalerExportRowShrink = ExportRowShrink_MIPS32; +} + +#else // !WEBP_USE_MIPS32 + +WEBP_DSP_INIT_STUB(WebPRescalerDspInitMIPS32) + +#endif // WEBP_USE_MIPS32 diff --git a/internal/libwebp-1.2.1/src/dsp/rescaler_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/rescaler_mips_dsp_r2.c new file mode 100644 index 0000000..419b741 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/rescaler_mips_dsp_r2.c @@ -0,0 +1,314 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS version of rescaling functions +// +// Author(s): Djordje Pesut (djordje.pesut@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) && !defined(WEBP_REDUCE_SIZE) + +#include +#include "src/utils/rescaler_utils.h" + +#define ROUNDER (WEBP_RESCALER_ONE >> 1) +#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX) +#define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX) + +//------------------------------------------------------------------------------ +// Row export + +#if 0 // disabled for now. TODO(skal): make match the C-code +static void ExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) { + int i; + const int x_out_max = wrk->dst_width * wrk->num_channels; + uint8_t* dst = wrk->dst; + rescaler_t* irow = wrk->irow; + const rescaler_t* frow = wrk->frow; + const int yscale = wrk->fy_scale * (-wrk->y_accum); + int temp0, temp1, temp2, temp3, temp4, temp5, loop_end; + const int temp7 = (int)wrk->fxy_scale; + const int temp6 = (x_out_max & ~0x3) << 2; + assert(!WebPRescalerOutputDone(wrk)); + assert(wrk->y_accum <= 0); + assert(!wrk->y_expand); + assert(wrk->fxy_scale != 0); + if (yscale) { + if (x_out_max >= 4) { + int temp8, temp9, temp10, temp11; + __asm__ volatile ( + "li %[temp3], 0x10000 \n\t" + "li %[temp4], 0x8000 \n\t" + "addu %[loop_end], %[frow], %[temp6] \n\t" + "1: \n\t" + "lw %[temp0], 0(%[frow]) \n\t" + "lw %[temp1], 4(%[frow]) \n\t" + "lw %[temp2], 8(%[frow]) \n\t" + "lw %[temp5], 12(%[frow]) \n\t" + "mult $ac0, %[temp3], %[temp4] \n\t" + "maddu $ac0, %[temp0], %[yscale] \n\t" + "mult $ac1, %[temp3], %[temp4] \n\t" + "maddu $ac1, %[temp1], %[yscale] \n\t" + "mult $ac2, %[temp3], %[temp4] \n\t" + "maddu $ac2, %[temp2], %[yscale] \n\t" + "mult $ac3, %[temp3], %[temp4] \n\t" + "maddu $ac3, %[temp5], %[yscale] \n\t" + "addiu %[frow], %[frow], 16 \n\t" + "mfhi %[temp0], $ac0 \n\t" + "mfhi %[temp1], $ac1 \n\t" + "mfhi %[temp2], $ac2 \n\t" + "mfhi %[temp5], $ac3 \n\t" + "lw %[temp8], 0(%[irow]) \n\t" + "lw %[temp9], 4(%[irow]) \n\t" + "lw %[temp10], 8(%[irow]) \n\t" + "lw %[temp11], 12(%[irow]) \n\t" + "addiu %[dst], %[dst], 4 \n\t" + "addiu %[irow], %[irow], 16 \n\t" + "subu %[temp8], %[temp8], %[temp0] \n\t" + "subu %[temp9], %[temp9], %[temp1] \n\t" + "subu %[temp10], %[temp10], %[temp2] \n\t" + "subu %[temp11], %[temp11], %[temp5] \n\t" + "mult $ac0, %[temp3], %[temp4] \n\t" + "maddu $ac0, %[temp8], %[temp7] \n\t" + "mult $ac1, %[temp3], %[temp4] \n\t" + "maddu $ac1, %[temp9], %[temp7] \n\t" + "mult $ac2, %[temp3], %[temp4] \n\t" + "maddu $ac2, %[temp10], %[temp7] \n\t" + "mult $ac3, %[temp3], %[temp4] \n\t" + "maddu $ac3, %[temp11], %[temp7] \n\t" + "mfhi %[temp8], $ac0 \n\t" + "mfhi %[temp9], $ac1 \n\t" + "mfhi %[temp10], $ac2 \n\t" + "mfhi %[temp11], $ac3 \n\t" + "sw %[temp0], -16(%[irow]) \n\t" + "sw %[temp1], -12(%[irow]) \n\t" + "sw %[temp2], -8(%[irow]) \n\t" + "sw %[temp5], -4(%[irow]) \n\t" + "sb %[temp8], -4(%[dst]) \n\t" + "sb %[temp9], -3(%[dst]) \n\t" + "sb %[temp10], -2(%[dst]) \n\t" + "sb %[temp11], -1(%[dst]) \n\t" + "bne %[frow], %[loop_end], 1b \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow), + [irow]"+r"(irow), [dst]"+r"(dst), [loop_end]"=&r"(loop_end), + [temp8]"=&r"(temp8), [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), + [temp11]"=&r"(temp11), [temp2]"=&r"(temp2) + : [temp7]"r"(temp7), [yscale]"r"(yscale), [temp6]"r"(temp6) + : "memory", "hi", "lo", "$ac1hi", "$ac1lo", + "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo" + ); + } + for (i = 0; i < (x_out_max & 0x3); ++i) { + const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(*frow++, yscale); + const int v = (int)MULT_FIX(*irow - frac, wrk->fxy_scale); + *dst++ = (v > 255) ? 255u : (uint8_t)v; + *irow++ = frac; // new fractional start + } + } else { + if (x_out_max >= 4) { + __asm__ volatile ( + "li %[temp3], 0x10000 \n\t" + "li %[temp4], 0x8000 \n\t" + "addu %[loop_end], %[irow], %[temp6] \n\t" + "1: \n\t" + "lw %[temp0], 0(%[irow]) \n\t" + "lw %[temp1], 4(%[irow]) \n\t" + "lw %[temp2], 8(%[irow]) \n\t" + "lw %[temp5], 12(%[irow]) \n\t" + "addiu %[dst], %[dst], 4 \n\t" + "addiu %[irow], %[irow], 16 \n\t" + "mult $ac0, %[temp3], %[temp4] \n\t" + "maddu $ac0, %[temp0], %[temp7] \n\t" + "mult $ac1, %[temp3], %[temp4] \n\t" + "maddu $ac1, %[temp1], %[temp7] \n\t" + "mult $ac2, %[temp3], %[temp4] \n\t" + "maddu $ac2, %[temp2], %[temp7] \n\t" + "mult $ac3, %[temp3], %[temp4] \n\t" + "maddu $ac3, %[temp5], %[temp7] \n\t" + "mfhi %[temp0], $ac0 \n\t" + "mfhi %[temp1], $ac1 \n\t" + "mfhi %[temp2], $ac2 \n\t" + "mfhi %[temp5], $ac3 \n\t" + "sw $zero, -16(%[irow]) \n\t" + "sw $zero, -12(%[irow]) \n\t" + "sw $zero, -8(%[irow]) \n\t" + "sw $zero, -4(%[irow]) \n\t" + "sb %[temp0], -4(%[dst]) \n\t" + "sb %[temp1], -3(%[dst]) \n\t" + "sb %[temp2], -2(%[dst]) \n\t" + "sb %[temp5], -1(%[dst]) \n\t" + "bne %[irow], %[loop_end], 1b \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [irow]"+r"(irow), + [dst]"+r"(dst), [loop_end]"=&r"(loop_end), [temp2]"=&r"(temp2) + : [temp7]"r"(temp7), [temp6]"r"(temp6) + : "memory", "hi", "lo", "$ac1hi", "$ac1lo", + "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo" + ); + } + for (i = 0; i < (x_out_max & 0x3); ++i) { + const int v = (int)MULT_FIX_FLOOR(*irow, wrk->fxy_scale); + *dst++ = (v > 255) ? 255u : (uint8_t)v; + *irow++ = 0; + } + } +} +#endif // 0 + +static void ExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) { + int i; + 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; + int temp0, temp1, temp2, temp3, temp4, temp5, loop_end; + const int temp6 = (x_out_max & ~0x3) << 2; + const int temp7 = (int)wrk->fy_scale; + assert(!WebPRescalerOutputDone(wrk)); + assert(wrk->y_accum <= 0); + assert(wrk->y_expand); + assert(wrk->y_sub != 0); + if (wrk->y_accum == 0) { + if (x_out_max >= 4) { + __asm__ volatile ( + "li %[temp4], 0x10000 \n\t" + "li %[temp5], 0x8000 \n\t" + "addu %[loop_end], %[frow], %[temp6] \n\t" + "1: \n\t" + "lw %[temp0], 0(%[frow]) \n\t" + "lw %[temp1], 4(%[frow]) \n\t" + "lw %[temp2], 8(%[frow]) \n\t" + "lw %[temp3], 12(%[frow]) \n\t" + "addiu %[dst], %[dst], 4 \n\t" + "addiu %[frow], %[frow], 16 \n\t" + "mult $ac0, %[temp4], %[temp5] \n\t" + "maddu $ac0, %[temp0], %[temp7] \n\t" + "mult $ac1, %[temp4], %[temp5] \n\t" + "maddu $ac1, %[temp1], %[temp7] \n\t" + "mult $ac2, %[temp4], %[temp5] \n\t" + "maddu $ac2, %[temp2], %[temp7] \n\t" + "mult $ac3, %[temp4], %[temp5] \n\t" + "maddu $ac3, %[temp3], %[temp7] \n\t" + "mfhi %[temp0], $ac0 \n\t" + "mfhi %[temp1], $ac1 \n\t" + "mfhi %[temp2], $ac2 \n\t" + "mfhi %[temp3], $ac3 \n\t" + "sb %[temp0], -4(%[dst]) \n\t" + "sb %[temp1], -3(%[dst]) \n\t" + "sb %[temp2], -2(%[dst]) \n\t" + "sb %[temp3], -1(%[dst]) \n\t" + "bne %[frow], %[loop_end], 1b \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow), + [dst]"+r"(dst), [loop_end]"=&r"(loop_end), [temp2]"=&r"(temp2) + : [temp7]"r"(temp7), [temp6]"r"(temp6) + : "memory", "hi", "lo", "$ac1hi", "$ac1lo", + "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo" + ); + } + for (i = 0; i < (x_out_max & 0x3); ++i) { + const uint32_t J = *frow++; + const int v = (int)MULT_FIX(J, wrk->fy_scale); + *dst++ = (v > 255) ? 255u : (uint8_t)v; + } + } else { + const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); + const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B); + if (x_out_max >= 4) { + int temp8, temp9, temp10, temp11; + __asm__ volatile ( + "li %[temp8], 0x10000 \n\t" + "li %[temp9], 0x8000 \n\t" + "addu %[loop_end], %[frow], %[temp6] \n\t" + "1: \n\t" + "lw %[temp0], 0(%[frow]) \n\t" + "lw %[temp1], 4(%[frow]) \n\t" + "lw %[temp2], 8(%[frow]) \n\t" + "lw %[temp3], 12(%[frow]) \n\t" + "lw %[temp4], 0(%[irow]) \n\t" + "lw %[temp5], 4(%[irow]) \n\t" + "lw %[temp10], 8(%[irow]) \n\t" + "lw %[temp11], 12(%[irow]) \n\t" + "addiu %[dst], %[dst], 4 \n\t" + "mult $ac0, %[temp8], %[temp9] \n\t" + "maddu $ac0, %[A], %[temp0] \n\t" + "maddu $ac0, %[B], %[temp4] \n\t" + "mult $ac1, %[temp8], %[temp9] \n\t" + "maddu $ac1, %[A], %[temp1] \n\t" + "maddu $ac1, %[B], %[temp5] \n\t" + "mult $ac2, %[temp8], %[temp9] \n\t" + "maddu $ac2, %[A], %[temp2] \n\t" + "maddu $ac2, %[B], %[temp10] \n\t" + "mult $ac3, %[temp8], %[temp9] \n\t" + "maddu $ac3, %[A], %[temp3] \n\t" + "maddu $ac3, %[B], %[temp11] \n\t" + "addiu %[frow], %[frow], 16 \n\t" + "addiu %[irow], %[irow], 16 \n\t" + "mfhi %[temp0], $ac0 \n\t" + "mfhi %[temp1], $ac1 \n\t" + "mfhi %[temp2], $ac2 \n\t" + "mfhi %[temp3], $ac3 \n\t" + "mult $ac0, %[temp8], %[temp9] \n\t" + "maddu $ac0, %[temp0], %[temp7] \n\t" + "mult $ac1, %[temp8], %[temp9] \n\t" + "maddu $ac1, %[temp1], %[temp7] \n\t" + "mult $ac2, %[temp8], %[temp9] \n\t" + "maddu $ac2, %[temp2], %[temp7] \n\t" + "mult $ac3, %[temp8], %[temp9] \n\t" + "maddu $ac3, %[temp3], %[temp7] \n\t" + "mfhi %[temp0], $ac0 \n\t" + "mfhi %[temp1], $ac1 \n\t" + "mfhi %[temp2], $ac2 \n\t" + "mfhi %[temp3], $ac3 \n\t" + "sb %[temp0], -4(%[dst]) \n\t" + "sb %[temp1], -3(%[dst]) \n\t" + "sb %[temp2], -2(%[dst]) \n\t" + "sb %[temp3], -1(%[dst]) \n\t" + "bne %[frow], %[loop_end], 1b \n\t" + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3), + [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow), + [irow]"+r"(irow), [dst]"+r"(dst), [loop_end]"=&r"(loop_end), + [temp8]"=&r"(temp8), [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), + [temp11]"=&r"(temp11), [temp2]"=&r"(temp2) + : [temp7]"r"(temp7), [temp6]"r"(temp6), [A]"r"(A), [B]"r"(B) + : "memory", "hi", "lo", "$ac1hi", "$ac1lo", + "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo" + ); + } + for (i = 0; i < (x_out_max & 0x3); ++i) { + const uint64_t I = (uint64_t)A * *frow++ + + (uint64_t)B * *irow++; + const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX); + const int v = (int)MULT_FIX(J, wrk->fy_scale); + *dst++ = (v > 255) ? 255u : (uint8_t)v; + } + } +} + +#undef MULT_FIX_FLOOR +#undef MULT_FIX +#undef ROUNDER + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPRescalerDspInitMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMIPSdspR2(void) { + WebPRescalerExportRowExpand = ExportRowExpand_MIPSdspR2; +// WebPRescalerExportRowShrink = ExportRowShrink_MIPSdspR2; +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(WebPRescalerDspInitMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 diff --git a/internal/libwebp-1.2.1/src/dsp/rescaler_msa.c b/internal/libwebp-1.2.1/src/dsp/rescaler_msa.c new file mode 100644 index 0000000..256dbdd --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/rescaler_msa.c @@ -0,0 +1,443 @@ +// 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 "src/dsp/dsp.h" + +#if defined(WEBP_USE_MSA) && !defined(WEBP_REDUCE_SIZE) + +#include + +#include "src/utils/rescaler_utils.h" +#include "src/dsp/msa_macro.h" + +#define ROUNDER (WEBP_RESCALER_ONE >> 1) +#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX) +#define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> 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); + dst[x_out] = (v > 255) ? 255u : (uint8_t)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); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + } + } +} + +static void RescalerExportRowExpand_MIPSdspR2(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); + } +} + +#if 0 // disabled for now. TODO(skal): make match the C-code +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_FLOOR(frow[x_out], yscale); + const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)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); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + irow[x_out] = 0; + } + } +} + +static void RescalerExportRowShrink_MIPSdspR2(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); + } +} +#endif // 0 + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPRescalerDspInitMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMSA(void) { + WebPRescalerExportRowExpand = RescalerExportRowExpand_MIPSdspR2; +// WebPRescalerExportRowShrink = RescalerExportRowShrink_MIPSdspR2; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(WebPRescalerDspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/internal/libwebp-1.2.1/src/dsp/rescaler_neon.c b/internal/libwebp-1.2.1/src/dsp/rescaler_neon.c new file mode 100644 index 0000000..b976a85 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/rescaler_neon.c @@ -0,0 +1,192 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// NEON version of rescaling functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_NEON) && !defined(WEBP_REDUCE_SIZE) + +#include +#include +#include "src/dsp/neon.h" +#include "src/utils/rescaler_utils.h" + +#define ROUNDER (WEBP_RESCALER_ONE >> 1) +#define MULT_FIX_C(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX) +#define MULT_FIX_FLOOR_C(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX) + +#define LOAD_32x4(SRC, DST) const uint32x4_t DST = vld1q_u32((SRC)) +#define LOAD_32x8(SRC, DST0, DST1) \ + LOAD_32x4(SRC + 0, DST0); \ + LOAD_32x4(SRC + 4, DST1) + +#define STORE_32x8(SRC0, SRC1, DST) do { \ + vst1q_u32((DST) + 0, SRC0); \ + vst1q_u32((DST) + 4, SRC1); \ +} while (0); + +#if (WEBP_RESCALER_RFIX == 32) +#define MAKE_HALF_CST(C) vdupq_n_s32((int32_t)((C) >> 1)) +// note: B is actualy scale>>1. See MAKE_HALF_CST +#define MULT_FIX(A, B) \ + vreinterpretq_u32_s32(vqrdmulhq_s32(vreinterpretq_s32_u32((A)), (B))) +#define MULT_FIX_FLOOR(A, B) \ + vreinterpretq_u32_s32(vqdmulhq_s32(vreinterpretq_s32_u32((A)), (B))) +#else +#error "MULT_FIX/WEBP_RESCALER_RFIX need some more work" +#endif + +static uint32x4_t Interpolate_NEON(const rescaler_t* const frow, + const rescaler_t* const irow, + uint32_t A, uint32_t B) { + LOAD_32x4(frow, A0); + LOAD_32x4(irow, B0); + const uint64x2_t C0 = vmull_n_u32(vget_low_u32(A0), A); + const uint64x2_t C1 = vmull_n_u32(vget_high_u32(A0), A); + const uint64x2_t D0 = vmlal_n_u32(C0, vget_low_u32(B0), B); + const uint64x2_t D1 = vmlal_n_u32(C1, vget_high_u32(B0), B); + const uint32x4_t E = vcombine_u32( + vrshrn_n_u64(D0, WEBP_RESCALER_RFIX), + vrshrn_n_u64(D1, WEBP_RESCALER_RFIX)); + return E; +} + +static void RescalerExportRowExpand_NEON(WebPRescaler* const wrk) { + int x_out; + uint8_t* const dst = wrk->dst; + rescaler_t* const irow = wrk->irow; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const int max_span = x_out_max & ~7; + const rescaler_t* const frow = wrk->frow; + const uint32_t fy_scale = wrk->fy_scale; + const int32x4_t fy_scale_half = MAKE_HALF_CST(fy_scale); + assert(!WebPRescalerOutputDone(wrk)); + assert(wrk->y_accum <= 0); + assert(wrk->y_expand); + assert(wrk->y_sub != 0); + if (wrk->y_accum == 0) { + for (x_out = 0; x_out < max_span; x_out += 8) { + LOAD_32x4(frow + x_out + 0, A0); + LOAD_32x4(frow + x_out + 4, A1); + const uint32x4_t B0 = MULT_FIX(A0, fy_scale_half); + const uint32x4_t B1 = MULT_FIX(A1, fy_scale_half); + const uint16x4_t C0 = vmovn_u32(B0); + const uint16x4_t C1 = vmovn_u32(B1); + const uint8x8_t D = vqmovn_u16(vcombine_u16(C0, C1)); + vst1_u8(dst + x_out, D); + } + for (; x_out < x_out_max; ++x_out) { + const uint32_t J = frow[x_out]; + const int v = (int)MULT_FIX_C(J, fy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + } + } else { + const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); + const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B); + for (x_out = 0; x_out < max_span; x_out += 8) { + const uint32x4_t C0 = + Interpolate_NEON(frow + x_out + 0, irow + x_out + 0, A, B); + const uint32x4_t C1 = + Interpolate_NEON(frow + x_out + 4, irow + x_out + 4, A, B); + const uint32x4_t D0 = MULT_FIX(C0, fy_scale_half); + const uint32x4_t D1 = MULT_FIX(C1, fy_scale_half); + const uint16x4_t E0 = vmovn_u32(D0); + const uint16x4_t E1 = vmovn_u32(D1); + const uint8x8_t F = vqmovn_u16(vcombine_u16(E0, E1)); + vst1_u8(dst + x_out, F); + } + for (; x_out < x_out_max; ++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_C(J, fy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + } + } +} + +static void RescalerExportRowShrink_NEON(WebPRescaler* const wrk) { + int x_out; + uint8_t* const dst = wrk->dst; + rescaler_t* const irow = wrk->irow; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const int max_span = x_out_max & ~7; + const rescaler_t* const frow = wrk->frow; + const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum); + const uint32_t fxy_scale = wrk->fxy_scale; + const uint32x4_t zero = vdupq_n_u32(0); + const int32x4_t yscale_half = MAKE_HALF_CST(yscale); + const int32x4_t fxy_scale_half = MAKE_HALF_CST(fxy_scale); + assert(!WebPRescalerOutputDone(wrk)); + assert(wrk->y_accum <= 0); + assert(!wrk->y_expand); + if (yscale) { + for (x_out = 0; x_out < max_span; x_out += 8) { + LOAD_32x8(frow + x_out, in0, in1); + LOAD_32x8(irow + x_out, in2, in3); + const uint32x4_t A0 = MULT_FIX_FLOOR(in0, yscale_half); + const uint32x4_t A1 = MULT_FIX_FLOOR(in1, yscale_half); + const uint32x4_t B0 = vqsubq_u32(in2, A0); + const uint32x4_t B1 = vqsubq_u32(in3, A1); + const uint32x4_t C0 = MULT_FIX(B0, fxy_scale_half); + const uint32x4_t C1 = MULT_FIX(B1, fxy_scale_half); + const uint16x4_t D0 = vmovn_u32(C0); + const uint16x4_t D1 = vmovn_u32(C1); + const uint8x8_t E = vqmovn_u16(vcombine_u16(D0, D1)); + vst1_u8(dst + x_out, E); + STORE_32x8(A0, A1, irow + x_out); + } + for (; x_out < x_out_max; ++x_out) { + const uint32_t frac = (uint32_t)MULT_FIX_FLOOR_C(frow[x_out], yscale); + const int v = (int)MULT_FIX_C(irow[x_out] - frac, fxy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + irow[x_out] = frac; // new fractional start + } + } else { + for (x_out = 0; x_out < max_span; x_out += 8) { + LOAD_32x8(irow + x_out, in0, in1); + const uint32x4_t A0 = MULT_FIX(in0, fxy_scale_half); + const uint32x4_t A1 = MULT_FIX(in1, fxy_scale_half); + const uint16x4_t B0 = vmovn_u32(A0); + const uint16x4_t B1 = vmovn_u32(A1); + const uint8x8_t C = vqmovn_u16(vcombine_u16(B0, B1)); + vst1_u8(dst + x_out, C); + STORE_32x8(zero, zero, irow + x_out); + } + for (; x_out < x_out_max; ++x_out) { + const int v = (int)MULT_FIX_C(irow[x_out], fxy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + irow[x_out] = 0; + } + } +} + +#undef MULT_FIX_FLOOR_C +#undef MULT_FIX_C +#undef MULT_FIX_FLOOR +#undef MULT_FIX +#undef ROUNDER + +//------------------------------------------------------------------------------ + +extern void WebPRescalerDspInitNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitNEON(void) { + WebPRescalerExportRowExpand = RescalerExportRowExpand_NEON; + WebPRescalerExportRowShrink = RescalerExportRowShrink_NEON; +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(WebPRescalerDspInitNEON) + +#endif // WEBP_USE_NEON diff --git a/internal/libwebp-1.2.1/src/dsp/rescaler_sse2.c b/internal/libwebp-1.2.1/src/dsp/rescaler_sse2.c new file mode 100644 index 0000000..d7effea --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/rescaler_sse2.c @@ -0,0 +1,366 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE2 Rescaling functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) && !defined(WEBP_REDUCE_SIZE) +#include + +#include +#include "src/utils/rescaler_utils.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ +// Implementations of critical functions ImportRow / ExportRow + +#define ROUNDER (WEBP_RESCALER_ONE >> 1) +#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX) +#define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX) + +// input: 8 bytes ABCDEFGH -> output: A0E0B0F0C0G0D0H0 +static void LoadTwoPixels_SSE2(const uint8_t* const src, __m128i* out) { + const __m128i zero = _mm_setzero_si128(); + const __m128i A = _mm_loadl_epi64((const __m128i*)(src)); // ABCDEFGH + const __m128i B = _mm_unpacklo_epi8(A, zero); // A0B0C0D0E0F0G0H0 + const __m128i C = _mm_srli_si128(B, 8); // E0F0G0H0 + *out = _mm_unpacklo_epi16(B, C); +} + +// input: 8 bytes ABCDEFGH -> output: A0B0C0D0E0F0G0H0 +static void LoadEightPixels_SSE2(const uint8_t* const src, __m128i* out) { + const __m128i zero = _mm_setzero_si128(); + const __m128i A = _mm_loadl_epi64((const __m128i*)(src)); // ABCDEFGH + *out = _mm_unpacklo_epi8(A, zero); +} + +static void RescalerImportRowExpand_SSE2(WebPRescaler* const wrk, + const uint8_t* src) { + rescaler_t* frow = wrk->frow; + const rescaler_t* const frow_end = frow + wrk->dst_width * wrk->num_channels; + const int x_add = wrk->x_add; + int accum = x_add; + __m128i cur_pixels; + + // SSE2 implementation only works with 16b signed arithmetic at max. + if (wrk->src_width < 8 || accum >= (1 << 15)) { + WebPRescalerImportRowExpand_C(wrk, src); + return; + } + + assert(!WebPRescalerInputDone(wrk)); + assert(wrk->x_expand); + if (wrk->num_channels == 4) { + LoadTwoPixels_SSE2(src, &cur_pixels); + src += 4; + while (1) { + const __m128i mult = _mm_set1_epi32(((x_add - accum) << 16) | accum); + const __m128i out = _mm_madd_epi16(cur_pixels, mult); + _mm_storeu_si128((__m128i*)frow, out); + frow += 4; + if (frow >= frow_end) break; + accum -= wrk->x_sub; + if (accum < 0) { + LoadTwoPixels_SSE2(src, &cur_pixels); + src += 4; + accum += x_add; + } + } + } else { + int left; + const uint8_t* const src_limit = src + wrk->src_width - 8; + LoadEightPixels_SSE2(src, &cur_pixels); + src += 7; + left = 7; + while (1) { + const __m128i mult = _mm_cvtsi32_si128(((x_add - accum) << 16) | accum); + const __m128i out = _mm_madd_epi16(cur_pixels, mult); + assert(sizeof(*frow) == sizeof(uint32_t)); + WebPUint32ToMem((uint8_t*)frow, _mm_cvtsi128_si32(out)); + frow += 1; + if (frow >= frow_end) break; + accum -= wrk->x_sub; + if (accum < 0) { + if (--left) { + cur_pixels = _mm_srli_si128(cur_pixels, 2); + } else if (src <= src_limit) { + LoadEightPixels_SSE2(src, &cur_pixels); + src += 7; + left = 7; + } else { // tail + cur_pixels = _mm_srli_si128(cur_pixels, 2); + cur_pixels = _mm_insert_epi16(cur_pixels, src[1], 1); + src += 1; + left = 1; + } + accum += x_add; + } + } + } + assert(accum == 0); +} + +static void RescalerImportRowShrink_SSE2(WebPRescaler* const wrk, + const uint8_t* src) { + const int x_sub = wrk->x_sub; + int accum = 0; + const __m128i zero = _mm_setzero_si128(); + const __m128i mult0 = _mm_set1_epi16(x_sub); + const __m128i mult1 = _mm_set1_epi32(wrk->fx_scale); + const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER); + __m128i sum = zero; + rescaler_t* frow = wrk->frow; + const rescaler_t* const frow_end = wrk->frow + 4 * wrk->dst_width; + + if (wrk->num_channels != 4 || wrk->x_add > (x_sub << 7)) { + WebPRescalerImportRowShrink_C(wrk, src); + return; + } + assert(!WebPRescalerInputDone(wrk)); + assert(!wrk->x_expand); + + for (; frow < frow_end; frow += 4) { + __m128i base = zero; + accum += wrk->x_add; + while (accum > 0) { + const __m128i A = _mm_cvtsi32_si128(WebPMemToUint32(src)); + src += 4; + base = _mm_unpacklo_epi8(A, zero); + // To avoid overflow, we need: base * x_add / x_sub < 32768 + // => x_add < x_sub << 7. That's a 1/128 reduction ratio limit. + sum = _mm_add_epi16(sum, base); + accum -= x_sub; + } + { // Emit next horizontal pixel. + const __m128i mult = _mm_set1_epi16(-accum); + const __m128i frac0 = _mm_mullo_epi16(base, mult); // 16b x 16b -> 32b + const __m128i frac1 = _mm_mulhi_epu16(base, mult); + const __m128i frac = _mm_unpacklo_epi16(frac0, frac1); // frac is 32b + const __m128i A0 = _mm_mullo_epi16(sum, mult0); + const __m128i A1 = _mm_mulhi_epu16(sum, mult0); + const __m128i B0 = _mm_unpacklo_epi16(A0, A1); // sum * x_sub + const __m128i frow_out = _mm_sub_epi32(B0, frac); // sum * x_sub - frac + const __m128i D0 = _mm_srli_epi64(frac, 32); + const __m128i D1 = _mm_mul_epu32(frac, mult1); // 32b x 16b -> 64b + const __m128i D2 = _mm_mul_epu32(D0, mult1); + const __m128i E1 = _mm_add_epi64(D1, rounder); + const __m128i E2 = _mm_add_epi64(D2, rounder); + const __m128i F1 = _mm_shuffle_epi32(E1, 1 | (3 << 2)); + const __m128i F2 = _mm_shuffle_epi32(E2, 1 | (3 << 2)); + const __m128i G = _mm_unpacklo_epi32(F1, F2); + sum = _mm_packs_epi32(G, zero); + _mm_storeu_si128((__m128i*)frow, frow_out); + } + } + assert(accum == 0); +} + +//------------------------------------------------------------------------------ +// Row export + +// load *src as epi64, multiply by mult and store result in [out0 ... out3] +static WEBP_INLINE void LoadDispatchAndMult_SSE2(const rescaler_t* const src, + const __m128i* const mult, + __m128i* const out0, + __m128i* const out1, + __m128i* const out2, + __m128i* const out3) { + const __m128i A0 = _mm_loadu_si128((const __m128i*)(src + 0)); + const __m128i A1 = _mm_loadu_si128((const __m128i*)(src + 4)); + const __m128i A2 = _mm_srli_epi64(A0, 32); + const __m128i A3 = _mm_srli_epi64(A1, 32); + if (mult != NULL) { + *out0 = _mm_mul_epu32(A0, *mult); + *out1 = _mm_mul_epu32(A1, *mult); + *out2 = _mm_mul_epu32(A2, *mult); + *out3 = _mm_mul_epu32(A3, *mult); + } else { + *out0 = A0; + *out1 = A1; + *out2 = A2; + *out3 = A3; + } +} + +static WEBP_INLINE void ProcessRow_SSE2(const __m128i* const A0, + const __m128i* const A1, + const __m128i* const A2, + const __m128i* const A3, + const __m128i* const mult, + uint8_t* const dst) { + const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER); + const __m128i mask = _mm_set_epi32(0xffffffffu, 0, 0xffffffffu, 0); + const __m128i B0 = _mm_mul_epu32(*A0, *mult); + const __m128i B1 = _mm_mul_epu32(*A1, *mult); + const __m128i B2 = _mm_mul_epu32(*A2, *mult); + const __m128i B3 = _mm_mul_epu32(*A3, *mult); + const __m128i C0 = _mm_add_epi64(B0, rounder); + const __m128i C1 = _mm_add_epi64(B1, rounder); + const __m128i C2 = _mm_add_epi64(B2, rounder); + const __m128i C3 = _mm_add_epi64(B3, rounder); + const __m128i D0 = _mm_srli_epi64(C0, WEBP_RESCALER_RFIX); + const __m128i D1 = _mm_srli_epi64(C1, WEBP_RESCALER_RFIX); +#if (WEBP_RESCALER_RFIX < 32) + const __m128i D2 = + _mm_and_si128(_mm_slli_epi64(C2, 32 - WEBP_RESCALER_RFIX), mask); + const __m128i D3 = + _mm_and_si128(_mm_slli_epi64(C3, 32 - WEBP_RESCALER_RFIX), mask); +#else + const __m128i D2 = _mm_and_si128(C2, mask); + const __m128i D3 = _mm_and_si128(C3, mask); +#endif + const __m128i E0 = _mm_or_si128(D0, D2); + const __m128i E1 = _mm_or_si128(D1, D3); + const __m128i F = _mm_packs_epi32(E0, E1); + const __m128i G = _mm_packus_epi16(F, F); + _mm_storel_epi64((__m128i*)dst, G); +} + +static void RescalerExportRowExpand_SSE2(WebPRescaler* const wrk) { + int x_out; + uint8_t* const dst = wrk->dst; + rescaler_t* const irow = wrk->irow; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const rescaler_t* const frow = wrk->frow; + const __m128i mult = _mm_set_epi32(0, wrk->fy_scale, 0, wrk->fy_scale); + + assert(!WebPRescalerOutputDone(wrk)); + assert(wrk->y_accum <= 0 && wrk->y_sub + wrk->y_accum >= 0); + assert(wrk->y_expand); + if (wrk->y_accum == 0) { + for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) { + __m128i A0, A1, A2, A3; + LoadDispatchAndMult_SSE2(frow + x_out, NULL, &A0, &A1, &A2, &A3); + ProcessRow_SSE2(&A0, &A1, &A2, &A3, &mult, dst + x_out); + } + for (; x_out < x_out_max; ++x_out) { + const uint32_t J = frow[x_out]; + const int v = (int)MULT_FIX(J, wrk->fy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + } + } else { + const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub); + const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B); + const __m128i mA = _mm_set_epi32(0, A, 0, A); + const __m128i mB = _mm_set_epi32(0, B, 0, B); + const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER); + for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) { + __m128i A0, A1, A2, A3, B0, B1, B2, B3; + LoadDispatchAndMult_SSE2(frow + x_out, &mA, &A0, &A1, &A2, &A3); + LoadDispatchAndMult_SSE2(irow + x_out, &mB, &B0, &B1, &B2, &B3); + { + const __m128i C0 = _mm_add_epi64(A0, B0); + const __m128i C1 = _mm_add_epi64(A1, B1); + const __m128i C2 = _mm_add_epi64(A2, B2); + const __m128i C3 = _mm_add_epi64(A3, B3); + const __m128i D0 = _mm_add_epi64(C0, rounder); + const __m128i D1 = _mm_add_epi64(C1, rounder); + const __m128i D2 = _mm_add_epi64(C2, rounder); + const __m128i D3 = _mm_add_epi64(C3, rounder); + const __m128i E0 = _mm_srli_epi64(D0, WEBP_RESCALER_RFIX); + const __m128i E1 = _mm_srli_epi64(D1, WEBP_RESCALER_RFIX); + const __m128i E2 = _mm_srli_epi64(D2, WEBP_RESCALER_RFIX); + const __m128i E3 = _mm_srli_epi64(D3, WEBP_RESCALER_RFIX); + ProcessRow_SSE2(&E0, &E1, &E2, &E3, &mult, dst + x_out); + } + } + for (; x_out < x_out_max; ++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); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + } + } +} + +static void RescalerExportRowShrink_SSE2(WebPRescaler* const wrk) { + int x_out; + uint8_t* const dst = wrk->dst; + rescaler_t* const irow = wrk->irow; + const int x_out_max = wrk->dst_width * wrk->num_channels; + const rescaler_t* const 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) { + const int scale_xy = wrk->fxy_scale; + const __m128i mult_xy = _mm_set_epi32(0, scale_xy, 0, scale_xy); + const __m128i mult_y = _mm_set_epi32(0, yscale, 0, yscale); + for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) { + __m128i A0, A1, A2, A3, B0, B1, B2, B3; + LoadDispatchAndMult_SSE2(irow + x_out, NULL, &A0, &A1, &A2, &A3); + LoadDispatchAndMult_SSE2(frow + x_out, &mult_y, &B0, &B1, &B2, &B3); + { + const __m128i D0 = _mm_srli_epi64(B0, WEBP_RESCALER_RFIX); // = frac + const __m128i D1 = _mm_srli_epi64(B1, WEBP_RESCALER_RFIX); + const __m128i D2 = _mm_srli_epi64(B2, WEBP_RESCALER_RFIX); + const __m128i D3 = _mm_srli_epi64(B3, WEBP_RESCALER_RFIX); + const __m128i E0 = _mm_sub_epi64(A0, D0); // irow[x] - frac + const __m128i E1 = _mm_sub_epi64(A1, D1); + const __m128i E2 = _mm_sub_epi64(A2, D2); + const __m128i E3 = _mm_sub_epi64(A3, D3); + const __m128i F2 = _mm_slli_epi64(D2, 32); + const __m128i F3 = _mm_slli_epi64(D3, 32); + const __m128i G0 = _mm_or_si128(D0, F2); + const __m128i G1 = _mm_or_si128(D1, F3); + _mm_storeu_si128((__m128i*)(irow + x_out + 0), G0); + _mm_storeu_si128((__m128i*)(irow + x_out + 4), G1); + ProcessRow_SSE2(&E0, &E1, &E2, &E3, &mult_xy, dst + x_out); + } + } + for (; x_out < x_out_max; ++x_out) { + const uint32_t frac = (int)MULT_FIX_FLOOR(frow[x_out], yscale); + const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + irow[x_out] = frac; // new fractional start + } + } else { + const uint32_t scale = wrk->fxy_scale; + const __m128i mult = _mm_set_epi32(0, scale, 0, scale); + const __m128i zero = _mm_setzero_si128(); + for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) { + __m128i A0, A1, A2, A3; + LoadDispatchAndMult_SSE2(irow + x_out, NULL, &A0, &A1, &A2, &A3); + _mm_storeu_si128((__m128i*)(irow + x_out + 0), zero); + _mm_storeu_si128((__m128i*)(irow + x_out + 4), zero); + ProcessRow_SSE2(&A0, &A1, &A2, &A3, &mult, dst + x_out); + } + for (; x_out < x_out_max; ++x_out) { + const int v = (int)MULT_FIX(irow[x_out], scale); + dst[x_out] = (v > 255) ? 255u : (uint8_t)v; + irow[x_out] = 0; + } + } +} + +#undef MULT_FIX_FLOOR +#undef MULT_FIX +#undef ROUNDER + +//------------------------------------------------------------------------------ + +extern void WebPRescalerDspInitSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitSSE2(void) { + WebPRescalerImportRowExpand = RescalerImportRowExpand_SSE2; + WebPRescalerImportRowShrink = RescalerImportRowShrink_SSE2; + WebPRescalerExportRowExpand = RescalerExportRowExpand_SSE2; + WebPRescalerExportRowShrink = RescalerExportRowShrink_SSE2; +} + +#else // !WEBP_USE_SSE2 + +WEBP_DSP_INIT_STUB(WebPRescalerDspInitSSE2) + +#endif // WEBP_USE_SSE2 diff --git a/internal/libwebp-1.2.1/src/dsp/ssim.c b/internal/libwebp-1.2.1/src/dsp/ssim.c new file mode 100644 index 0000000..f85c2e6 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/ssim.c @@ -0,0 +1,159 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// distortion calculation +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include // for abs() + +#include "src/dsp/dsp.h" + +#if !defined(WEBP_REDUCE_SIZE) + +//------------------------------------------------------------------------------ +// SSIM / PSNR + +// 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; + const int xmin = (xo - VP8_SSIM_KERNEL < 0) ? 0 : xo - VP8_SSIM_KERNEL; + const int xmax = (xo + VP8_SSIM_KERNEL > W - 1) ? W - 1 + : xo + VP8_SSIM_KERNEL; + int x, y; + src1 += ymin * stride1; + src2 += ymin * stride2; + for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) { + for (x = xmin; x <= xmax; ++x) { + 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 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 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); +} + +#endif // !defined(WEBP_REDUCE_SIZE) + +//------------------------------------------------------------------------------ + +#if !defined(WEBP_DISABLE_STATS) +static uint32_t AccumulateSSE_C(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; +} +#endif + +//------------------------------------------------------------------------------ + +#if !defined(WEBP_REDUCE_SIZE) +VP8SSIMGetFunc VP8SSIMGet; +VP8SSIMGetClippedFunc VP8SSIMGetClipped; +#endif +#if !defined(WEBP_DISABLE_STATS) +VP8AccumulateSSEFunc VP8AccumulateSSE; +#endif + +extern void VP8SSIMDspInitSSE2(void); + +WEBP_DSP_INIT_FUNC(VP8SSIMDspInit) { +#if !defined(WEBP_REDUCE_SIZE) + VP8SSIMGetClipped = SSIMGetClipped_C; + VP8SSIMGet = SSIMGet_C; +#endif + +#if !defined(WEBP_DISABLE_STATS) + VP8AccumulateSSE = AccumulateSSE_C; +#endif + + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + VP8SSIMDspInitSSE2(); + } +#endif + } +} diff --git a/internal/libwebp-1.2.1/src/dsp/ssim_sse2.c b/internal/libwebp-1.2.1/src/dsp/ssim_sse2.c new file mode 100644 index 0000000..1dcb0eb --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/ssim_sse2.c @@ -0,0 +1,165 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE2 version of distortion calculation +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) + +#include +#include + +#include "src/dsp/common_sse2.h" + +#if !defined(WEBP_DISABLE_STATS) + +// Helper function +static WEBP_INLINE void SubtractAndSquare_SSE2(const __m128i a, const __m128i b, + __m128i* const sum) { + // take abs(a-b) in 8b + const __m128i a_b = _mm_subs_epu8(a, b); + const __m128i b_a = _mm_subs_epu8(b, a); + const __m128i abs_a_b = _mm_or_si128(a_b, b_a); + // zero-extend to 16b + const __m128i zero = _mm_setzero_si128(); + const __m128i C0 = _mm_unpacklo_epi8(abs_a_b, zero); + const __m128i C1 = _mm_unpackhi_epi8(abs_a_b, zero); + // multiply with self + const __m128i sum1 = _mm_madd_epi16(C0, C0); + const __m128i sum2 = _mm_madd_epi16(C1, C1); + *sum = _mm_add_epi32(sum1, sum2); +} + +//------------------------------------------------------------------------------ +// SSIM / PSNR entry point + +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; + SubtractAndSquare_SSE2(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; + SubtractAndSquare_SSE2(a1, b1, &sum2); + sum = _mm_add_epi32(sum, sum2); + } + SubtractAndSquare_SSE2(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; +} +#endif // !defined(WEBP_DISABLE_STATS) + +#if !defined(WEBP_REDUCE_SIZE) + +static uint32_t HorizontalAdd16b_SSE2(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_SSE2(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_SSE2(&xm); + stats.ym = HorizontalAdd16b_SSE2(&ym); + stats.xxm = HorizontalAdd32b_SSE2(&xxm); + stats.xym = HorizontalAdd32b_SSE2(&xym); + stats.yym = HorizontalAdd32b_SSE2(&yym); + return VP8SSIMFromStats(&stats); +} + +#endif // !defined(WEBP_REDUCE_SIZE) + +extern void VP8SSIMDspInitSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8SSIMDspInitSSE2(void) { +#if !defined(WEBP_DISABLE_STATS) + VP8AccumulateSSE = AccumulateSSE_SSE2; +#endif +#if !defined(WEBP_REDUCE_SIZE) + VP8SSIMGet = SSIMGet_SSE2; +#endif +} + +#else // !WEBP_USE_SSE2 + +WEBP_DSP_INIT_STUB(VP8SSIMDspInitSSE2) + +#endif // WEBP_USE_SSE2 diff --git a/internal/libwebp-1.2.1/src/dsp/upsampling.c b/internal/libwebp-1.2.1/src/dsp/upsampling.c new file mode 100644 index 0000000..87f771f --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/upsampling.c @@ -0,0 +1,327 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// YUV to RGB upsampling functions. +// +// Author: somnath@google.com (Somnath Banerjee) + +#include "src/dsp/dsp.h" +#include "src/dsp/yuv.h" + +#include + +//------------------------------------------------------------------------------ +// Fancy upsampler + +#ifdef FANCY_UPSAMPLING + +// Fancy upsampling functions to convert YUV to RGB +WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST]; + +// Given samples laid out in a square as: +// [a b] +// [c d] +// we interpolate u/v as: +// ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16 +// ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16 + +// We process u and v together stashed into 32bit (16bit each). +#define LOAD_UV(u, v) ((u) | ((v) << 16)) + +#define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \ +static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_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* bottom_dst, int len) { \ + int x; \ + const int last_pixel_pair = (len - 1) >> 1; \ + uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \ + uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \ + assert(top_y != NULL); \ + { \ + const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ + FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \ + } \ + if (bottom_y != NULL) { \ + const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ + FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \ + } \ + for (x = 1; x <= last_pixel_pair; ++x) { \ + const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \ + const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \ + /* precompute invariant values associated with first and second diagonals*/\ + const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \ + const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \ + const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \ + { \ + const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \ + const uint32_t uv1 = (diag_03 + t_uv) >> 1; \ + FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \ + top_dst + (2 * x - 1) * (XSTEP)); \ + FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \ + top_dst + (2 * x - 0) * (XSTEP)); \ + } \ + if (bottom_y != NULL) { \ + const uint32_t uv0 = (diag_03 + l_uv) >> 1; \ + const uint32_t uv1 = (diag_12 + uv) >> 1; \ + FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \ + bottom_dst + (2 * x - 1) * (XSTEP)); \ + FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \ + bottom_dst + (2 * x + 0) * (XSTEP)); \ + } \ + tl_uv = t_uv; \ + l_uv = uv; \ + } \ + if (!(len & 1)) { \ + { \ + const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ + FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \ + top_dst + (len - 1) * (XSTEP)); \ + } \ + if (bottom_y != NULL) { \ + const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ + FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \ + bottom_dst + (len - 1) * (XSTEP)); \ + } \ + } \ +} + +// All variants implemented. +#if !WEBP_NEON_OMIT_C_CODE +UPSAMPLE_FUNC(UpsampleRgbaLinePair_C, VP8YuvToRgba, 4) +UPSAMPLE_FUNC(UpsampleBgraLinePair_C, VP8YuvToBgra, 4) +#if !defined(WEBP_REDUCE_CSP) +UPSAMPLE_FUNC(UpsampleArgbLinePair_C, VP8YuvToArgb, 4) +UPSAMPLE_FUNC(UpsampleRgbLinePair_C, VP8YuvToRgb, 3) +UPSAMPLE_FUNC(UpsampleBgrLinePair_C, VP8YuvToBgr, 3) +UPSAMPLE_FUNC(UpsampleRgba4444LinePair_C, VP8YuvToRgba4444, 2) +UPSAMPLE_FUNC(UpsampleRgb565LinePair_C, VP8YuvToRgb565, 2) +#else +static void EmptyUpsampleFunc(const uint8_t* top_y, const uint8_t* bottom_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* bottom_dst, int len) { + (void)top_y; + (void)bottom_y; + (void)top_u; + (void)top_v; + (void)cur_u; + (void)cur_v; + (void)top_dst; + (void)bottom_dst; + (void)len; + assert(0); // COLORSPACE SUPPORT NOT COMPILED +} +#define UpsampleArgbLinePair_C EmptyUpsampleFunc +#define UpsampleRgbLinePair_C EmptyUpsampleFunc +#define UpsampleBgrLinePair_C EmptyUpsampleFunc +#define UpsampleRgba4444LinePair_C EmptyUpsampleFunc +#define UpsampleRgb565LinePair_C EmptyUpsampleFunc +#endif // WEBP_REDUCE_CSP + +#endif + +#undef LOAD_UV +#undef UPSAMPLE_FUNC + +#endif // FANCY_UPSAMPLING + +//------------------------------------------------------------------------------ + +#if !defined(FANCY_UPSAMPLING) +#define DUAL_SAMPLE_FUNC(FUNC_NAME, FUNC) \ +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* bot_u, const uint8_t* bot_v, \ + uint8_t* top_dst, uint8_t* bot_dst, int len) { \ + const int half_len = len >> 1; \ + int x; \ + assert(top_dst != NULL); \ + { \ + for (x = 0; x < half_len; ++x) { \ + FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x + 0); \ + FUNC(top_y[2 * x + 1], top_u[x], top_v[x], top_dst + 8 * x + 4); \ + } \ + if (len & 1) FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x); \ + } \ + if (bot_dst != NULL) { \ + for (x = 0; x < half_len; ++x) { \ + FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x + 0); \ + FUNC(bot_y[2 * x + 1], bot_u[x], bot_v[x], bot_dst + 8 * x + 4); \ + } \ + if (len & 1) FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x); \ + } \ +} + +DUAL_SAMPLE_FUNC(DualLineSamplerBGRA, VP8YuvToBgra) +DUAL_SAMPLE_FUNC(DualLineSamplerARGB, VP8YuvToArgb) +#undef DUAL_SAMPLE_FUNC + +#endif // !FANCY_UPSAMPLING + +WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last) { + WebPInitUpsamplers(); +#ifdef FANCY_UPSAMPLING + return WebPUpsamplers[alpha_is_last ? MODE_BGRA : MODE_ARGB]; +#else + return (alpha_is_last ? DualLineSamplerBGRA : DualLineSamplerARGB); +#endif +} + +//------------------------------------------------------------------------------ +// YUV444 converter + +#define YUV444_FUNC(FUNC_NAME, FUNC, XSTEP) \ +extern void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len); \ +void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len) { \ + int i; \ + for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * (XSTEP)]); \ +} + +YUV444_FUNC(WebPYuv444ToRgba_C, VP8YuvToRgba, 4) +YUV444_FUNC(WebPYuv444ToBgra_C, VP8YuvToBgra, 4) +#if !defined(WEBP_REDUCE_CSP) +YUV444_FUNC(WebPYuv444ToRgb_C, VP8YuvToRgb, 3) +YUV444_FUNC(WebPYuv444ToBgr_C, VP8YuvToBgr, 3) +YUV444_FUNC(WebPYuv444ToArgb_C, VP8YuvToArgb, 4) +YUV444_FUNC(WebPYuv444ToRgba4444_C, VP8YuvToRgba4444, 2) +YUV444_FUNC(WebPYuv444ToRgb565_C, VP8YuvToRgb565, 2) +#else +static void EmptyYuv444Func(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len) { + (void)y; + (void)u; + (void)v; + (void)dst; + (void)len; +} +#define WebPYuv444ToRgb_C EmptyYuv444Func +#define WebPYuv444ToBgr_C EmptyYuv444Func +#define WebPYuv444ToArgb_C EmptyYuv444Func +#define WebPYuv444ToRgba4444_C EmptyYuv444Func +#define WebPYuv444ToRgb565_C EmptyYuv444Func +#endif // WEBP_REDUCE_CSP + +#undef YUV444_FUNC + +WebPYUV444Converter WebPYUV444Converters[MODE_LAST]; + +extern void WebPInitYUV444ConvertersMIPSdspR2(void); +extern void WebPInitYUV444ConvertersSSE2(void); +extern void WebPInitYUV444ConvertersSSE41(void); + +WEBP_DSP_INIT_FUNC(WebPInitYUV444Converters) { + WebPYUV444Converters[MODE_RGBA] = WebPYuv444ToRgba_C; + WebPYUV444Converters[MODE_BGRA] = WebPYuv444ToBgra_C; + WebPYUV444Converters[MODE_RGB] = WebPYuv444ToRgb_C; + WebPYUV444Converters[MODE_BGR] = WebPYuv444ToBgr_C; + WebPYUV444Converters[MODE_ARGB] = WebPYuv444ToArgb_C; + WebPYUV444Converters[MODE_RGBA_4444] = WebPYuv444ToRgba4444_C; + WebPYUV444Converters[MODE_RGB_565] = WebPYuv444ToRgb565_C; + WebPYUV444Converters[MODE_rgbA] = WebPYuv444ToRgba_C; + WebPYUV444Converters[MODE_bgrA] = WebPYuv444ToBgra_C; + WebPYUV444Converters[MODE_Argb] = WebPYuv444ToArgb_C; + WebPYUV444Converters[MODE_rgbA_4444] = WebPYuv444ToRgba4444_C; + + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + WebPInitYUV444ConvertersSSE2(); + } +#endif +#if defined(WEBP_HAVE_SSE41) + if (VP8GetCPUInfo(kSSE4_1)) { + WebPInitYUV444ConvertersSSE41(); + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + WebPInitYUV444ConvertersMIPSdspR2(); + } +#endif + } +} + +//------------------------------------------------------------------------------ +// Main calls + +extern void WebPInitUpsamplersSSE2(void); +extern void WebPInitUpsamplersSSE41(void); +extern void WebPInitUpsamplersNEON(void); +extern void WebPInitUpsamplersMIPSdspR2(void); +extern void WebPInitUpsamplersMSA(void); + +WEBP_DSP_INIT_FUNC(WebPInitUpsamplers) { +#ifdef FANCY_UPSAMPLING +#if !WEBP_NEON_OMIT_C_CODE + WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair_C; + WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair_C; + WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair_C; + WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair_C; + WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair_C; + WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair_C; + WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair_C; + WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair_C; + WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair_C; + WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair_C; + WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair_C; +#endif + + // If defined, use CPUInfo() to overwrite some pointers with faster versions. + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + WebPInitUpsamplersSSE2(); + } +#endif +#if defined(WEBP_HAVE_SSE41) + if (VP8GetCPUInfo(kSSE4_1)) { + WebPInitUpsamplersSSE41(); + } +#endif +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + WebPInitUpsamplersMIPSdspR2(); + } +#endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + WebPInitUpsamplersMSA(); + } +#endif + } + +#if defined(WEBP_HAVE_NEON) + if (WEBP_NEON_OMIT_C_CODE || + (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) { + WebPInitUpsamplersNEON(); + } +#endif + + assert(WebPUpsamplers[MODE_RGBA] != NULL); + assert(WebPUpsamplers[MODE_BGRA] != NULL); + assert(WebPUpsamplers[MODE_rgbA] != NULL); + assert(WebPUpsamplers[MODE_bgrA] != NULL); +#if !defined(WEBP_REDUCE_CSP) || !WEBP_NEON_OMIT_C_CODE + assert(WebPUpsamplers[MODE_RGB] != NULL); + assert(WebPUpsamplers[MODE_BGR] != NULL); + assert(WebPUpsamplers[MODE_ARGB] != NULL); + assert(WebPUpsamplers[MODE_RGBA_4444] != NULL); + assert(WebPUpsamplers[MODE_RGB_565] != NULL); + assert(WebPUpsamplers[MODE_Argb] != NULL); + assert(WebPUpsamplers[MODE_rgbA_4444] != NULL); +#endif + +#endif // FANCY_UPSAMPLING +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/src/dsp/upsampling_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/upsampling_mips_dsp_r2.c new file mode 100644 index 0000000..10d499d --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/upsampling_mips_dsp_r2.c @@ -0,0 +1,291 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// YUV to RGB upsampling functions. +// +// Author(s): Branimir Vasic (branimir.vasic@imgtec.com) +// Djordje Pesut (djordje.pesut@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) + +#include +#include "src/dsp/yuv.h" + +#define YUV_TO_RGB(Y, U, V, R, G, B) do { \ + const int t1 = MultHi(Y, 19077); \ + const int t2 = MultHi(V, 13320); \ + R = MultHi(V, 26149); \ + G = MultHi(U, 6419); \ + B = MultHi(U, 33050); \ + R = t1 + R; \ + G = t1 - G; \ + B = t1 + B; \ + R = R - 14234; \ + G = G - t2 + 8708; \ + B = B - 17685; \ + __asm__ volatile ( \ + "shll_s.w %[" #R "], %[" #R "], 17 \n\t" \ + "shll_s.w %[" #G "], %[" #G "], 17 \n\t" \ + "shll_s.w %[" #B "], %[" #B "], 17 \n\t" \ + "precrqu_s.qb.ph %[" #R "], %[" #R "], $zero \n\t" \ + "precrqu_s.qb.ph %[" #G "], %[" #G "], $zero \n\t" \ + "precrqu_s.qb.ph %[" #B "], %[" #B "], $zero \n\t" \ + "srl %[" #R "], %[" #R "], 24 \n\t" \ + "srl %[" #G "], %[" #G "], 24 \n\t" \ + "srl %[" #B "], %[" #B "], 24 \n\t" \ + : [R]"+r"(R), [G]"+r"(G), [B]"+r"(B) \ + : \ + ); \ + } while (0) + +#if !defined(WEBP_REDUCE_CSP) +static WEBP_INLINE void YuvToRgb(int y, int u, int v, uint8_t* const rgb) { + int r, g, b; + YUV_TO_RGB(y, u, v, r, g, b); + rgb[0] = r; + rgb[1] = g; + rgb[2] = b; +} +static WEBP_INLINE void YuvToBgr(int y, int u, int v, uint8_t* const bgr) { + int r, g, b; + YUV_TO_RGB(y, u, v, r, g, b); + bgr[0] = b; + bgr[1] = g; + bgr[2] = r; +} +static WEBP_INLINE void YuvToRgb565(int y, int u, int v, uint8_t* const rgb) { + int r, g, b; + YUV_TO_RGB(y, u, v, r, g, b); + { + const int rg = (r & 0xf8) | (g >> 5); + const int gb = ((g << 3) & 0xe0) | (b >> 3); +#if (WEBP_SWAP_16BIT_CSP == 1) + rgb[0] = gb; + rgb[1] = rg; +#else + rgb[0] = rg; + rgb[1] = gb; +#endif + } +} +static WEBP_INLINE void YuvToRgba4444(int y, int u, int v, + uint8_t* const argb) { + int r, g, b; + YUV_TO_RGB(y, u, v, r, g, b); + { + const int rg = (r & 0xf0) | (g >> 4); + const int ba = (b & 0xf0) | 0x0f; // overwrite the lower 4 bits +#if (WEBP_SWAP_16BIT_CSP == 1) + argb[0] = ba; + argb[1] = rg; +#else + argb[0] = rg; + argb[1] = ba; +#endif + } +} +#endif // WEBP_REDUCE_CSP + +//----------------------------------------------------------------------------- +// Alpha handling variants + +#if !defined(WEBP_REDUCE_CSP) +static WEBP_INLINE void YuvToArgb(uint8_t y, uint8_t u, uint8_t v, + uint8_t* const argb) { + int r, g, b; + YUV_TO_RGB(y, u, v, r, g, b); + argb[0] = 0xff; + argb[1] = r; + argb[2] = g; + argb[3] = b; +} +#endif // WEBP_REDUCE_CSP +static WEBP_INLINE void YuvToBgra(uint8_t y, uint8_t u, uint8_t v, + uint8_t* const bgra) { + int r, g, b; + YUV_TO_RGB(y, u, v, r, g, b); + bgra[0] = b; + bgra[1] = g; + bgra[2] = r; + bgra[3] = 0xff; +} +static WEBP_INLINE void YuvToRgba(uint8_t y, uint8_t u, uint8_t v, + uint8_t* const rgba) { + int r, g, b; + YUV_TO_RGB(y, u, v, r, g, b); + rgba[0] = r; + rgba[1] = g; + rgba[2] = b; + rgba[3] = 0xff; +} + +//------------------------------------------------------------------------------ +// Fancy upsampler + +#ifdef FANCY_UPSAMPLING + +// Given samples laid out in a square as: +// [a b] +// [c d] +// we interpolate u/v as: +// ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16 +// ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16 + +// We process u and v together stashed into 32bit (16bit each). +#define LOAD_UV(u, v) ((u) | ((v) << 16)) + +#define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \ +static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_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* bottom_dst, int len) { \ + int x; \ + const int last_pixel_pair = (len - 1) >> 1; \ + uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \ + uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \ + assert(top_y != NULL); \ + { \ + const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ + FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \ + } \ + if (bottom_y != NULL) { \ + const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ + FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \ + } \ + for (x = 1; x <= last_pixel_pair; ++x) { \ + const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \ + const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \ + /* precompute invariant values associated with first and second diagonals*/\ + const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \ + const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \ + const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \ + { \ + const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \ + const uint32_t uv1 = (diag_03 + t_uv) >> 1; \ + FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \ + top_dst + (2 * x - 1) * XSTEP); \ + FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \ + top_dst + (2 * x - 0) * XSTEP); \ + } \ + if (bottom_y != NULL) { \ + const uint32_t uv0 = (diag_03 + l_uv) >> 1; \ + const uint32_t uv1 = (diag_12 + uv) >> 1; \ + FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \ + bottom_dst + (2 * x - 1) * XSTEP); \ + FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \ + bottom_dst + (2 * x + 0) * XSTEP); \ + } \ + tl_uv = t_uv; \ + l_uv = uv; \ + } \ + if (!(len & 1)) { \ + { \ + const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ + FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \ + top_dst + (len - 1) * XSTEP); \ + } \ + if (bottom_y != NULL) { \ + const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ + FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \ + bottom_dst + (len - 1) * XSTEP); \ + } \ + } \ +} + +// All variants implemented. +UPSAMPLE_FUNC(UpsampleRgbaLinePair, YuvToRgba, 4) +UPSAMPLE_FUNC(UpsampleBgraLinePair, YuvToBgra, 4) +#if !defined(WEBP_REDUCE_CSP) +UPSAMPLE_FUNC(UpsampleRgbLinePair, YuvToRgb, 3) +UPSAMPLE_FUNC(UpsampleBgrLinePair, YuvToBgr, 3) +UPSAMPLE_FUNC(UpsampleArgbLinePair, YuvToArgb, 4) +UPSAMPLE_FUNC(UpsampleRgba4444LinePair, YuvToRgba4444, 2) +UPSAMPLE_FUNC(UpsampleRgb565LinePair, YuvToRgb565, 2) +#endif // WEBP_REDUCE_CSP + +#undef LOAD_UV +#undef UPSAMPLE_FUNC + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPInitUpsamplersMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersMIPSdspR2(void) { + WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair; + WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair; + WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair; + WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair; +#if !defined(WEBP_REDUCE_CSP) + WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair; + WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair; + WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair; + WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair; + WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair; + WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair; + WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair; +#endif // WEBP_REDUCE_CSP +} + +#endif // FANCY_UPSAMPLING + +//------------------------------------------------------------------------------ +// YUV444 converter + +#define YUV444_FUNC(FUNC_NAME, FUNC, XSTEP) \ +static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len) { \ + int i; \ + for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * XSTEP]); \ +} + +YUV444_FUNC(Yuv444ToRgba, YuvToRgba, 4) +YUV444_FUNC(Yuv444ToBgra, YuvToBgra, 4) +#if !defined(WEBP_REDUCE_CSP) +YUV444_FUNC(Yuv444ToRgb, YuvToRgb, 3) +YUV444_FUNC(Yuv444ToBgr, YuvToBgr, 3) +YUV444_FUNC(Yuv444ToArgb, YuvToArgb, 4) +YUV444_FUNC(Yuv444ToRgba4444, YuvToRgba4444, 2) +YUV444_FUNC(Yuv444ToRgb565, YuvToRgb565, 2) +#endif // WEBP_REDUCE_CSP + +#undef YUV444_FUNC + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPInitYUV444ConvertersMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersMIPSdspR2(void) { + WebPYUV444Converters[MODE_RGBA] = Yuv444ToRgba; + WebPYUV444Converters[MODE_BGRA] = Yuv444ToBgra; + WebPYUV444Converters[MODE_rgbA] = Yuv444ToRgba; + WebPYUV444Converters[MODE_bgrA] = Yuv444ToBgra; +#if !defined(WEBP_REDUCE_CSP) + WebPYUV444Converters[MODE_RGB] = Yuv444ToRgb; + WebPYUV444Converters[MODE_BGR] = Yuv444ToBgr; + WebPYUV444Converters[MODE_ARGB] = Yuv444ToArgb; + WebPYUV444Converters[MODE_RGBA_4444] = Yuv444ToRgba4444; + WebPYUV444Converters[MODE_RGB_565] = Yuv444ToRgb565; + WebPYUV444Converters[MODE_Argb] = Yuv444ToArgb; + WebPYUV444Converters[MODE_rgbA_4444] = Yuv444ToRgba4444; +#endif // WEBP_REDUCE_CSP +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(WebPInitYUV444ConvertersMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 + +#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_MIPS_DSP_R2)) +WEBP_DSP_INIT_STUB(WebPInitUpsamplersMIPSdspR2) +#endif diff --git a/internal/libwebp-1.2.1/src/dsp/upsampling_msa.c b/internal/libwebp-1.2.1/src/dsp/upsampling_msa.c new file mode 100644 index 0000000..f2e03e8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/upsampling_msa.c @@ -0,0 +1,688 @@ +// 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 "src/dsp/dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "src/dsp/msa_macro.h" +#include "src/dsp/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); +} + +#if !defined(WEBP_REDUCE_CSP) +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); +#if (WEBP_SWAP_16BIT_CSP == 1) + 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 +#if (WEBP_SWAP_16BIT_CSP == 1) + 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); +} +#endif // WEBP_REDUCE_CSP + +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; +} + +#if !defined(WEBP_REDUCE_CSP) +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)); + } +} +#endif // WEBP_REDUCE_CSP + +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(ALPHAVAL); + 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(ALPHAVAL); + 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)); + } +} + +#if !defined(WEBP_REDUCE_CSP) +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(ALPHAVAL); + 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) { +#if (WEBP_SWAP_16BIT_CSP == 1) + 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)); +#if (WEBP_SWAP_16BIT_CSP == 1) + 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)); +#if (WEBP_SWAP_16BIT_CSP == 1) + 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) { +#if (WEBP_SWAP_16BIT_CSP == 1) + 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)); +#if (WEBP_SWAP_16BIT_CSP == 1) + 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)); +#if (WEBP_SWAP_16BIT_CSP == 1) + 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)); + } +} +#endif // WEBP_REDUCE_CSP + +#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(UpsampleRgbaLinePair, YuvToRgba, 4) +UPSAMPLE_FUNC(UpsampleBgraLinePair, YuvToBgra, 4) +#if !defined(WEBP_REDUCE_CSP) +UPSAMPLE_FUNC(UpsampleRgbLinePair, YuvToRgb, 3) +UPSAMPLE_FUNC(UpsampleBgrLinePair, YuvToBgr, 3) +UPSAMPLE_FUNC(UpsampleArgbLinePair, YuvToArgb, 4) +UPSAMPLE_FUNC(UpsampleRgba4444LinePair, YuvToRgba4444, 2) +UPSAMPLE_FUNC(UpsampleRgb565LinePair, YuvToRgb565, 2) +#endif // WEBP_REDUCE_CSP + +//------------------------------------------------------------------------------ +// Entry point + +extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; + +extern void WebPInitUpsamplersMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersMSA(void) { + WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair; + WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair; + WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair; + WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair; +#if !defined(WEBP_REDUCE_CSP) + WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair; + WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair; + WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair; + WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair; + WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair; + WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair; + WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair; +#endif // WEBP_REDUCE_CSP +} + +#endif // FANCY_UPSAMPLING + +#endif // WEBP_USE_MSA + +#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_MSA)) +WEBP_DSP_INIT_STUB(WebPInitUpsamplersMSA) +#endif diff --git a/internal/libwebp-1.2.1/src/dsp/upsampling_neon.c b/internal/libwebp-1.2.1/src/dsp/upsampling_neon.c new file mode 100644 index 0000000..6ba71a7 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/upsampling_neon.c @@ -0,0 +1,285 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// NEON version of YUV to RGB upsampling functions. +// +// Author: mans@mansr.com (Mans Rullgard) +// Based on SSE code by: somnath@google.com (Somnath Banerjee) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_NEON) + +#include +#include +#include +#include "src/dsp/neon.h" +#include "src/dsp/yuv.h" + +#ifdef FANCY_UPSAMPLING + +//----------------------------------------------------------------------------- +// U/V upsampling + +// Loads 9 pixels each from rows r1 and r2 and generates 16 pixels. +#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 */ \ + 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)); \ + \ + const uint8x8_t diag2 = vshrn_n_u16(al, 3); \ + const uint8x8_t diag1 = vshrn_n_u16(bl, 3); \ + \ + 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 + 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_NEON(const uint8_t* r1, const uint8_t* r2, + uint8_t* out) { + UPSAMPLE_16PIXELS(r1, r2, out); +} + +#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \ + uint8_t r1[9], r2[9]; \ + memcpy(r1, (tb), (num_pixels)); \ + memcpy(r2, (bb), (num_pixels)); \ + /* replicate last byte */ \ + memset(r1 + (num_pixels), r1[(num_pixels) - 1], 9 - (num_pixels)); \ + memset(r2 + (num_pixels), r2[(num_pixels) - 1], 9 - (num_pixels)); \ + Upsample16Pixels_NEON(r1, r2, out); \ +} + +//----------------------------------------------------------------------------- +// YUV->RGB conversion + +// note: we represent the 33050 large constant as 32768 + 282 +static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 }; + +#define v255 vdup_n_u8(255) + +#define STORE_Rgb(out, r, g, b) do { \ + uint8x8x3_t r_g_b; \ + INIT_VECTOR3(r_g_b, r, g, b); \ + vst3_u8(out, r_g_b); \ +} while (0) + +#define STORE_Bgr(out, r, g, b) do { \ + uint8x8x3_t b_g_r; \ + INIT_VECTOR3(b_g_r, b, g, r); \ + vst3_u8(out, b_g_r); \ +} while (0) + +#define STORE_Rgba(out, r, g, b) do { \ + uint8x8x4_t r_g_b_v255; \ + INIT_VECTOR4(r_g_b_v255, r, g, b, v255); \ + vst4_u8(out, r_g_b_v255); \ +} while (0) + +#define STORE_Bgra(out, r, g, b) do { \ + uint8x8x4_t b_g_r_v255; \ + INIT_VECTOR4(b_g_r_v255, b, g, r, v255); \ + vst4_u8(out, b_g_r_v255); \ +} while (0) + +#define STORE_Argb(out, r, g, b) do { \ + uint8x8x4_t v255_r_g_b; \ + INIT_VECTOR4(v255_r_g_b, v255, r, g, b); \ + vst4_u8(out, v255_r_g_b); \ +} while (0) + +#if !defined(WEBP_SWAP_16BIT_CSP) +#define ZIP_U8(lo, hi) vzip_u8((lo), (hi)) +#else +#define ZIP_U8(lo, hi) vzip_u8((hi), (lo)) +#endif + +#define STORE_Rgba4444(out, r, g, b) do { \ + 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 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) + +#define CONVERT8(FMT, XSTEP, N, src_y, src_uv, out, cur_x) do { \ + int i; \ + for (i = 0; i < N; i += 8) { \ + const int off = ((cur_x) + i) * XSTEP; \ + const uint8x8_t y = vld1_u8((src_y) + (cur_x) + i); \ + const uint8x8_t u = vld1_u8((src_uv) + i + 0); \ + const uint8x8_t v = vld1_u8((src_uv) + i + 16); \ + const int16x8_t Y0 = vreinterpretq_s16_u16(vshll_n_u8(y, 7)); \ + const int16x8_t U0 = vreinterpretq_s16_u16(vshll_n_u8(u, 7)); \ + const int16x8_t V0 = vreinterpretq_s16_u16(vshll_n_u8(v, 7)); \ + const int16x8_t Y1 = vqdmulhq_lane_s16(Y0, coeff1, 0); \ + const int16x8_t R0 = vqdmulhq_lane_s16(V0, coeff1, 1); \ + const int16x8_t G0 = vqdmulhq_lane_s16(U0, coeff1, 2); \ + const int16x8_t G1 = vqdmulhq_lane_s16(V0, coeff1, 3); \ + const int16x8_t B0 = vqdmulhq_n_s16(U0, 282); \ + const int16x8_t R1 = vqaddq_s16(Y1, R_Rounder); \ + const int16x8_t G2 = vqaddq_s16(Y1, G_Rounder); \ + const int16x8_t B1 = vqaddq_s16(Y1, B_Rounder); \ + const int16x8_t R2 = vqaddq_s16(R0, R1); \ + const int16x8_t G3 = vqaddq_s16(G0, G1); \ + const int16x8_t B2 = vqaddq_s16(B0, B1); \ + const int16x8_t G4 = vqsubq_s16(G2, G3); \ + const int16x8_t B3 = vqaddq_s16(B2, U0); \ + const uint8x8_t R = vqshrun_n_s16(R2, YUV_FIX2); \ + const uint8x8_t G = vqshrun_n_s16(G4, YUV_FIX2); \ + const uint8x8_t B = vqshrun_n_s16(B3, YUV_FIX2); \ + STORE_ ## FMT(out + off, R, G, B); \ + } \ +} while (0) + +#define CONVERT1(FUNC, XSTEP, N, src_y, src_uv, rgb, cur_x) { \ + int i; \ + for (i = 0; i < N; i++) { \ + const int off = ((cur_x) + i) * XSTEP; \ + const int y = src_y[(cur_x) + i]; \ + const int u = (src_uv)[i]; \ + const int v = (src_uv)[i + 16]; \ + FUNC(y, u, v, rgb + off); \ + } \ +} + +#define CONVERT2RGB_8(FMT, XSTEP, top_y, bottom_y, uv, \ + top_dst, bottom_dst, cur_x, len) { \ + CONVERT8(FMT, XSTEP, len, top_y, uv, top_dst, cur_x); \ + if (bottom_y != NULL) { \ + CONVERT8(FMT, XSTEP, len, bottom_y, (uv) + 32, bottom_dst, cur_x); \ + } \ +} + +#define CONVERT2RGB_1(FUNC, XSTEP, top_y, bottom_y, uv, \ + top_dst, bottom_dst, cur_x, len) { \ + CONVERT1(FUNC, XSTEP, len, top_y, uv, top_dst, cur_x); \ + if (bottom_y != NULL) { \ + CONVERT1(FUNC, XSTEP, len, bottom_y, (uv) + 32, bottom_dst, cur_x); \ + } \ +} + +#define NEON_UPSAMPLE_FUNC(FUNC_NAME, FMT, XSTEP) \ +static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_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* bottom_dst, int len) { \ + int block; \ + /* 16 byte aligned array to cache reconstructed u and v */ \ + uint8_t uv_buf[2 * 32 + 15]; \ + uint8_t* const r_uv = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \ + const int uv_len = (len + 1) >> 1; \ + /* 9 pixels must be read-able for each block */ \ + const int num_blocks = (uv_len - 1) >> 3; \ + const int leftover = uv_len - num_blocks * 8; \ + const int last_pos = 1 + 16 * num_blocks; \ + \ + const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \ + const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \ + \ + const int16x4_t coeff1 = vld1_s16(kCoeffs1); \ + const int16x8_t R_Rounder = vdupq_n_s16(-14234); \ + const int16x8_t G_Rounder = vdupq_n_s16(8708); \ + const int16x8_t B_Rounder = vdupq_n_s16(-17685); \ + \ + /* Treat the first pixel in regular way */ \ + assert(top_y != NULL); \ + { \ + const int u0 = (top_u[0] + u_diag) >> 1; \ + const int v0 = (top_v[0] + v_diag) >> 1; \ + VP8YuvTo ## FMT(top_y[0], u0, v0, top_dst); \ + } \ + if (bottom_y != NULL) { \ + const int u0 = (cur_u[0] + u_diag) >> 1; \ + const int v0 = (cur_v[0] + v_diag) >> 1; \ + VP8YuvTo ## FMT(bottom_y[0], u0, v0, bottom_dst); \ + } \ + \ + for (block = 0; block < num_blocks; ++block) { \ + UPSAMPLE_16PIXELS(top_u, cur_u, r_uv); \ + UPSAMPLE_16PIXELS(top_v, cur_v, r_uv + 16); \ + CONVERT2RGB_8(FMT, XSTEP, top_y, bottom_y, r_uv, \ + top_dst, bottom_dst, 16 * block + 1, 16); \ + top_u += 8; \ + cur_u += 8; \ + top_v += 8; \ + cur_v += 8; \ + } \ + \ + UPSAMPLE_LAST_BLOCK(top_u, cur_u, leftover, r_uv); \ + UPSAMPLE_LAST_BLOCK(top_v, cur_v, leftover, r_uv + 16); \ + CONVERT2RGB_1(VP8YuvTo ## FMT, XSTEP, top_y, bottom_y, r_uv, \ + top_dst, bottom_dst, last_pos, len - last_pos); \ +} + +// NEON variants of the fancy upsampler. +NEON_UPSAMPLE_FUNC(UpsampleRgbaLinePair_NEON, Rgba, 4) +NEON_UPSAMPLE_FUNC(UpsampleBgraLinePair_NEON, Bgra, 4) +#if !defined(WEBP_REDUCE_CSP) +NEON_UPSAMPLE_FUNC(UpsampleRgbLinePair_NEON, Rgb, 3) +NEON_UPSAMPLE_FUNC(UpsampleBgrLinePair_NEON, Bgr, 3) +NEON_UPSAMPLE_FUNC(UpsampleArgbLinePair_NEON, Argb, 4) +NEON_UPSAMPLE_FUNC(UpsampleRgba4444LinePair_NEON, Rgba4444, 2) +NEON_UPSAMPLE_FUNC(UpsampleRgb565LinePair_NEON, Rgb565, 2) +#endif // WEBP_REDUCE_CSP + +//------------------------------------------------------------------------------ +// Entry point + +extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; + +extern void WebPInitUpsamplersNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersNEON(void) { + WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair_NEON; + WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair_NEON; + WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair_NEON; + WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair_NEON; +#if !defined(WEBP_REDUCE_CSP) + WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair_NEON; + WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair_NEON; + WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair_NEON; + WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair_NEON; + WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair_NEON; + WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair_NEON; + WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair_NEON; +#endif // WEBP_REDUCE_CSP +} + +#endif // FANCY_UPSAMPLING + +#endif // WEBP_USE_NEON + +#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_NEON)) +WEBP_DSP_INIT_STUB(WebPInitUpsamplersNEON) +#endif diff --git a/internal/libwebp-1.2.1/src/dsp/upsampling_sse2.c b/internal/libwebp-1.2.1/src/dsp/upsampling_sse2.c new file mode 100644 index 0000000..340f1e2 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/upsampling_sse2.c @@ -0,0 +1,267 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE2 version of YUV to RGB upsampling functions. +// +// Author: somnath@google.com (Somnath Banerjee) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE2) + +#include +#include +#include +#include "src/dsp/yuv.h" + +#ifdef FANCY_UPSAMPLING + +// We compute (9*a + 3*b + 3*c + d + 8) / 16 as follows +// u = (9*a + 3*b + 3*c + d + 8) / 16 +// = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2 +// = (a + m + 1) / 2 +// where m = (a + 3*b + 3*c + d) / 8 +// = ((a + b + c + d) / 2 + b + c) / 4 +// +// Let's say k = (a + b + c + d) / 4. +// We can compute k as +// k = (s + t + 1) / 2 - ((a^d) | (b^c) | (s^t)) & 1 +// where s = (a + d + 1) / 2 and t = (b + c + 1) / 2 +// +// Then m can be written as +// m = (k + t + 1) / 2 - (((b^c) & (s^t)) | (k^t)) & 1 + +// Computes out = (k + in + 1) / 2 - ((ij & (s^t)) | (k^in)) & 1 +#define GET_M(ij, in, out) do { \ + const __m128i tmp0 = _mm_avg_epu8(k, (in)); /* (k + in + 1) / 2 */ \ + const __m128i tmp1 = _mm_and_si128((ij), st); /* (ij) & (s^t) */ \ + const __m128i tmp2 = _mm_xor_si128(k, (in)); /* (k^in) */ \ + const __m128i tmp3 = _mm_or_si128(tmp1, tmp2); /* ((ij) & (s^t)) | (k^in) */\ + const __m128i tmp4 = _mm_and_si128(tmp3, one); /* & 1 -> lsb_correction */ \ + (out) = _mm_sub_epi8(tmp0, tmp4); /* (k + in + 1) / 2 - lsb_correction */ \ +} while (0) + +// pack and store two alternating pixel rows +#define PACK_AND_STORE(a, b, da, db, out) do { \ + const __m128i t_a = _mm_avg_epu8(a, da); /* (9a + 3b + 3c + d + 8) / 16 */ \ + const __m128i t_b = _mm_avg_epu8(b, db); /* (3a + 9b + c + 3d + 8) / 16 */ \ + const __m128i t_1 = _mm_unpacklo_epi8(t_a, t_b); \ + const __m128i t_2 = _mm_unpackhi_epi8(t_a, t_b); \ + _mm_store_si128(((__m128i*)(out)) + 0, t_1); \ + _mm_store_si128(((__m128i*)(out)) + 1, t_2); \ +} while (0) + +// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels. +#define UPSAMPLE_32PIXELS(r1, r2, out) { \ + const __m128i one = _mm_set1_epi8(1); \ + const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \ + const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \ + const __m128i c = _mm_loadu_si128((const __m128i*)&(r2)[0]); \ + const __m128i d = _mm_loadu_si128((const __m128i*)&(r2)[1]); \ + \ + const __m128i s = _mm_avg_epu8(a, d); /* s = (a + d + 1) / 2 */ \ + const __m128i t = _mm_avg_epu8(b, c); /* t = (b + c + 1) / 2 */ \ + const __m128i st = _mm_xor_si128(s, t); /* st = s^t */ \ + \ + const __m128i ad = _mm_xor_si128(a, d); /* ad = a^d */ \ + const __m128i bc = _mm_xor_si128(b, c); /* bc = b^c */ \ + \ + const __m128i t1 = _mm_or_si128(ad, bc); /* (a^d) | (b^c) */ \ + const __m128i t2 = _mm_or_si128(t1, st); /* (a^d) | (b^c) | (s^t) */ \ + const __m128i t3 = _mm_and_si128(t2, one); /* (a^d) | (b^c) | (s^t) & 1 */ \ + const __m128i t4 = _mm_avg_epu8(s, t); \ + const __m128i k = _mm_sub_epi8(t4, t3); /* k = (a + b + c + d) / 4 */ \ + __m128i diag1, diag2; \ + \ + GET_M(bc, t, diag1); /* diag1 = (a + 3b + 3c + d) / 8 */ \ + GET_M(ad, s, diag2); /* diag2 = (3a + b + c + 3d) / 8 */ \ + \ + /* pack the alternate pixels */ \ + PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \ + PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \ +} + +// Turn the macro into a function for reducing code-size when non-critical +static void Upsample32Pixels_SSE2(const uint8_t r1[], const uint8_t r2[], + uint8_t* const out) { + UPSAMPLE_32PIXELS(r1, r2, out); +} + +#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \ + uint8_t r1[17], r2[17]; \ + memcpy(r1, (tb), (num_pixels)); \ + memcpy(r2, (bb), (num_pixels)); \ + /* replicate last byte */ \ + memset(r1 + (num_pixels), r1[(num_pixels) - 1], 17 - (num_pixels)); \ + memset(r2 + (num_pixels), r2[(num_pixels) - 1], 17 - (num_pixels)); \ + /* using the shared function instead of the macro saves ~3k code size */ \ + Upsample32Pixels_SSE2(r1, r2, out); \ +} + +#define CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, \ + top_dst, bottom_dst, cur_x) do { \ + FUNC##32_SSE2((top_y) + (cur_x), r_u, r_v, (top_dst) + (cur_x) * (XSTEP)); \ + if ((bottom_y) != NULL) { \ + FUNC##32_SSE2((bottom_y) + (cur_x), r_u + 64, r_v + 64, \ + (bottom_dst) + (cur_x) * (XSTEP)); \ + } \ +} while (0) + +#define SSE2_UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \ +static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_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* bottom_dst, int len) { \ + int uv_pos, pos; \ + /* 16byte-aligned array to cache reconstructed u and v */ \ + uint8_t uv_buf[14 * 32 + 15] = { 0 }; \ + uint8_t* const r_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \ + uint8_t* const r_v = r_u + 32; \ + \ + assert(top_y != NULL); \ + { /* Treat the first pixel in regular way */ \ + const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \ + const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \ + const int u0_t = (top_u[0] + u_diag) >> 1; \ + const int v0_t = (top_v[0] + v_diag) >> 1; \ + FUNC(top_y[0], u0_t, v0_t, top_dst); \ + if (bottom_y != NULL) { \ + const int u0_b = (cur_u[0] + u_diag) >> 1; \ + const int v0_b = (cur_v[0] + v_diag) >> 1; \ + FUNC(bottom_y[0], u0_b, v0_b, bottom_dst); \ + } \ + } \ + /* For UPSAMPLE_32PIXELS, 17 u/v values must be read-able for each block */ \ + for (pos = 1, uv_pos = 0; pos + 32 + 1 <= len; pos += 32, uv_pos += 16) { \ + UPSAMPLE_32PIXELS(top_u + uv_pos, cur_u + uv_pos, r_u); \ + UPSAMPLE_32PIXELS(top_v + uv_pos, cur_v + uv_pos, r_v); \ + CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, top_dst, bottom_dst, pos); \ + } \ + if (len > 1) { \ + const int left_over = ((len + 1) >> 1) - (pos >> 1); \ + uint8_t* const tmp_top_dst = r_u + 4 * 32; \ + uint8_t* const tmp_bottom_dst = tmp_top_dst + 4 * 32; \ + uint8_t* const tmp_top = tmp_bottom_dst + 4 * 32; \ + uint8_t* const tmp_bottom = (bottom_y == NULL) ? NULL : tmp_top + 32; \ + assert(left_over > 0); \ + UPSAMPLE_LAST_BLOCK(top_u + uv_pos, cur_u + uv_pos, left_over, r_u); \ + UPSAMPLE_LAST_BLOCK(top_v + uv_pos, cur_v + uv_pos, left_over, r_v); \ + memcpy(tmp_top, top_y + pos, len - pos); \ + if (bottom_y != NULL) memcpy(tmp_bottom, bottom_y + pos, len - pos); \ + CONVERT2RGB_32(FUNC, XSTEP, tmp_top, tmp_bottom, tmp_top_dst, \ + tmp_bottom_dst, 0); \ + memcpy(top_dst + pos * (XSTEP), tmp_top_dst, (len - pos) * (XSTEP)); \ + if (bottom_y != NULL) { \ + memcpy(bottom_dst + pos * (XSTEP), tmp_bottom_dst, \ + (len - pos) * (XSTEP)); \ + } \ + } \ +} + +// SSE2 variants of the fancy upsampler. +SSE2_UPSAMPLE_FUNC(UpsampleRgbaLinePair_SSE2, VP8YuvToRgba, 4) +SSE2_UPSAMPLE_FUNC(UpsampleBgraLinePair_SSE2, VP8YuvToBgra, 4) + +#if !defined(WEBP_REDUCE_CSP) +SSE2_UPSAMPLE_FUNC(UpsampleRgbLinePair_SSE2, VP8YuvToRgb, 3) +SSE2_UPSAMPLE_FUNC(UpsampleBgrLinePair_SSE2, VP8YuvToBgr, 3) +SSE2_UPSAMPLE_FUNC(UpsampleArgbLinePair_SSE2, VP8YuvToArgb, 4) +SSE2_UPSAMPLE_FUNC(UpsampleRgba4444LinePair_SSE2, VP8YuvToRgba4444, 2) +SSE2_UPSAMPLE_FUNC(UpsampleRgb565LinePair_SSE2, VP8YuvToRgb565, 2) +#endif // WEBP_REDUCE_CSP + +#undef GET_M +#undef PACK_AND_STORE +#undef UPSAMPLE_32PIXELS +#undef UPSAMPLE_LAST_BLOCK +#undef CONVERT2RGB +#undef CONVERT2RGB_32 +#undef SSE2_UPSAMPLE_FUNC + +//------------------------------------------------------------------------------ +// Entry point + +extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; + +extern void WebPInitUpsamplersSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersSSE2(void) { + WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair_SSE2; + WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair_SSE2; + WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair_SSE2; + WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair_SSE2; +#if !defined(WEBP_REDUCE_CSP) + WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair_SSE2; + WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair_SSE2; + WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair_SSE2; + WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair_SSE2; + WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair_SSE2; + WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair_SSE2; + WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair_SSE2; +#endif // WEBP_REDUCE_CSP +} + +#endif // FANCY_UPSAMPLING + +//------------------------------------------------------------------------------ + +extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */]; +extern void WebPInitYUV444ConvertersSSE2(void); + +#define YUV444_FUNC(FUNC_NAME, CALL, CALL_C, XSTEP) \ +extern void CALL_C(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len); \ +static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len) { \ + int i; \ + const int max_len = len & ~31; \ + for (i = 0; i < max_len; i += 32) { \ + CALL(y + i, u + i, v + i, dst + i * (XSTEP)); \ + } \ + if (i < len) { /* C-fallback */ \ + CALL_C(y + i, u + i, v + i, dst + i * (XSTEP), len - i); \ + } \ +} + +YUV444_FUNC(Yuv444ToRgba_SSE2, VP8YuvToRgba32_SSE2, WebPYuv444ToRgba_C, 4); +YUV444_FUNC(Yuv444ToBgra_SSE2, VP8YuvToBgra32_SSE2, WebPYuv444ToBgra_C, 4); +#if !defined(WEBP_REDUCE_CSP) +YUV444_FUNC(Yuv444ToRgb_SSE2, VP8YuvToRgb32_SSE2, WebPYuv444ToRgb_C, 3); +YUV444_FUNC(Yuv444ToBgr_SSE2, VP8YuvToBgr32_SSE2, WebPYuv444ToBgr_C, 3); +YUV444_FUNC(Yuv444ToArgb_SSE2, VP8YuvToArgb32_SSE2, WebPYuv444ToArgb_C, 4) +YUV444_FUNC(Yuv444ToRgba4444_SSE2, VP8YuvToRgba444432_SSE2, \ + WebPYuv444ToRgba4444_C, 2) +YUV444_FUNC(Yuv444ToRgb565_SSE2, VP8YuvToRgb56532_SSE2, WebPYuv444ToRgb565_C, 2) +#endif // WEBP_REDUCE_CSP + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersSSE2(void) { + WebPYUV444Converters[MODE_RGBA] = Yuv444ToRgba_SSE2; + WebPYUV444Converters[MODE_BGRA] = Yuv444ToBgra_SSE2; + WebPYUV444Converters[MODE_rgbA] = Yuv444ToRgba_SSE2; + WebPYUV444Converters[MODE_bgrA] = Yuv444ToBgra_SSE2; +#if !defined(WEBP_REDUCE_CSP) + WebPYUV444Converters[MODE_RGB] = Yuv444ToRgb_SSE2; + WebPYUV444Converters[MODE_BGR] = Yuv444ToBgr_SSE2; + WebPYUV444Converters[MODE_ARGB] = Yuv444ToArgb_SSE2; + WebPYUV444Converters[MODE_RGBA_4444] = Yuv444ToRgba4444_SSE2; + WebPYUV444Converters[MODE_RGB_565] = Yuv444ToRgb565_SSE2; + WebPYUV444Converters[MODE_Argb] = Yuv444ToArgb_SSE2; + WebPYUV444Converters[MODE_rgbA_4444] = Yuv444ToRgba4444_SSE2; +#endif // WEBP_REDUCE_CSP +} + +#else + +WEBP_DSP_INIT_STUB(WebPInitYUV444ConvertersSSE2) + +#endif // WEBP_USE_SSE2 + +#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_SSE2)) +WEBP_DSP_INIT_STUB(WebPInitUpsamplersSSE2) +#endif diff --git a/internal/libwebp-1.2.1/src/dsp/upsampling_sse41.c b/internal/libwebp-1.2.1/src/dsp/upsampling_sse41.c new file mode 100644 index 0000000..648d456 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/upsampling_sse41.c @@ -0,0 +1,239 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// SSE41 version of YUV to RGB upsampling functions. +// +// Author: somnath@google.com (Somnath Banerjee) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_SSE41) + +#include +#include +#include +#include "src/dsp/yuv.h" + +#ifdef FANCY_UPSAMPLING + +#if !defined(WEBP_REDUCE_CSP) + +// We compute (9*a + 3*b + 3*c + d + 8) / 16 as follows +// u = (9*a + 3*b + 3*c + d + 8) / 16 +// = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2 +// = (a + m + 1) / 2 +// where m = (a + 3*b + 3*c + d) / 8 +// = ((a + b + c + d) / 2 + b + c) / 4 +// +// Let's say k = (a + b + c + d) / 4. +// We can compute k as +// k = (s + t + 1) / 2 - ((a^d) | (b^c) | (s^t)) & 1 +// where s = (a + d + 1) / 2 and t = (b + c + 1) / 2 +// +// Then m can be written as +// m = (k + t + 1) / 2 - (((b^c) & (s^t)) | (k^t)) & 1 + +// Computes out = (k + in + 1) / 2 - ((ij & (s^t)) | (k^in)) & 1 +#define GET_M(ij, in, out) do { \ + const __m128i tmp0 = _mm_avg_epu8(k, (in)); /* (k + in + 1) / 2 */ \ + const __m128i tmp1 = _mm_and_si128((ij), st); /* (ij) & (s^t) */ \ + const __m128i tmp2 = _mm_xor_si128(k, (in)); /* (k^in) */ \ + const __m128i tmp3 = _mm_or_si128(tmp1, tmp2); /* ((ij) & (s^t)) | (k^in) */\ + const __m128i tmp4 = _mm_and_si128(tmp3, one); /* & 1 -> lsb_correction */ \ + (out) = _mm_sub_epi8(tmp0, tmp4); /* (k + in + 1) / 2 - lsb_correction */ \ +} while (0) + +// pack and store two alternating pixel rows +#define PACK_AND_STORE(a, b, da, db, out) do { \ + const __m128i t_a = _mm_avg_epu8(a, da); /* (9a + 3b + 3c + d + 8) / 16 */ \ + const __m128i t_b = _mm_avg_epu8(b, db); /* (3a + 9b + c + 3d + 8) / 16 */ \ + const __m128i t_1 = _mm_unpacklo_epi8(t_a, t_b); \ + const __m128i t_2 = _mm_unpackhi_epi8(t_a, t_b); \ + _mm_store_si128(((__m128i*)(out)) + 0, t_1); \ + _mm_store_si128(((__m128i*)(out)) + 1, t_2); \ +} while (0) + +// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels. +#define UPSAMPLE_32PIXELS(r1, r2, out) { \ + const __m128i one = _mm_set1_epi8(1); \ + const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \ + const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \ + const __m128i c = _mm_loadu_si128((const __m128i*)&(r2)[0]); \ + const __m128i d = _mm_loadu_si128((const __m128i*)&(r2)[1]); \ + \ + const __m128i s = _mm_avg_epu8(a, d); /* s = (a + d + 1) / 2 */ \ + const __m128i t = _mm_avg_epu8(b, c); /* t = (b + c + 1) / 2 */ \ + const __m128i st = _mm_xor_si128(s, t); /* st = s^t */ \ + \ + const __m128i ad = _mm_xor_si128(a, d); /* ad = a^d */ \ + const __m128i bc = _mm_xor_si128(b, c); /* bc = b^c */ \ + \ + const __m128i t1 = _mm_or_si128(ad, bc); /* (a^d) | (b^c) */ \ + const __m128i t2 = _mm_or_si128(t1, st); /* (a^d) | (b^c) | (s^t) */ \ + const __m128i t3 = _mm_and_si128(t2, one); /* (a^d) | (b^c) | (s^t) & 1 */ \ + const __m128i t4 = _mm_avg_epu8(s, t); \ + const __m128i k = _mm_sub_epi8(t4, t3); /* k = (a + b + c + d) / 4 */ \ + __m128i diag1, diag2; \ + \ + GET_M(bc, t, diag1); /* diag1 = (a + 3b + 3c + d) / 8 */ \ + GET_M(ad, s, diag2); /* diag2 = (3a + b + c + 3d) / 8 */ \ + \ + /* pack the alternate pixels */ \ + PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \ + PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \ +} + +// Turn the macro into a function for reducing code-size when non-critical +static void Upsample32Pixels_SSE41(const uint8_t r1[], const uint8_t r2[], + uint8_t* const out) { + UPSAMPLE_32PIXELS(r1, r2, out); +} + +#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \ + uint8_t r1[17], r2[17]; \ + memcpy(r1, (tb), (num_pixels)); \ + memcpy(r2, (bb), (num_pixels)); \ + /* replicate last byte */ \ + memset(r1 + (num_pixels), r1[(num_pixels) - 1], 17 - (num_pixels)); \ + memset(r2 + (num_pixels), r2[(num_pixels) - 1], 17 - (num_pixels)); \ + /* using the shared function instead of the macro saves ~3k code size */ \ + Upsample32Pixels_SSE41(r1, r2, out); \ +} + +#define CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, \ + top_dst, bottom_dst, cur_x) do { \ + FUNC##32_SSE41((top_y) + (cur_x), r_u, r_v, (top_dst) + (cur_x) * (XSTEP)); \ + if ((bottom_y) != NULL) { \ + FUNC##32_SSE41((bottom_y) + (cur_x), r_u + 64, r_v + 64, \ + (bottom_dst) + (cur_x) * (XSTEP)); \ + } \ +} while (0) + +#define SSE4_UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \ +static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_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* bottom_dst, int len) { \ + int uv_pos, pos; \ + /* 16byte-aligned array to cache reconstructed u and v */ \ + uint8_t uv_buf[14 * 32 + 15] = { 0 }; \ + uint8_t* const r_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \ + uint8_t* const r_v = r_u + 32; \ + \ + assert(top_y != NULL); \ + { /* Treat the first pixel in regular way */ \ + const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \ + const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \ + const int u0_t = (top_u[0] + u_diag) >> 1; \ + const int v0_t = (top_v[0] + v_diag) >> 1; \ + FUNC(top_y[0], u0_t, v0_t, top_dst); \ + if (bottom_y != NULL) { \ + const int u0_b = (cur_u[0] + u_diag) >> 1; \ + const int v0_b = (cur_v[0] + v_diag) >> 1; \ + FUNC(bottom_y[0], u0_b, v0_b, bottom_dst); \ + } \ + } \ + /* For UPSAMPLE_32PIXELS, 17 u/v values must be read-able for each block */ \ + for (pos = 1, uv_pos = 0; pos + 32 + 1 <= len; pos += 32, uv_pos += 16) { \ + UPSAMPLE_32PIXELS(top_u + uv_pos, cur_u + uv_pos, r_u); \ + UPSAMPLE_32PIXELS(top_v + uv_pos, cur_v + uv_pos, r_v); \ + CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, top_dst, bottom_dst, pos); \ + } \ + if (len > 1) { \ + const int left_over = ((len + 1) >> 1) - (pos >> 1); \ + uint8_t* const tmp_top_dst = r_u + 4 * 32; \ + uint8_t* const tmp_bottom_dst = tmp_top_dst + 4 * 32; \ + uint8_t* const tmp_top = tmp_bottom_dst + 4 * 32; \ + uint8_t* const tmp_bottom = (bottom_y == NULL) ? NULL : tmp_top + 32; \ + assert(left_over > 0); \ + UPSAMPLE_LAST_BLOCK(top_u + uv_pos, cur_u + uv_pos, left_over, r_u); \ + UPSAMPLE_LAST_BLOCK(top_v + uv_pos, cur_v + uv_pos, left_over, r_v); \ + memcpy(tmp_top, top_y + pos, len - pos); \ + if (bottom_y != NULL) memcpy(tmp_bottom, bottom_y + pos, len - pos); \ + CONVERT2RGB_32(FUNC, XSTEP, tmp_top, tmp_bottom, tmp_top_dst, \ + tmp_bottom_dst, 0); \ + memcpy(top_dst + pos * (XSTEP), tmp_top_dst, (len - pos) * (XSTEP)); \ + if (bottom_y != NULL) { \ + memcpy(bottom_dst + pos * (XSTEP), tmp_bottom_dst, \ + (len - pos) * (XSTEP)); \ + } \ + } \ +} + +// SSE4 variants of the fancy upsampler. +SSE4_UPSAMPLE_FUNC(UpsampleRgbLinePair_SSE41, VP8YuvToRgb, 3) +SSE4_UPSAMPLE_FUNC(UpsampleBgrLinePair_SSE41, VP8YuvToBgr, 3) + +#undef GET_M +#undef PACK_AND_STORE +#undef UPSAMPLE_32PIXELS +#undef UPSAMPLE_LAST_BLOCK +#undef CONVERT2RGB +#undef CONVERT2RGB_32 +#undef SSE4_UPSAMPLE_FUNC + +#endif // WEBP_REDUCE_CSP + +//------------------------------------------------------------------------------ +// Entry point + +extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; + +extern void WebPInitUpsamplersSSE41(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersSSE41(void) { +#if !defined(WEBP_REDUCE_CSP) + WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair_SSE41; + WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair_SSE41; +#endif // WEBP_REDUCE_CSP +} + +#endif // FANCY_UPSAMPLING + +//------------------------------------------------------------------------------ + +extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */]; +extern void WebPInitYUV444ConvertersSSE41(void); + +#define YUV444_FUNC(FUNC_NAME, CALL, CALL_C, XSTEP) \ +extern void CALL_C(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len); \ +static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len) { \ + int i; \ + const int max_len = len & ~31; \ + for (i = 0; i < max_len; i += 32) { \ + CALL(y + i, u + i, v + i, dst + i * (XSTEP)); \ + } \ + if (i < len) { /* C-fallback */ \ + CALL_C(y + i, u + i, v + i, dst + i * (XSTEP), len - i); \ + } \ +} + +#if !defined(WEBP_REDUCE_CSP) +YUV444_FUNC(Yuv444ToRgb_SSE41, VP8YuvToRgb32_SSE41, WebPYuv444ToRgb_C, 3); +YUV444_FUNC(Yuv444ToBgr_SSE41, VP8YuvToBgr32_SSE41, WebPYuv444ToBgr_C, 3); +#endif // WEBP_REDUCE_CSP + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersSSE41(void) { +#if !defined(WEBP_REDUCE_CSP) + WebPYUV444Converters[MODE_RGB] = Yuv444ToRgb_SSE41; + WebPYUV444Converters[MODE_BGR] = Yuv444ToBgr_SSE41; +#endif // WEBP_REDUCE_CSP +} + +#else + +WEBP_DSP_INIT_STUB(WebPInitYUV444ConvertersSSE41) + +#endif // WEBP_USE_SSE41 + +#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_SSE41)) +WEBP_DSP_INIT_STUB(WebPInitUpsamplersSSE41) +#endif diff --git a/internal/libwebp-1.2.1/src/dsp/yuv.c b/internal/libwebp-1.2.1/src/dsp/yuv.c new file mode 100644 index 0000000..48466f8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/yuv.c @@ -0,0 +1,308 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// YUV->RGB conversion functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/yuv.h" + +#include +#include + +//----------------------------------------------------------------------------- +// Plain-C version + +#define ROW_FUNC(FUNC_NAME, FUNC, XSTEP) \ +static void FUNC_NAME(const uint8_t* y, \ + const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len) { \ + const uint8_t* const end = dst + (len & ~1) * (XSTEP); \ + while (dst != end) { \ + FUNC(y[0], u[0], v[0], dst); \ + FUNC(y[1], u[0], v[0], dst + (XSTEP)); \ + y += 2; \ + ++u; \ + ++v; \ + dst += 2 * (XSTEP); \ + } \ + if (len & 1) { \ + FUNC(y[0], u[0], v[0], dst); \ + } \ +} \ + +// All variants implemented. +ROW_FUNC(YuvToRgbRow, VP8YuvToRgb, 3) +ROW_FUNC(YuvToBgrRow, VP8YuvToBgr, 3) +ROW_FUNC(YuvToRgbaRow, VP8YuvToRgba, 4) +ROW_FUNC(YuvToBgraRow, VP8YuvToBgra, 4) +ROW_FUNC(YuvToArgbRow, VP8YuvToArgb, 4) +ROW_FUNC(YuvToRgba4444Row, VP8YuvToRgba4444, 2) +ROW_FUNC(YuvToRgb565Row, VP8YuvToRgb565, 2) + +#undef ROW_FUNC + +// Main call for processing a plane with a WebPSamplerRowFunc function: +void WebPSamplerProcessPlane(const uint8_t* y, int y_stride, + const uint8_t* u, const uint8_t* v, int uv_stride, + uint8_t* dst, int dst_stride, + int width, int height, WebPSamplerRowFunc func) { + int j; + for (j = 0; j < height; ++j) { + func(y, u, v, dst, width); + y += y_stride; + if (j & 1) { + u += uv_stride; + v += uv_stride; + } + dst += dst_stride; + } +} + +//----------------------------------------------------------------------------- +// Main call + +WebPSamplerRowFunc WebPSamplers[MODE_LAST]; + +extern void WebPInitSamplersSSE2(void); +extern void WebPInitSamplersSSE41(void); +extern void WebPInitSamplersMIPS32(void); +extern void WebPInitSamplersMIPSdspR2(void); + +WEBP_DSP_INIT_FUNC(WebPInitSamplers) { + WebPSamplers[MODE_RGB] = YuvToRgbRow; + WebPSamplers[MODE_RGBA] = YuvToRgbaRow; + WebPSamplers[MODE_BGR] = YuvToBgrRow; + WebPSamplers[MODE_BGRA] = YuvToBgraRow; + WebPSamplers[MODE_ARGB] = YuvToArgbRow; + WebPSamplers[MODE_RGBA_4444] = YuvToRgba4444Row; + WebPSamplers[MODE_RGB_565] = YuvToRgb565Row; + WebPSamplers[MODE_rgbA] = YuvToRgbaRow; + WebPSamplers[MODE_bgrA] = YuvToBgraRow; + WebPSamplers[MODE_Argb] = YuvToArgbRow; + WebPSamplers[MODE_rgbA_4444] = YuvToRgba4444Row; + + // If defined, use CPUInfo() to overwrite some pointers with faster versions. + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + WebPInitSamplersSSE2(); + } +#endif // WEBP_HAVE_SSE2 +#if defined(WEBP_HAVE_SSE41) + if (VP8GetCPUInfo(kSSE4_1)) { + WebPInitSamplersSSE41(); + } +#endif // WEBP_HAVE_SSE41 +#if defined(WEBP_USE_MIPS32) + if (VP8GetCPUInfo(kMIPS32)) { + WebPInitSamplersMIPS32(); + } +#endif // WEBP_USE_MIPS32 +#if defined(WEBP_USE_MIPS_DSP_R2) + if (VP8GetCPUInfo(kMIPSdspR2)) { + WebPInitSamplersMIPSdspR2(); + } +#endif // WEBP_USE_MIPS_DSP_R2 + } +} + +//----------------------------------------------------------------------------- +// ARGB -> YUV converters + +static void ConvertARGBToY_C(const uint32_t* argb, uint8_t* y, int width) { + int i; + for (i = 0; i < width; ++i) { + const uint32_t p = argb[i]; + y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff, + YUV_HALF); + } +} + +void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v, + int src_width, int do_store) { + // No rounding. Last pixel is dealt with separately. + const int uv_width = src_width >> 1; + int i; + for (i = 0; i < uv_width; ++i) { + const uint32_t v0 = argb[2 * i + 0]; + const uint32_t v1 = argb[2 * i + 1]; + // VP8RGBToU/V expects four accumulated pixels. Hence we need to + // scale r/g/b value by a factor 2. We just shift v0/v1 one bit less. + const int r = ((v0 >> 15) & 0x1fe) + ((v1 >> 15) & 0x1fe); + const int g = ((v0 >> 7) & 0x1fe) + ((v1 >> 7) & 0x1fe); + const int b = ((v0 << 1) & 0x1fe) + ((v1 << 1) & 0x1fe); + const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2); + const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2); + if (do_store) { + u[i] = tmp_u; + v[i] = tmp_v; + } else { + // Approximated average-of-four. But it's an acceptable diff. + u[i] = (u[i] + tmp_u + 1) >> 1; + v[i] = (v[i] + tmp_v + 1) >> 1; + } + } + if (src_width & 1) { // last pixel + const uint32_t v0 = argb[2 * i + 0]; + const int r = (v0 >> 14) & 0x3fc; + const int g = (v0 >> 6) & 0x3fc; + const int b = (v0 << 2) & 0x3fc; + const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2); + const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2); + if (do_store) { + u[i] = tmp_u; + v[i] = tmp_v; + } else { + u[i] = (u[i] + tmp_u + 1) >> 1; + v[i] = (v[i] + tmp_v + 1) >> 1; + } + } +} + +//----------------------------------------------------------------------------- + +static void ConvertRGB24ToY_C(const uint8_t* rgb, uint8_t* y, int width) { + int i; + for (i = 0; i < width; ++i, rgb += 3) { + y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF); + } +} + +static void ConvertBGR24ToY_C(const uint8_t* bgr, uint8_t* y, int width) { + int i; + for (i = 0; i < width; ++i, bgr += 3) { + y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF); + } +} + +void WebPConvertRGBA32ToUV_C(const uint16_t* rgb, + uint8_t* u, uint8_t* v, int width) { + int i; + for (i = 0; i < width; i += 1, rgb += 4) { + const int r = rgb[0], g = rgb[1], b = rgb[2]; + u[i] = VP8RGBToU(r, g, b, YUV_HALF << 2); + v[i] = VP8RGBToV(r, g, b, YUV_HALF << 2); + } +} + +//----------------------------------------------------------------------------- + +#if !WEBP_NEON_OMIT_C_CODE +#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); + } +} +#endif // !WEBP_NEON_OMIT_C_CODE + +#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, + uint8_t* u, uint8_t* v, int width); + +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); + +extern void WebPInitConvertARGBToYUVSSE2(void); +extern void WebPInitConvertARGBToYUVSSE41(void); +extern void WebPInitConvertARGBToYUVNEON(void); +extern void WebPInitSharpYUVSSE2(void); +extern void WebPInitSharpYUVNEON(void); + +WEBP_DSP_INIT_FUNC(WebPInitConvertARGBToYUV) { + WebPConvertARGBToY = ConvertARGBToY_C; + WebPConvertARGBToUV = WebPConvertARGBToUV_C; + + WebPConvertRGB24ToY = ConvertRGB24ToY_C; + WebPConvertBGR24ToY = ConvertBGR24ToY_C; + + WebPConvertRGBA32ToUV = WebPConvertRGBA32ToUV_C; + +#if !WEBP_NEON_OMIT_C_CODE + WebPSharpYUVUpdateY = SharpYUVUpdateY_C; + WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_C; + WebPSharpYUVFilterRow = SharpYUVFilterRow_C; +#endif + + if (VP8GetCPUInfo != NULL) { +#if defined(WEBP_HAVE_SSE2) + if (VP8GetCPUInfo(kSSE2)) { + WebPInitConvertARGBToYUVSSE2(); + WebPInitSharpYUVSSE2(); + } +#endif // WEBP_HAVE_SSE2 +#if defined(WEBP_HAVE_SSE41) + if (VP8GetCPUInfo(kSSE4_1)) { + WebPInitConvertARGBToYUVSSE41(); + } +#endif // WEBP_HAVE_SSE41 + } + +#if defined(WEBP_HAVE_NEON) + if (WEBP_NEON_OMIT_C_CODE || + (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) { + WebPInitConvertARGBToYUVNEON(); + WebPInitSharpYUVNEON(); + } +#endif // WEBP_HAVE_NEON + + assert(WebPConvertARGBToY != NULL); + assert(WebPConvertARGBToUV != NULL); + assert(WebPConvertRGB24ToY != NULL); + assert(WebPConvertBGR24ToY != NULL); + assert(WebPConvertRGBA32ToUV != NULL); + assert(WebPSharpYUVUpdateY != NULL); + assert(WebPSharpYUVUpdateRGB != NULL); + assert(WebPSharpYUVFilterRow != NULL); +} diff --git a/internal/libwebp-1.2.1/src/dsp/yuv.h b/internal/libwebp-1.2.1/src/dsp/yuv.h new file mode 100644 index 0000000..c12be1d --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/yuv.h @@ -0,0 +1,210 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// inline YUV<->RGB conversion function +// +// The exact naming is Y'CbCr, following the ITU-R BT.601 standard. +// More information at: http://en.wikipedia.org/wiki/YCbCr +// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16 +// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128 +// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128 +// We use 16bit fixed point operations for RGB->YUV conversion (YUV_FIX). +// +// For the Y'CbCr to RGB conversion, the BT.601 specification reads: +// R = 1.164 * (Y-16) + 1.596 * (V-128) +// G = 1.164 * (Y-16) - 0.813 * (V-128) - 0.391 * (U-128) +// B = 1.164 * (Y-16) + 2.018 * (U-128) +// where Y is in the [16,235] range, and U/V in the [16,240] range. +// +// The fixed-point implementation used here is: +// R = (19077 . y + 26149 . v - 14234) >> 6 +// G = (19077 . y - 6419 . u - 13320 . v + 8708) >> 6 +// B = (19077 . y + 33050 . u - 17685) >> 6 +// where the '.' operator is the mulhi_epu16 variant: +// a . b = ((a << 8) * b) >> 16 +// that preserves 8 bits of fractional precision before final descaling. + +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_DSP_YUV_H_ +#define WEBP_DSP_YUV_H_ + +#include "src/dsp/dsp.h" +#include "src/dec/vp8_dec.h" + +//------------------------------------------------------------------------------ +// YUV -> RGB conversion + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + YUV_FIX = 16, // fixed-point precision for RGB->YUV + YUV_HALF = 1 << (YUV_FIX - 1), + + YUV_FIX2 = 6, // fixed-point precision for YUV->RGB + YUV_MASK2 = (256 << YUV_FIX2) - 1 +}; + +//------------------------------------------------------------------------------ +// slower on x86 by ~7-8%, but bit-exact with the SSE2/NEON version + +static WEBP_INLINE int MultHi(int v, int coeff) { // _mm_mulhi_epu16 emulation + return (v * coeff) >> 8; +} + +static WEBP_INLINE int VP8Clip8(int v) { + return ((v & ~YUV_MASK2) == 0) ? (v >> YUV_FIX2) : (v < 0) ? 0 : 255; +} + +static WEBP_INLINE int VP8YUVToR(int y, int v) { + return VP8Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234); +} + +static WEBP_INLINE int VP8YUVToG(int y, int u, int v) { + return VP8Clip8(MultHi(y, 19077) - MultHi(u, 6419) - MultHi(v, 13320) + 8708); +} + +static WEBP_INLINE int VP8YUVToB(int y, int u) { + return VP8Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685); +} + +static WEBP_INLINE void VP8YuvToRgb(int y, int u, int v, + uint8_t* const rgb) { + rgb[0] = VP8YUVToR(y, v); + rgb[1] = VP8YUVToG(y, u, v); + rgb[2] = VP8YUVToB(y, u); +} + +static WEBP_INLINE void VP8YuvToBgr(int y, int u, int v, + uint8_t* const bgr) { + bgr[0] = VP8YUVToB(y, u); + bgr[1] = VP8YUVToG(y, u, v); + bgr[2] = VP8YUVToR(y, v); +} + +static WEBP_INLINE void VP8YuvToRgb565(int y, int u, int v, + uint8_t* const rgb) { + const int r = VP8YUVToR(y, v); // 5 usable bits + const int g = VP8YUVToG(y, u, v); // 6 usable bits + const int b = VP8YUVToB(y, u); // 5 usable bits + const int rg = (r & 0xf8) | (g >> 5); + const int gb = ((g << 3) & 0xe0) | (b >> 3); +#if (WEBP_SWAP_16BIT_CSP == 1) + rgb[0] = gb; + rgb[1] = rg; +#else + rgb[0] = rg; + rgb[1] = gb; +#endif +} + +static WEBP_INLINE void VP8YuvToRgba4444(int y, int u, int v, + uint8_t* const argb) { + const int r = VP8YUVToR(y, v); // 4 usable bits + const int g = VP8YUVToG(y, u, v); // 4 usable bits + const int b = VP8YUVToB(y, u); // 4 usable bits + const int rg = (r & 0xf0) | (g >> 4); + const int ba = (b & 0xf0) | 0x0f; // overwrite the lower 4 bits +#if (WEBP_SWAP_16BIT_CSP == 1) + argb[0] = ba; + argb[1] = rg; +#else + argb[0] = rg; + argb[1] = ba; +#endif +} + +//----------------------------------------------------------------------------- +// Alpha handling variants + +static WEBP_INLINE void VP8YuvToArgb(uint8_t y, uint8_t u, uint8_t v, + uint8_t* const argb) { + argb[0] = 0xff; + VP8YuvToRgb(y, u, v, argb + 1); +} + +static WEBP_INLINE void VP8YuvToBgra(uint8_t y, uint8_t u, uint8_t v, + uint8_t* const bgra) { + VP8YuvToBgr(y, u, v, bgra); + bgra[3] = 0xff; +} + +static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v, + uint8_t* const rgba) { + VP8YuvToRgb(y, u, v, rgba); + rgba[3] = 0xff; +} + +//----------------------------------------------------------------------------- +// SSE2 extra functions (mostly for upsampling_sse2.c) + +#if defined(WEBP_USE_SSE2) + +// Process 32 pixels and store the result (16b, 24b or 32b per pixel) in *dst. +void VP8YuvToRgba32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); +void VP8YuvToRgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); +void VP8YuvToBgra32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); +void VP8YuvToBgr32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); +void VP8YuvToArgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); +void VP8YuvToRgba444432_SSE2(const uint8_t* y, const uint8_t* u, + const uint8_t* v, uint8_t* dst); +void VP8YuvToRgb56532_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); + +#endif // WEBP_USE_SSE2 + +//----------------------------------------------------------------------------- +// SSE41 extra functions (mostly for upsampling_sse41.c) + +#if defined(WEBP_USE_SSE41) + +// Process 32 pixels and store the result (16b, 24b or 32b per pixel) in *dst. +void VP8YuvToRgb32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); +void VP8YuvToBgr32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); + +#endif // WEBP_USE_SSE41 + +//------------------------------------------------------------------------------ +// RGB -> YUV conversion + +// Stub functions that can be called with various rounding values: +static WEBP_INLINE int VP8ClipUV(int uv, int rounding) { + uv = (uv + rounding + (128 << (YUV_FIX + 2))) >> (YUV_FIX + 2); + return ((uv & ~0xff) == 0) ? uv : (uv < 0) ? 0 : 255; +} + +static WEBP_INLINE int VP8RGBToY(int r, int g, int b, int rounding) { + const int luma = 16839 * r + 33059 * g + 6420 * b; + return (luma + rounding + (16 << YUV_FIX)) >> YUV_FIX; // no need to clip +} + +static WEBP_INLINE int VP8RGBToU(int r, int g, int b, int rounding) { + const int u = -9719 * r - 19081 * g + 28800 * b; + return VP8ClipUV(u, rounding); +} + +static WEBP_INLINE int VP8RGBToV(int r, int g, int b, int rounding) { + const int v = +28800 * r - 24116 * g - 4684 * b; + return VP8ClipUV(v, rounding); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_DSP_YUV_H_ diff --git a/internal/libwebp-1.2.1/src/dsp/yuv_mips32.c b/internal/libwebp-1.2.1/src/dsp/yuv_mips32.c new file mode 100644 index 0000000..9d0a887 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/yuv_mips32.c @@ -0,0 +1,103 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS version of YUV to RGB upsampling functions. +// +// Author(s): Djordje Pesut (djordje.pesut@imgtec.com) +// Jovan Zelincevic (jovan.zelincevic@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS32) + +#include "src/dsp/yuv.h" + +//------------------------------------------------------------------------------ +// simple point-sampling + +#define ROW_FUNC(FUNC_NAME, XSTEP, R, G, B, A) \ +static void FUNC_NAME(const uint8_t* y, \ + const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len) { \ + int i, r, g, b; \ + int temp0, temp1, temp2, temp3, temp4; \ + for (i = 0; i < (len >> 1); i++) { \ + temp1 = MultHi(v[0], 26149); \ + temp3 = MultHi(v[0], 13320); \ + temp2 = MultHi(u[0], 6419); \ + temp4 = MultHi(u[0], 33050); \ + temp0 = MultHi(y[0], 19077); \ + temp1 -= 14234; \ + temp3 -= 8708; \ + temp2 += temp3; \ + temp4 -= 17685; \ + r = VP8Clip8(temp0 + temp1); \ + g = VP8Clip8(temp0 - temp2); \ + b = VP8Clip8(temp0 + temp4); \ + temp0 = MultHi(y[1], 19077); \ + dst[R] = r; \ + dst[G] = g; \ + dst[B] = b; \ + if (A) dst[A] = 0xff; \ + r = VP8Clip8(temp0 + temp1); \ + g = VP8Clip8(temp0 - temp2); \ + b = VP8Clip8(temp0 + temp4); \ + dst[R + XSTEP] = r; \ + dst[G + XSTEP] = g; \ + dst[B + XSTEP] = b; \ + if (A) dst[A + XSTEP] = 0xff; \ + y += 2; \ + ++u; \ + ++v; \ + dst += 2 * XSTEP; \ + } \ + if (len & 1) { \ + temp1 = MultHi(v[0], 26149); \ + temp3 = MultHi(v[0], 13320); \ + temp2 = MultHi(u[0], 6419); \ + temp4 = MultHi(u[0], 33050); \ + temp0 = MultHi(y[0], 19077); \ + temp1 -= 14234; \ + temp3 -= 8708; \ + temp2 += temp3; \ + temp4 -= 17685; \ + r = VP8Clip8(temp0 + temp1); \ + g = VP8Clip8(temp0 - temp2); \ + b = VP8Clip8(temp0 + temp4); \ + dst[R] = r; \ + dst[G] = g; \ + dst[B] = b; \ + if (A) dst[A] = 0xff; \ + } \ +} + +ROW_FUNC(YuvToRgbRow_MIPS32, 3, 0, 1, 2, 0) +ROW_FUNC(YuvToRgbaRow_MIPS32, 4, 0, 1, 2, 3) +ROW_FUNC(YuvToBgrRow_MIPS32, 3, 2, 1, 0, 0) +ROW_FUNC(YuvToBgraRow_MIPS32, 4, 2, 1, 0, 3) + +#undef ROW_FUNC + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPInitSamplersMIPS32(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersMIPS32(void) { + WebPSamplers[MODE_RGB] = YuvToRgbRow_MIPS32; + WebPSamplers[MODE_RGBA] = YuvToRgbaRow_MIPS32; + WebPSamplers[MODE_BGR] = YuvToBgrRow_MIPS32; + WebPSamplers[MODE_BGRA] = YuvToBgraRow_MIPS32; +} + +#else // !WEBP_USE_MIPS32 + +WEBP_DSP_INIT_STUB(WebPInitSamplersMIPS32) + +#endif // WEBP_USE_MIPS32 diff --git a/internal/libwebp-1.2.1/src/dsp/yuv_mips_dsp_r2.c b/internal/libwebp-1.2.1/src/dsp/yuv_mips_dsp_r2.c new file mode 100644 index 0000000..cc8afcc --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/yuv_mips_dsp_r2.c @@ -0,0 +1,134 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// MIPS DSPr2 version of YUV to RGB upsampling functions. +// +// Author(s): Branimir Vasic (branimir.vasic@imgtec.com) +// Djordje Pesut (djordje.pesut@imgtec.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_MIPS_DSP_R2) + +#include "src/dsp/yuv.h" + +//------------------------------------------------------------------------------ +// simple point-sampling + +#define ROW_FUNC_PART_1() \ + "lbu %[temp3], 0(%[v]) \n\t" \ + "lbu %[temp4], 0(%[u]) \n\t" \ + "lbu %[temp0], 0(%[y]) \n\t" \ + "mul %[temp1], %[t_con_1], %[temp3] \n\t" \ + "mul %[temp3], %[t_con_2], %[temp3] \n\t" \ + "mul %[temp2], %[t_con_3], %[temp4] \n\t" \ + "mul %[temp4], %[t_con_4], %[temp4] \n\t" \ + "mul %[temp0], %[t_con_5], %[temp0] \n\t" \ + "subu %[temp1], %[temp1], %[t_con_6] \n\t" \ + "subu %[temp3], %[temp3], %[t_con_7] \n\t" \ + "addu %[temp2], %[temp2], %[temp3] \n\t" \ + "subu %[temp4], %[temp4], %[t_con_8] \n\t" \ + +#define ROW_FUNC_PART_2(R, G, B, K) \ + "addu %[temp5], %[temp0], %[temp1] \n\t" \ + "subu %[temp6], %[temp0], %[temp2] \n\t" \ + "addu %[temp7], %[temp0], %[temp4] \n\t" \ +".if " #K " \n\t" \ + "lbu %[temp0], 1(%[y]) \n\t" \ +".endif \n\t" \ + "shll_s.w %[temp5], %[temp5], 17 \n\t" \ + "shll_s.w %[temp6], %[temp6], 17 \n\t" \ +".if " #K " \n\t" \ + "mul %[temp0], %[t_con_5], %[temp0] \n\t" \ +".endif \n\t" \ + "shll_s.w %[temp7], %[temp7], 17 \n\t" \ + "precrqu_s.qb.ph %[temp5], %[temp5], $zero \n\t" \ + "precrqu_s.qb.ph %[temp6], %[temp6], $zero \n\t" \ + "precrqu_s.qb.ph %[temp7], %[temp7], $zero \n\t" \ + "srl %[temp5], %[temp5], 24 \n\t" \ + "srl %[temp6], %[temp6], 24 \n\t" \ + "srl %[temp7], %[temp7], 24 \n\t" \ + "sb %[temp5], " #R "(%[dst]) \n\t" \ + "sb %[temp6], " #G "(%[dst]) \n\t" \ + "sb %[temp7], " #B "(%[dst]) \n\t" \ + +#define ASM_CLOBBER_LIST() \ + : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \ + [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \ + [temp6]"=&r"(temp6), [temp7]"=&r"(temp7) \ + : [t_con_1]"r"(t_con_1), [t_con_2]"r"(t_con_2), [t_con_3]"r"(t_con_3), \ + [t_con_4]"r"(t_con_4), [t_con_5]"r"(t_con_5), [t_con_6]"r"(t_con_6), \ + [u]"r"(u), [v]"r"(v), [y]"r"(y), [dst]"r"(dst), \ + [t_con_7]"r"(t_con_7), [t_con_8]"r"(t_con_8) \ + : "memory", "hi", "lo" \ + +#define ROW_FUNC(FUNC_NAME, XSTEP, R, G, B, A) \ +static void FUNC_NAME(const uint8_t* y, \ + const uint8_t* u, const uint8_t* v, \ + uint8_t* dst, int len) { \ + int i; \ + uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; \ + const int t_con_1 = 26149; \ + const int t_con_2 = 13320; \ + const int t_con_3 = 6419; \ + const int t_con_4 = 33050; \ + const int t_con_5 = 19077; \ + const int t_con_6 = 14234; \ + const int t_con_7 = 8708; \ + const int t_con_8 = 17685; \ + for (i = 0; i < (len >> 1); i++) { \ + __asm__ volatile ( \ + ROW_FUNC_PART_1() \ + ROW_FUNC_PART_2(R, G, B, 1) \ + ROW_FUNC_PART_2(R + XSTEP, G + XSTEP, B + XSTEP, 0) \ + ASM_CLOBBER_LIST() \ + ); \ + if (A) dst[A] = dst[A + XSTEP] = 0xff; \ + y += 2; \ + ++u; \ + ++v; \ + dst += 2 * XSTEP; \ + } \ + if (len & 1) { \ + __asm__ volatile ( \ + ROW_FUNC_PART_1() \ + ROW_FUNC_PART_2(R, G, B, 0) \ + ASM_CLOBBER_LIST() \ + ); \ + if (A) dst[A] = 0xff; \ + } \ +} + +ROW_FUNC(YuvToRgbRow_MIPSdspR2, 3, 0, 1, 2, 0) +ROW_FUNC(YuvToRgbaRow_MIPSdspR2, 4, 0, 1, 2, 3) +ROW_FUNC(YuvToBgrRow_MIPSdspR2, 3, 2, 1, 0, 0) +ROW_FUNC(YuvToBgraRow_MIPSdspR2, 4, 2, 1, 0, 3) + +#undef ROW_FUNC +#undef ASM_CLOBBER_LIST +#undef ROW_FUNC_PART_2 +#undef ROW_FUNC_PART_1 + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPInitSamplersMIPSdspR2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersMIPSdspR2(void) { + WebPSamplers[MODE_RGB] = YuvToRgbRow_MIPSdspR2; + WebPSamplers[MODE_RGBA] = YuvToRgbaRow_MIPSdspR2; + WebPSamplers[MODE_BGR] = YuvToBgrRow_MIPSdspR2; + WebPSamplers[MODE_BGRA] = YuvToBgraRow_MIPSdspR2; +} + +#else // !WEBP_USE_MIPS_DSP_R2 + +WEBP_DSP_INIT_STUB(WebPInitSamplersMIPSdspR2) + +#endif // WEBP_USE_MIPS_DSP_R2 diff --git a/internal/libwebp-1.2.1/src/dsp/yuv_neon.c b/internal/libwebp-1.2.1/src/dsp/yuv_neon.c new file mode 100644 index 0000000..a34d602 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/yuv_neon.c @@ -0,0 +1,288 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// YUV->RGB conversion functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/yuv.h" + +#if defined(WEBP_USE_NEON) + +#include +#include + +#include "src/dsp/neon.h" + +//----------------------------------------------------------------------------- + +static uint8x8_t ConvertRGBToY_NEON(const uint8x8_t R, + const uint8x8_t G, + const uint8x8_t B) { + const uint16x8_t r = vmovl_u8(R); + const uint16x8_t g = vmovl_u8(G); + const uint16x8_t b = vmovl_u8(B); + const uint16x4_t r_lo = vget_low_u16(r); + const uint16x4_t r_hi = vget_high_u16(r); + const uint16x4_t g_lo = vget_low_u16(g); + const uint16x4_t g_hi = vget_high_u16(g); + const uint16x4_t b_lo = vget_low_u16(b); + const uint16x4_t b_hi = vget_high_u16(b); + const uint32x4_t tmp0_lo = vmull_n_u16( r_lo, 16839u); + const uint32x4_t tmp0_hi = vmull_n_u16( r_hi, 16839u); + const uint32x4_t tmp1_lo = vmlal_n_u16(tmp0_lo, g_lo, 33059u); + const uint32x4_t tmp1_hi = vmlal_n_u16(tmp0_hi, g_hi, 33059u); + const uint32x4_t tmp2_lo = vmlal_n_u16(tmp1_lo, b_lo, 6420u); + const uint32x4_t tmp2_hi = vmlal_n_u16(tmp1_hi, b_hi, 6420u); + const uint16x8_t Y1 = vcombine_u16(vrshrn_n_u32(tmp2_lo, 16), + vrshrn_n_u32(tmp2_hi, 16)); + const uint16x8_t Y2 = vaddq_u16(Y1, vdupq_n_u16(16)); + return vqmovn_u16(Y2); +} + +static void ConvertRGB24ToY_NEON(const uint8_t* rgb, uint8_t* y, int width) { + int i; + for (i = 0; i + 8 <= width; i += 8, rgb += 3 * 8) { + const uint8x8x3_t RGB = vld3_u8(rgb); + const uint8x8_t Y = ConvertRGBToY_NEON(RGB.val[0], RGB.val[1], RGB.val[2]); + vst1_u8(y + i, Y); + } + for (; i < width; ++i, rgb += 3) { // left-over + y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF); + } +} + +static void ConvertBGR24ToY_NEON(const uint8_t* bgr, uint8_t* y, int width) { + int i; + for (i = 0; i + 8 <= width; i += 8, bgr += 3 * 8) { + const uint8x8x3_t BGR = vld3_u8(bgr); + const uint8x8_t Y = ConvertRGBToY_NEON(BGR.val[2], BGR.val[1], BGR.val[0]); + vst1_u8(y + i, Y); + } + for (; i < width; ++i, bgr += 3) { // left-over + y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF); + } +} + +static void ConvertARGBToY_NEON(const uint32_t* argb, uint8_t* y, int width) { + int i; + for (i = 0; i + 8 <= width; i += 8) { + const uint8x8x4_t RGB = vld4_u8((const uint8_t*)&argb[i]); + const uint8x8_t Y = ConvertRGBToY_NEON(RGB.val[2], RGB.val[1], RGB.val[0]); + vst1_u8(y + i, Y); + } + for (; i < width; ++i) { // left-over + const uint32_t p = argb[i]; + y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff, + YUV_HALF); + } +} + +//----------------------------------------------------------------------------- + +// computes: DST_s16 = [(C0 * r + C1 * g + C2 * b) >> 16] + CST +#define MULTIPLY_16b_PREAMBLE(r, g, b) \ + const int16x4_t r_lo = vreinterpret_s16_u16(vget_low_u16(r)); \ + const int16x4_t r_hi = vreinterpret_s16_u16(vget_high_u16(r)); \ + const int16x4_t g_lo = vreinterpret_s16_u16(vget_low_u16(g)); \ + const int16x4_t g_hi = vreinterpret_s16_u16(vget_high_u16(g)); \ + const int16x4_t b_lo = vreinterpret_s16_u16(vget_low_u16(b)); \ + const int16x4_t b_hi = vreinterpret_s16_u16(vget_high_u16(b)) + +#define MULTIPLY_16b(C0, C1, C2, CST, DST_s16) do { \ + const int32x4_t tmp0_lo = vmull_n_s16( r_lo, C0); \ + const int32x4_t tmp0_hi = vmull_n_s16( r_hi, C0); \ + const int32x4_t tmp1_lo = vmlal_n_s16(tmp0_lo, g_lo, C1); \ + const int32x4_t tmp1_hi = vmlal_n_s16(tmp0_hi, g_hi, C1); \ + const int32x4_t tmp2_lo = vmlal_n_s16(tmp1_lo, b_lo, C2); \ + const int32x4_t tmp2_hi = vmlal_n_s16(tmp1_hi, b_hi, C2); \ + const int16x8_t tmp3 = vcombine_s16(vshrn_n_s32(tmp2_lo, 16), \ + vshrn_n_s32(tmp2_hi, 16)); \ + DST_s16 = vaddq_s16(tmp3, vdupq_n_s16(CST)); \ +} while (0) + +// This needs to be a macro, since (128 << SHIFT) needs to be an immediate. +#define CONVERT_RGB_TO_UV(r, g, b, SHIFT, U_DST, V_DST) do { \ + MULTIPLY_16b_PREAMBLE(r, g, b); \ + MULTIPLY_16b(-9719, -19081, 28800, 128 << SHIFT, U_DST); \ + MULTIPLY_16b(28800, -24116, -4684, 128 << SHIFT, V_DST); \ +} while (0) + +static void ConvertRGBA32ToUV_NEON(const uint16_t* rgb, + uint8_t* u, uint8_t* v, int width) { + int i; + for (i = 0; i + 8 <= width; i += 8, rgb += 4 * 8) { + const uint16x8x4_t RGB = vld4q_u16((const uint16_t*)rgb); + int16x8_t U, V; + CONVERT_RGB_TO_UV(RGB.val[0], RGB.val[1], RGB.val[2], 2, U, V); + vst1_u8(u + i, vqrshrun_n_s16(U, 2)); + vst1_u8(v + i, vqrshrun_n_s16(V, 2)); + } + for (; i < width; i += 1, rgb += 4) { + const int r = rgb[0], g = rgb[1], b = rgb[2]; + u[i] = VP8RGBToU(r, g, b, YUV_HALF << 2); + v[i] = VP8RGBToV(r, g, b, YUV_HALF << 2); + } +} + +static void ConvertARGBToUV_NEON(const uint32_t* argb, uint8_t* u, uint8_t* v, + int src_width, int do_store) { + int i; + for (i = 0; i + 16 <= src_width; i += 16, u += 8, v += 8) { + const uint8x16x4_t RGB = vld4q_u8((const uint8_t*)&argb[i]); + const uint16x8_t R = vpaddlq_u8(RGB.val[2]); // pair-wise adds + const uint16x8_t G = vpaddlq_u8(RGB.val[1]); + const uint16x8_t B = vpaddlq_u8(RGB.val[0]); + int16x8_t U_tmp, V_tmp; + CONVERT_RGB_TO_UV(R, G, B, 1, U_tmp, V_tmp); + { + const uint8x8_t U = vqrshrun_n_s16(U_tmp, 1); + const uint8x8_t V = vqrshrun_n_s16(V_tmp, 1); + if (do_store) { + vst1_u8(u, U); + vst1_u8(v, V); + } else { + const uint8x8_t prev_u = vld1_u8(u); + const uint8x8_t prev_v = vld1_u8(v); + vst1_u8(u, vrhadd_u8(U, prev_u)); + vst1_u8(v, vrhadd_u8(V, prev_v)); + } + } + } + if (i < src_width) { // left-over + WebPConvertARGBToUV_C(argb + i, u, v, src_width - i, do_store); + } +} + + +//------------------------------------------------------------------------------ + +extern void WebPInitConvertARGBToYUVNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVNEON(void) { + WebPConvertRGB24ToY = ConvertRGB24ToY_NEON; + WebPConvertBGR24ToY = ConvertBGR24ToY_NEON; + WebPConvertARGBToY = ConvertARGBToY_NEON; + WebPConvertARGBToUV = ConvertARGBToUV_NEON; + WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_NEON; +} + +//------------------------------------------------------------------------------ + +#define MAX_Y ((1 << 10) - 1) // 10b precision over 16b-arithmetic +static uint16_t clip_y_NEON(int v) { + return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v; +} + +static uint64_t SharpYUVUpdateY_NEON(const uint16_t* ref, const uint16_t* src, + uint16_t* dst, int len) { + int i; + const int16x8_t zero = vdupq_n_s16(0); + const int16x8_t max = vdupq_n_s16(MAX_Y); + uint64x2_t sum = vdupq_n_u64(0); + uint64_t diff; + + for (i = 0; i + 8 <= len; i += 8) { + const int16x8_t A = vreinterpretq_s16_u16(vld1q_u16(ref + i)); + const int16x8_t B = vreinterpretq_s16_u16(vld1q_u16(src + i)); + const int16x8_t C = vreinterpretq_s16_u16(vld1q_u16(dst + i)); + const int16x8_t D = vsubq_s16(A, B); // diff_y + const int16x8_t F = vaddq_s16(C, D); // new_y + const uint16x8_t H = + vreinterpretq_u16_s16(vmaxq_s16(vminq_s16(F, max), zero)); + const int16x8_t I = vabsq_s16(D); // abs(diff_y) + vst1q_u16(dst + i, H); + sum = vpadalq_u32(sum, vpaddlq_u16(vreinterpretq_u16_s16(I))); + } + diff = vgetq_lane_u64(sum, 0) + vgetq_lane_u64(sum, 1); + 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_NEON(new_y); + diff += (uint64_t)(abs(diff_y)); + } + return diff; +} + +static void SharpYUVUpdateRGB_NEON(const int16_t* ref, const int16_t* src, + int16_t* dst, int len) { + int i; + for (i = 0; i + 8 <= len; i += 8) { + const int16x8_t A = vld1q_s16(ref + i); + const int16x8_t B = vld1q_s16(src + i); + const int16x8_t C = vld1q_s16(dst + i); + const int16x8_t D = vsubq_s16(A, B); // diff_uv + const int16x8_t E = vaddq_s16(C, D); // new_uv + vst1q_s16(dst + i, E); + } + for (; i < len; ++i) { + const int diff_uv = ref[i] - src[i]; + dst[i] += diff_uv; + } +} + +static void SharpYUVFilterRow_NEON(const int16_t* A, const int16_t* B, int len, + const uint16_t* best_y, uint16_t* out) { + int i; + const int16x8_t max = vdupq_n_s16(MAX_Y); + const int16x8_t zero = vdupq_n_s16(0); + for (i = 0; i + 8 <= len; i += 8) { + const int16x8_t a0 = vld1q_s16(A + i + 0); + const int16x8_t a1 = vld1q_s16(A + i + 1); + const int16x8_t b0 = vld1q_s16(B + i + 0); + const int16x8_t b1 = vld1q_s16(B + i + 1); + const int16x8_t a0b1 = vaddq_s16(a0, b1); + const int16x8_t a1b0 = vaddq_s16(a1, b0); + const int16x8_t a0a1b0b1 = vaddq_s16(a0b1, a1b0); // A0+A1+B0+B1 + const int16x8_t a0b1_2 = vaddq_s16(a0b1, a0b1); // 2*(A0+B1) + const int16x8_t a1b0_2 = vaddq_s16(a1b0, a1b0); // 2*(A1+B0) + const int16x8_t c0 = vshrq_n_s16(vaddq_s16(a0b1_2, a0a1b0b1), 3); + const int16x8_t c1 = vshrq_n_s16(vaddq_s16(a1b0_2, a0a1b0b1), 3); + const int16x8_t d0 = vaddq_s16(c1, a0); + const int16x8_t d1 = vaddq_s16(c0, a1); + const int16x8_t e0 = vrshrq_n_s16(d0, 1); + const int16x8_t e1 = vrshrq_n_s16(d1, 1); + const int16x8x2_t f = vzipq_s16(e0, e1); + const int16x8_t g0 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 0)); + const int16x8_t g1 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 8)); + const int16x8_t h0 = vaddq_s16(g0, f.val[0]); + const int16x8_t h1 = vaddq_s16(g1, f.val[1]); + const int16x8_t i0 = vmaxq_s16(vminq_s16(h0, max), zero); + const int16x8_t i1 = vmaxq_s16(vminq_s16(h1, max), zero); + vst1q_u16(out + 2 * i + 0, vreinterpretq_u16_s16(i0)); + vst1q_u16(out + 2 * i + 8, vreinterpretq_u16_s16(i1)); + } + for (; i < len; ++i) { + 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_NEON(best_y[2 * i + 0] + v0); + out[2 * i + 1] = clip_y_NEON(best_y[2 * i + 1] + v1); + } +} +#undef MAX_Y + +//------------------------------------------------------------------------------ + +extern void WebPInitSharpYUVNEON(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitSharpYUVNEON(void) { + WebPSharpYUVUpdateY = SharpYUVUpdateY_NEON; + WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_NEON; + WebPSharpYUVFilterRow = SharpYUVFilterRow_NEON; +} + +#else // !WEBP_USE_NEON + +WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVNEON) +WEBP_DSP_INIT_STUB(WebPInitSharpYUVNEON) + +#endif // WEBP_USE_NEON diff --git a/internal/libwebp-1.2.1/src/dsp/yuv_sse2.c b/internal/libwebp-1.2.1/src/dsp/yuv_sse2.c new file mode 100644 index 0000000..baa48d5 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/yuv_sse2.c @@ -0,0 +1,874 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// YUV->RGB conversion functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/yuv.h" + +#if defined(WEBP_USE_SSE2) + +#include "src/dsp/common_sse2.h" +#include +#include + +//----------------------------------------------------------------------------- +// Convert spans of 32 pixels to various RGB formats for the fancy upsampler. + +// These constants are 14b fixed-point version of ITU-R BT.601 constants. +// R = (19077 * y + 26149 * v - 14234) >> 6 +// G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6 +// B = (19077 * y + 33050 * u - 17685) >> 6 +static void ConvertYUV444ToRGB_SSE2(const __m128i* const Y0, + const __m128i* const U0, + const __m128i* const V0, + __m128i* const R, + __m128i* const G, + __m128i* const B) { + const __m128i k19077 = _mm_set1_epi16(19077); + const __m128i k26149 = _mm_set1_epi16(26149); + const __m128i k14234 = _mm_set1_epi16(14234); + // 33050 doesn't fit in a signed short: only use this with unsigned arithmetic + const __m128i k33050 = _mm_set1_epi16((short)33050); + const __m128i k17685 = _mm_set1_epi16(17685); + const __m128i k6419 = _mm_set1_epi16(6419); + const __m128i k13320 = _mm_set1_epi16(13320); + const __m128i k8708 = _mm_set1_epi16(8708); + + const __m128i Y1 = _mm_mulhi_epu16(*Y0, k19077); + + const __m128i R0 = _mm_mulhi_epu16(*V0, k26149); + const __m128i R1 = _mm_sub_epi16(Y1, k14234); + const __m128i R2 = _mm_add_epi16(R1, R0); + + const __m128i G0 = _mm_mulhi_epu16(*U0, k6419); + const __m128i G1 = _mm_mulhi_epu16(*V0, k13320); + const __m128i G2 = _mm_add_epi16(Y1, k8708); + const __m128i G3 = _mm_add_epi16(G0, G1); + const __m128i G4 = _mm_sub_epi16(G2, G3); + + // be careful with the saturated *unsigned* arithmetic here! + const __m128i B0 = _mm_mulhi_epu16(*U0, k33050); + const __m128i B1 = _mm_adds_epu16(B0, Y1); + const __m128i B2 = _mm_subs_epu16(B1, k17685); + + // use logical shift for B2, which can be larger than 32767 + *R = _mm_srai_epi16(R2, 6); // range: [-14234, 30815] + *G = _mm_srai_epi16(G4, 6); // range: [-10953, 27710] + *B = _mm_srli_epi16(B2, 6); // range: [0, 34238] +} + +// Load the bytes into the *upper* part of 16b words. That's "<< 8", basically. +static WEBP_INLINE __m128i Load_HI_16_SSE2(const uint8_t* src) { + const __m128i zero = _mm_setzero_si128(); + return _mm_unpacklo_epi8(zero, _mm_loadl_epi64((const __m128i*)src)); +} + +// Load and replicate the U/V samples +static WEBP_INLINE __m128i Load_UV_HI_8_SSE2(const uint8_t* src) { + const __m128i zero = _mm_setzero_si128(); + const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src); + const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0); + return _mm_unpacklo_epi16(tmp1, tmp1); // replicate samples +} + +// Convert 32 samples of YUV444 to R/G/B +static void YUV444ToRGB_SSE2(const uint8_t* const y, + const uint8_t* const u, + const uint8_t* const v, + __m128i* const R, __m128i* const G, + __m128i* const B) { + const __m128i Y0 = Load_HI_16_SSE2(y), U0 = Load_HI_16_SSE2(u), + V0 = Load_HI_16_SSE2(v); + ConvertYUV444ToRGB_SSE2(&Y0, &U0, &V0, R, G, B); +} + +// Convert 32 samples of YUV420 to R/G/B +static void YUV420ToRGB_SSE2(const uint8_t* const y, + const uint8_t* const u, + const uint8_t* const v, + __m128i* const R, __m128i* const G, + __m128i* const B) { + const __m128i Y0 = Load_HI_16_SSE2(y), U0 = Load_UV_HI_8_SSE2(u), + V0 = Load_UV_HI_8_SSE2(v); + ConvertYUV444ToRGB_SSE2(&Y0, &U0, &V0, R, G, B); +} + +// Pack R/G/B/A results into 32b output. +static WEBP_INLINE void PackAndStore4_SSE2(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + const __m128i* const A, + uint8_t* const dst) { + const __m128i rb = _mm_packus_epi16(*R, *B); + const __m128i ga = _mm_packus_epi16(*G, *A); + const __m128i rg = _mm_unpacklo_epi8(rb, ga); + const __m128i ba = _mm_unpackhi_epi8(rb, ga); + const __m128i RGBA_lo = _mm_unpacklo_epi16(rg, ba); + const __m128i RGBA_hi = _mm_unpackhi_epi16(rg, ba); + _mm_storeu_si128((__m128i*)(dst + 0), RGBA_lo); + _mm_storeu_si128((__m128i*)(dst + 16), RGBA_hi); +} + +// Pack R/G/B/A results into 16b output. +static WEBP_INLINE void PackAndStore4444_SSE2(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + const __m128i* const A, + uint8_t* const dst) { +#if (WEBP_SWAP_16BIT_CSP == 0) + const __m128i rg0 = _mm_packus_epi16(*R, *G); + const __m128i ba0 = _mm_packus_epi16(*B, *A); +#else + const __m128i rg0 = _mm_packus_epi16(*B, *A); + const __m128i ba0 = _mm_packus_epi16(*R, *G); +#endif + const __m128i mask_0xf0 = _mm_set1_epi8(0xf0); + const __m128i rb1 = _mm_unpacklo_epi8(rg0, ba0); // rbrbrbrbrb... + const __m128i ga1 = _mm_unpackhi_epi8(rg0, ba0); // gagagagaga... + const __m128i rb2 = _mm_and_si128(rb1, mask_0xf0); + const __m128i ga2 = _mm_srli_epi16(_mm_and_si128(ga1, mask_0xf0), 4); + const __m128i rgba4444 = _mm_or_si128(rb2, ga2); + _mm_storeu_si128((__m128i*)dst, rgba4444); +} + +// Pack R/G/B results into 16b output. +static WEBP_INLINE void PackAndStore565_SSE2(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + uint8_t* const dst) { + const __m128i r0 = _mm_packus_epi16(*R, *R); + const __m128i g0 = _mm_packus_epi16(*G, *G); + const __m128i b0 = _mm_packus_epi16(*B, *B); + const __m128i r1 = _mm_and_si128(r0, _mm_set1_epi8(0xf8)); + const __m128i b1 = _mm_and_si128(_mm_srli_epi16(b0, 3), _mm_set1_epi8(0x1f)); + const __m128i g1 = _mm_srli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0xe0)), 5); + const __m128i g2 = _mm_slli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0x1c)), 3); + const __m128i rg = _mm_or_si128(r1, g1); + const __m128i gb = _mm_or_si128(g2, b1); +#if (WEBP_SWAP_16BIT_CSP == 0) + const __m128i rgb565 = _mm_unpacklo_epi8(rg, gb); +#else + const __m128i rgb565 = _mm_unpacklo_epi8(gb, rg); +#endif + _mm_storeu_si128((__m128i*)dst, rgb565); +} + +// 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_SSE2(__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 + // 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 + VP8PlanarTo24b_SSE2(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_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst) { + const __m128i kAlpha = _mm_set1_epi16(255); + int n; + for (n = 0; n < 32; n += 8, dst += 32) { + __m128i R, G, B; + YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B); + PackAndStore4_SSE2(&R, &G, &B, &kAlpha, dst); + } +} + +void VP8YuvToBgra32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst) { + const __m128i kAlpha = _mm_set1_epi16(255); + int n; + for (n = 0; n < 32; n += 8, dst += 32) { + __m128i R, G, B; + YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B); + PackAndStore4_SSE2(&B, &G, &R, &kAlpha, dst); + } +} + +void VP8YuvToArgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst) { + const __m128i kAlpha = _mm_set1_epi16(255); + int n; + for (n = 0; n < 32; n += 8, dst += 32) { + __m128i R, G, B; + YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B); + PackAndStore4_SSE2(&kAlpha, &R, &G, &B, dst); + } +} + +void VP8YuvToRgba444432_SSE2(const uint8_t* y, const uint8_t* u, + const uint8_t* v, uint8_t* dst) { + const __m128i kAlpha = _mm_set1_epi16(255); + int n; + for (n = 0; n < 32; n += 8, dst += 16) { + __m128i R, G, B; + YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B); + PackAndStore4444_SSE2(&R, &G, &B, &kAlpha, dst); + } +} + +void VP8YuvToRgb56532_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst) { + int n; + for (n = 0; n < 32; n += 8, dst += 16) { + __m128i R, G, B; + YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B); + PackAndStore565_SSE2(&R, &G, &B, dst); + } +} + +void VP8YuvToRgb32_SSE2(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 rgb0, rgb1, rgb2, rgb3, rgb4, rgb5; + + YUV444ToRGB_SSE2(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV444ToRGB_SSE2(y + 8, u + 8, v + 8, &R1, &G1, &B1); + YUV444ToRGB_SSE2(y + 16, u + 16, v + 16, &R2, &G2, &B2); + YUV444ToRGB_SSE2(y + 24, u + 24, v + 24, &R3, &G3, &B3); + + // Cast to 8b and store as RRRRGGGGBBBB. + 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_SSE2(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst); +} + +void VP8YuvToBgr32_SSE2(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 bgr0, bgr1, bgr2, bgr3, bgr4, bgr5; + + YUV444ToRGB_SSE2(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV444ToRGB_SSE2(y + 8, u + 8, v + 8, &R1, &G1, &B1); + YUV444ToRGB_SSE2(y + 16, u + 16, v + 16, &R2, &G2, &B2); + YUV444ToRGB_SSE2(y + 24, u + 24, v + 24, &R3, &G3, &B3); + + // Cast to 8b and store as BBBBGGGGRRRR. + 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_SSE2(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst); +} + +//----------------------------------------------------------------------------- +// Arbitrary-length row conversion functions + +static void YuvToRgbaRow_SSE2(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len) { + const __m128i kAlpha = _mm_set1_epi16(255); + int n; + for (n = 0; n + 8 <= len; n += 8, dst += 32) { + __m128i R, G, B; + YUV420ToRGB_SSE2(y, u, v, &R, &G, &B); + PackAndStore4_SSE2(&R, &G, &B, &kAlpha, dst); + y += 8; + u += 4; + v += 4; + } + for (; n < len; ++n) { // Finish off + VP8YuvToRgba(y[0], u[0], v[0], dst); + dst += 4; + y += 1; + u += (n & 1); + v += (n & 1); + } +} + +static void YuvToBgraRow_SSE2(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len) { + const __m128i kAlpha = _mm_set1_epi16(255); + int n; + for (n = 0; n + 8 <= len; n += 8, dst += 32) { + __m128i R, G, B; + YUV420ToRGB_SSE2(y, u, v, &R, &G, &B); + PackAndStore4_SSE2(&B, &G, &R, &kAlpha, dst); + y += 8; + u += 4; + v += 4; + } + for (; n < len; ++n) { // Finish off + VP8YuvToBgra(y[0], u[0], v[0], dst); + dst += 4; + y += 1; + u += (n & 1); + v += (n & 1); + } +} + +static void YuvToArgbRow_SSE2(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len) { + const __m128i kAlpha = _mm_set1_epi16(255); + int n; + for (n = 0; n + 8 <= len; n += 8, dst += 32) { + __m128i R, G, B; + YUV420ToRGB_SSE2(y, u, v, &R, &G, &B); + PackAndStore4_SSE2(&kAlpha, &R, &G, &B, dst); + y += 8; + u += 4; + v += 4; + } + for (; n < len; ++n) { // Finish off + VP8YuvToArgb(y[0], u[0], v[0], dst); + dst += 4; + y += 1; + u += (n & 1); + v += (n & 1); + } +} + +static void YuvToRgbRow_SSE2(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len) { + 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 rgb0, rgb1, rgb2, rgb3, rgb4, rgb5; + + YUV420ToRGB_SSE2(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV420ToRGB_SSE2(y + 8, u + 4, v + 4, &R1, &G1, &B1); + YUV420ToRGB_SSE2(y + 16, u + 8, v + 8, &R2, &G2, &B2); + YUV420ToRGB_SSE2(y + 24, u + 12, v + 12, &R3, &G3, &B3); + + // Cast to 8b and store as RRRRGGGGBBBB. + 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_SSE2(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst); + + y += 32; + u += 16; + v += 16; + } + for (; n < len; ++n) { // Finish off + VP8YuvToRgb(y[0], u[0], v[0], dst); + dst += 3; + y += 1; + u += (n & 1); + v += (n & 1); + } +} + +static void YuvToBgrRow_SSE2(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len) { + 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 bgr0, bgr1, bgr2, bgr3, bgr4, bgr5; + + YUV420ToRGB_SSE2(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV420ToRGB_SSE2(y + 8, u + 4, v + 4, &R1, &G1, &B1); + YUV420ToRGB_SSE2(y + 16, u + 8, v + 8, &R2, &G2, &B2); + YUV420ToRGB_SSE2(y + 24, u + 12, v + 12, &R3, &G3, &B3); + + // Cast to 8b and store as BBBBGGGGRRRR. + 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_SSE2(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst); + + y += 32; + u += 16; + v += 16; + } + for (; n < len; ++n) { // Finish off + VP8YuvToBgr(y[0], u[0], v[0], dst); + dst += 3; + y += 1; + u += (n & 1); + v += (n & 1); + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPInitSamplersSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersSSE2(void) { + WebPSamplers[MODE_RGB] = YuvToRgbRow_SSE2; + WebPSamplers[MODE_RGBA] = YuvToRgbaRow_SSE2; + WebPSamplers[MODE_BGR] = YuvToBgrRow_SSE2; + WebPSamplers[MODE_BGRA] = YuvToBgraRow_SSE2; + WebPSamplers[MODE_ARGB] = YuvToArgbRow_SSE2; +} + +//------------------------------------------------------------------------------ +// RGB24/32 -> YUV converters + +// Load eight 16b-words from *src. +#define LOAD_16(src) _mm_loadu_si128((const __m128i*)(src)) +// Store either 16b-words into *dst +#define STORE_16(V, dst) _mm_storeu_si128((__m128i*)(dst), (V)) + +// Function that inserts a value of the second half of the in buffer in between +// every two char of the first half. +static WEBP_INLINE void RGB24PackedToPlanarHelper_SSE2( + const __m128i* const in /*in[6]*/, __m128i* const out /*out[6]*/) { + out[0] = _mm_unpacklo_epi8(in[0], in[3]); + out[1] = _mm_unpackhi_epi8(in[0], in[3]); + out[2] = _mm_unpacklo_epi8(in[1], in[4]); + out[3] = _mm_unpackhi_epi8(in[1], in[4]); + out[4] = _mm_unpacklo_epi8(in[2], in[5]); + out[5] = _mm_unpackhi_epi8(in[2], in[5]); +} + +// Unpack the 8b input rgbrgbrgbrgb ... as contiguous registers: +// rrrr... rrrr... gggg... gggg... bbbb... bbbb.... +// Similar to PlanarTo24bHelper(), but in reverse order. +static WEBP_INLINE void RGB24PackedToPlanar_SSE2( + const uint8_t* const rgb, __m128i* const out /*out[6]*/) { + __m128i tmp[6]; + tmp[0] = _mm_loadu_si128((const __m128i*)(rgb + 0)); + tmp[1] = _mm_loadu_si128((const __m128i*)(rgb + 16)); + tmp[2] = _mm_loadu_si128((const __m128i*)(rgb + 32)); + tmp[3] = _mm_loadu_si128((const __m128i*)(rgb + 48)); + tmp[4] = _mm_loadu_si128((const __m128i*)(rgb + 64)); + tmp[5] = _mm_loadu_si128((const __m128i*)(rgb + 80)); + + RGB24PackedToPlanarHelper_SSE2(tmp, out); + RGB24PackedToPlanarHelper_SSE2(out, tmp); + RGB24PackedToPlanarHelper_SSE2(tmp, out); + RGB24PackedToPlanarHelper_SSE2(out, tmp); + RGB24PackedToPlanarHelper_SSE2(tmp, out); +} + +// Convert 8 packed ARGB to r[], g[], b[] +static WEBP_INLINE void RGB32PackedToPlanar_SSE2(const uint32_t* const argb, + __m128i* const rgb /*in[6]*/) { + const __m128i zero = _mm_setzero_si128(); + __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_SSE2(&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 +// It's a macro and not a function because we need to use immediate values with +// srai_epi32, e.g. +#define TRANSFORM(RG_LO, RG_HI, GB_LO, GB_HI, MULT_RG, MULT_GB, \ + ROUNDER, DESCALE_FIX, OUT) do { \ + const __m128i V0_lo = _mm_madd_epi16(RG_LO, MULT_RG); \ + const __m128i V0_hi = _mm_madd_epi16(RG_HI, MULT_RG); \ + const __m128i V1_lo = _mm_madd_epi16(GB_LO, MULT_GB); \ + const __m128i V1_hi = _mm_madd_epi16(GB_HI, MULT_GB); \ + const __m128i V2_lo = _mm_add_epi32(V0_lo, V1_lo); \ + const __m128i V2_hi = _mm_add_epi32(V0_hi, V1_hi); \ + const __m128i V3_lo = _mm_add_epi32(V2_lo, ROUNDER); \ + const __m128i V3_hi = _mm_add_epi32(V2_hi, ROUNDER); \ + const __m128i V5_lo = _mm_srai_epi32(V3_lo, DESCALE_FIX); \ + const __m128i V5_hi = _mm_srai_epi32(V3_hi, DESCALE_FIX); \ + (OUT) = _mm_packs_epi32(V5_lo, V5_hi); \ +} while (0) + +#define MK_CST_16(A, B) _mm_set_epi16((B), (A), (B), (A), (B), (A), (B), (A)) +static WEBP_INLINE void ConvertRGBToY_SSE2(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + __m128i* const Y) { + const __m128i kRG_y = MK_CST_16(16839, 33059 - 16384); + const __m128i kGB_y = MK_CST_16(16384, 6420); + const __m128i kHALF_Y = _mm_set1_epi32((16 << YUV_FIX) + YUV_HALF); + + const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G); + const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G); + const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B); + const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B); + TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_y, kGB_y, kHALF_Y, YUV_FIX, *Y); +} + +static WEBP_INLINE void ConvertRGBToUV_SSE2(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + __m128i* const U, + __m128i* const V) { + const __m128i kRG_u = MK_CST_16(-9719, -19081); + const __m128i kGB_u = MK_CST_16(0, 28800); + const __m128i kRG_v = MK_CST_16(28800, 0); + const __m128i kGB_v = MK_CST_16(-24116, -4684); + const __m128i kHALF_UV = _mm_set1_epi32(((128 << YUV_FIX) + YUV_HALF) << 2); + + const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G); + const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G); + const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B); + const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B); + TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_u, kGB_u, + kHALF_UV, YUV_FIX + 2, *U); + TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_v, kGB_v, + kHALF_UV, YUV_FIX + 2, *V); +} + +#undef MK_CST_16 +#undef TRANSFORM + +static void ConvertRGB24ToY_SSE2(const uint8_t* rgb, uint8_t* y, int width) { + const int max_width = width & ~31; + int i; + for (i = 0; i < max_width; rgb += 3 * 16 * 2) { + __m128i rgb_plane[6]; + int j; + + RGB24PackedToPlanar_SSE2(rgb, rgb_plane); + + for (j = 0; j < 2; ++j, i += 16) { + const __m128i zero = _mm_setzero_si128(); + __m128i r, g, b, Y0, Y1; + + // Convert to 16-bit Y. + r = _mm_unpacklo_epi8(rgb_plane[0 + j], zero); + g = _mm_unpacklo_epi8(rgb_plane[2 + j], zero); + b = _mm_unpacklo_epi8(rgb_plane[4 + j], zero); + ConvertRGBToY_SSE2(&r, &g, &b, &Y0); + + // Convert to 16-bit Y. + r = _mm_unpackhi_epi8(rgb_plane[0 + j], zero); + g = _mm_unpackhi_epi8(rgb_plane[2 + j], zero); + b = _mm_unpackhi_epi8(rgb_plane[4 + j], zero); + ConvertRGBToY_SSE2(&r, &g, &b, &Y1); + + // Cast to 8-bit and store. + STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + } + } + for (; i < width; ++i, rgb += 3) { // left-over + y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF); + } +} + +static void ConvertBGR24ToY_SSE2(const uint8_t* bgr, uint8_t* y, int width) { + const int max_width = width & ~31; + int i; + for (i = 0; i < max_width; bgr += 3 * 16 * 2) { + __m128i bgr_plane[6]; + int j; + + RGB24PackedToPlanar_SSE2(bgr, bgr_plane); + + for (j = 0; j < 2; ++j, i += 16) { + const __m128i zero = _mm_setzero_si128(); + __m128i r, g, b, Y0, Y1; + + // Convert to 16-bit Y. + b = _mm_unpacklo_epi8(bgr_plane[0 + j], zero); + g = _mm_unpacklo_epi8(bgr_plane[2 + j], zero); + r = _mm_unpacklo_epi8(bgr_plane[4 + j], zero); + ConvertRGBToY_SSE2(&r, &g, &b, &Y0); + + // Convert to 16-bit Y. + b = _mm_unpackhi_epi8(bgr_plane[0 + j], zero); + g = _mm_unpackhi_epi8(bgr_plane[2 + j], zero); + r = _mm_unpackhi_epi8(bgr_plane[4 + j], zero); + ConvertRGBToY_SSE2(&r, &g, &b, &Y1); + + // Cast to 8-bit and store. + STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + } + } + for (; i < width; ++i, bgr += 3) { // left-over + y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF); + } +} + +static void ConvertARGBToY_SSE2(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 Y0, Y1, rgb[6]; + RGB32PackedToPlanar_SSE2(&argb[i], rgb); + ConvertRGBToY_SSE2(&rgb[0], &rgb[2], &rgb[4], &Y0); + ConvertRGBToY_SSE2(&rgb[1], &rgb[3], &rgb[5], &Y1); + STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + } + for (; i < width; ++i) { // left-over + const uint32_t p = argb[i]; + y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff, + YUV_HALF); + } +} + +// Horizontal add (doubled) of two 16b values, result is 16b. +// in: A | B | C | D | ... -> out: 2*(A+B) | 2*(C+D) | ... +static void HorizontalAddPack_SSE2(const __m128i* const A, + const __m128i* const B, + __m128i* const out) { + const __m128i k2 = _mm_set1_epi16(2); + const __m128i C = _mm_madd_epi16(*A, k2); + const __m128i D = _mm_madd_epi16(*B, k2); + *out = _mm_packs_epi32(C, D); +} + +static void ConvertARGBToUV_SSE2(const uint32_t* argb, + uint8_t* u, uint8_t* v, + int src_width, int do_store) { + const int max_width = src_width & ~31; + int i; + for (i = 0; i < max_width; i += 32, u += 16, v += 16) { + __m128i rgb[6], U0, V0, U1, V1; + RGB32PackedToPlanar_SSE2(&argb[i], rgb); + HorizontalAddPack_SSE2(&rgb[0], &rgb[1], &rgb[0]); + HorizontalAddPack_SSE2(&rgb[2], &rgb[3], &rgb[2]); + HorizontalAddPack_SSE2(&rgb[4], &rgb[5], &rgb[4]); + ConvertRGBToUV_SSE2(&rgb[0], &rgb[2], &rgb[4], &U0, &V0); + + RGB32PackedToPlanar_SSE2(&argb[i + 16], rgb); + HorizontalAddPack_SSE2(&rgb[0], &rgb[1], &rgb[0]); + HorizontalAddPack_SSE2(&rgb[2], &rgb[3], &rgb[2]); + HorizontalAddPack_SSE2(&rgb[4], &rgb[5], &rgb[4]); + ConvertRGBToUV_SSE2(&rgb[0], &rgb[2], &rgb[4], &U1, &V1); + + U0 = _mm_packus_epi16(U0, U1); + V0 = _mm_packus_epi16(V0, V1); + if (!do_store) { + const __m128i prev_u = LOAD_16(u); + const __m128i prev_v = LOAD_16(v); + U0 = _mm_avg_epu8(U0, prev_u); + V0 = _mm_avg_epu8(V0, prev_v); + } + STORE_16(U0, u); + STORE_16(V0, v); + } + if (i < src_width) { // left-over + WebPConvertARGBToUV_C(argb + i, u, v, src_width - i, do_store); + } +} + +// Convert 16 packed ARGB 16b-values to r[], g[], b[] +static WEBP_INLINE void RGBA32PackedToPlanar_16b_SSE2( + const uint16_t* const rgbx, + __m128i* const r, __m128i* const g, __m128i* const b) { + const __m128i in0 = LOAD_16(rgbx + 0); // r0 | g0 | b0 |x| r1 | g1 | b1 |x + const __m128i in1 = LOAD_16(rgbx + 8); // r2 | g2 | b2 |x| r3 | g3 | b3 |x + const __m128i in2 = LOAD_16(rgbx + 16); // r4 | ... + const __m128i in3 = LOAD_16(rgbx + 24); // r6 | ... + // column-wise transpose + const __m128i A0 = _mm_unpacklo_epi16(in0, in1); + const __m128i A1 = _mm_unpackhi_epi16(in0, in1); + const __m128i A2 = _mm_unpacklo_epi16(in2, in3); + const __m128i A3 = _mm_unpackhi_epi16(in2, in3); + const __m128i B0 = _mm_unpacklo_epi16(A0, A1); // r0 r1 r2 r3 | g0 g1 .. + const __m128i B1 = _mm_unpackhi_epi16(A0, A1); // b0 b1 b2 b3 | x x x x + const __m128i B2 = _mm_unpacklo_epi16(A2, A3); // r4 r5 r6 r7 | g4 g5 .. + const __m128i B3 = _mm_unpackhi_epi16(A2, A3); // b4 b5 b6 b7 | x x x x + *r = _mm_unpacklo_epi64(B0, B2); + *g = _mm_unpackhi_epi64(B0, B2); + *b = _mm_unpacklo_epi64(B1, B3); +} + +static void ConvertRGBA32ToUV_SSE2(const uint16_t* rgb, + uint8_t* u, uint8_t* v, int width) { + const int max_width = width & ~15; + const uint16_t* const last_rgb = rgb + 4 * max_width; + while (rgb < last_rgb) { + __m128i r, g, b, U0, V0, U1, V1; + RGBA32PackedToPlanar_16b_SSE2(rgb + 0, &r, &g, &b); + ConvertRGBToUV_SSE2(&r, &g, &b, &U0, &V0); + RGBA32PackedToPlanar_16b_SSE2(rgb + 32, &r, &g, &b); + ConvertRGBToUV_SSE2(&r, &g, &b, &U1, &V1); + STORE_16(_mm_packus_epi16(U0, U1), u); + STORE_16(_mm_packus_epi16(V0, V1), v); + u += 16; + v += 16; + rgb += 2 * 32; + } + if (max_width < width) { // left-over + WebPConvertRGBA32ToUV_C(rgb, u, v, width - max_width); + } +} + +//------------------------------------------------------------------------------ + +extern void WebPInitConvertARGBToYUVSSE2(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVSSE2(void) { + WebPConvertARGBToY = ConvertARGBToY_SSE2; + WebPConvertARGBToUV = ConvertARGBToUV_SSE2; + + WebPConvertRGB24ToY = ConvertRGB24ToY_SSE2; + WebPConvertBGR24ToY = ConvertBGR24ToY_SSE2; + + WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_SSE2; +} + +//------------------------------------------------------------------------------ + +#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/internal/libwebp-1.2.1/src/dsp/yuv_sse41.c b/internal/libwebp-1.2.1/src/dsp/yuv_sse41.c new file mode 100644 index 0000000..579d1f7 --- /dev/null +++ b/internal/libwebp-1.2.1/src/dsp/yuv_sse41.c @@ -0,0 +1,613 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// YUV->RGB conversion functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include "src/dsp/yuv.h" + +#if defined(WEBP_USE_SSE41) + +#include "src/dsp/common_sse41.h" +#include +#include + +//----------------------------------------------------------------------------- +// Convert spans of 32 pixels to various RGB formats for the fancy upsampler. + +// These constants are 14b fixed-point version of ITU-R BT.601 constants. +// R = (19077 * y + 26149 * v - 14234) >> 6 +// G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6 +// B = (19077 * y + 33050 * u - 17685) >> 6 +static void ConvertYUV444ToRGB_SSE41(const __m128i* const Y0, + const __m128i* const U0, + const __m128i* const V0, + __m128i* const R, + __m128i* const G, + __m128i* const B) { + const __m128i k19077 = _mm_set1_epi16(19077); + const __m128i k26149 = _mm_set1_epi16(26149); + const __m128i k14234 = _mm_set1_epi16(14234); + // 33050 doesn't fit in a signed short: only use this with unsigned arithmetic + const __m128i k33050 = _mm_set1_epi16((short)33050); + const __m128i k17685 = _mm_set1_epi16(17685); + const __m128i k6419 = _mm_set1_epi16(6419); + const __m128i k13320 = _mm_set1_epi16(13320); + const __m128i k8708 = _mm_set1_epi16(8708); + + const __m128i Y1 = _mm_mulhi_epu16(*Y0, k19077); + + const __m128i R0 = _mm_mulhi_epu16(*V0, k26149); + const __m128i R1 = _mm_sub_epi16(Y1, k14234); + const __m128i R2 = _mm_add_epi16(R1, R0); + + const __m128i G0 = _mm_mulhi_epu16(*U0, k6419); + const __m128i G1 = _mm_mulhi_epu16(*V0, k13320); + const __m128i G2 = _mm_add_epi16(Y1, k8708); + const __m128i G3 = _mm_add_epi16(G0, G1); + const __m128i G4 = _mm_sub_epi16(G2, G3); + + // be careful with the saturated *unsigned* arithmetic here! + const __m128i B0 = _mm_mulhi_epu16(*U0, k33050); + const __m128i B1 = _mm_adds_epu16(B0, Y1); + const __m128i B2 = _mm_subs_epu16(B1, k17685); + + // use logical shift for B2, which can be larger than 32767 + *R = _mm_srai_epi16(R2, 6); // range: [-14234, 30815] + *G = _mm_srai_epi16(G4, 6); // range: [-10953, 27710] + *B = _mm_srli_epi16(B2, 6); // range: [0, 34238] +} + +// Load the bytes into the *upper* part of 16b words. That's "<< 8", basically. +static WEBP_INLINE __m128i Load_HI_16_SSE41(const uint8_t* src) { + const __m128i zero = _mm_setzero_si128(); + return _mm_unpacklo_epi8(zero, _mm_loadl_epi64((const __m128i*)src)); +} + +// Load and replicate the U/V samples +static WEBP_INLINE __m128i Load_UV_HI_8_SSE41(const uint8_t* src) { + const __m128i zero = _mm_setzero_si128(); + const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src); + const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0); + return _mm_unpacklo_epi16(tmp1, tmp1); // replicate samples +} + +// Convert 32 samples of YUV444 to R/G/B +static void YUV444ToRGB_SSE41(const uint8_t* const y, + const uint8_t* const u, + const uint8_t* const v, + __m128i* const R, __m128i* const G, + __m128i* const B) { + const __m128i Y0 = Load_HI_16_SSE41(y), U0 = Load_HI_16_SSE41(u), + V0 = Load_HI_16_SSE41(v); + ConvertYUV444ToRGB_SSE41(&Y0, &U0, &V0, R, G, B); +} + +// Convert 32 samples of YUV420 to R/G/B +static void YUV420ToRGB_SSE41(const uint8_t* const y, + const uint8_t* const u, + const uint8_t* const v, + __m128i* const R, __m128i* const G, + __m128i* const B) { + const __m128i Y0 = Load_HI_16_SSE41(y), U0 = Load_UV_HI_8_SSE41(u), + V0 = Load_UV_HI_8_SSE41(v); + ConvertYUV444ToRGB_SSE41(&Y0, &U0, &V0, R, G, B); +} + +// 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_SSE41( + __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 + // 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 + VP8PlanarTo24b_SSE41(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 VP8YuvToRgb32_SSE41(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 rgb0, rgb1, rgb2, rgb3, rgb4, rgb5; + + YUV444ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV444ToRGB_SSE41(y + 8, u + 8, v + 8, &R1, &G1, &B1); + YUV444ToRGB_SSE41(y + 16, u + 16, v + 16, &R2, &G2, &B2); + YUV444ToRGB_SSE41(y + 24, u + 24, v + 24, &R3, &G3, &B3); + + // Cast to 8b and store as RRRRGGGGBBBB. + 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_SSE41(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst); +} + +void VP8YuvToBgr32_SSE41(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 bgr0, bgr1, bgr2, bgr3, bgr4, bgr5; + + YUV444ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV444ToRGB_SSE41(y + 8, u + 8, v + 8, &R1, &G1, &B1); + YUV444ToRGB_SSE41(y + 16, u + 16, v + 16, &R2, &G2, &B2); + YUV444ToRGB_SSE41(y + 24, u + 24, v + 24, &R3, &G3, &B3); + + // Cast to 8b and store as BBBBGGGGRRRR. + 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_SSE41(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst); +} + +//----------------------------------------------------------------------------- +// Arbitrary-length row conversion functions + +static void YuvToRgbRow_SSE41(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len) { + 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 rgb0, rgb1, rgb2, rgb3, rgb4, rgb5; + + YUV420ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV420ToRGB_SSE41(y + 8, u + 4, v + 4, &R1, &G1, &B1); + YUV420ToRGB_SSE41(y + 16, u + 8, v + 8, &R2, &G2, &B2); + YUV420ToRGB_SSE41(y + 24, u + 12, v + 12, &R3, &G3, &B3); + + // Cast to 8b and store as RRRRGGGGBBBB. + 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_SSE41(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst); + + y += 32; + u += 16; + v += 16; + } + for (; n < len; ++n) { // Finish off + VP8YuvToRgb(y[0], u[0], v[0], dst); + dst += 3; + y += 1; + u += (n & 1); + v += (n & 1); + } +} + +static void YuvToBgrRow_SSE41(const uint8_t* y, + const uint8_t* u, const uint8_t* v, + uint8_t* dst, int len) { + 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 bgr0, bgr1, bgr2, bgr3, bgr4, bgr5; + + YUV420ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV420ToRGB_SSE41(y + 8, u + 4, v + 4, &R1, &G1, &B1); + YUV420ToRGB_SSE41(y + 16, u + 8, v + 8, &R2, &G2, &B2); + YUV420ToRGB_SSE41(y + 24, u + 12, v + 12, &R3, &G3, &B3); + + // Cast to 8b and store as BBBBGGGGRRRR. + 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_SSE41(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst); + + y += 32; + u += 16; + v += 16; + } + for (; n < len; ++n) { // Finish off + VP8YuvToBgr(y[0], u[0], v[0], dst); + dst += 3; + y += 1; + u += (n & 1); + v += (n & 1); + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void WebPInitSamplersSSE41(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersSSE41(void) { + WebPSamplers[MODE_RGB] = YuvToRgbRow_SSE41; + WebPSamplers[MODE_BGR] = YuvToBgrRow_SSE41; +} + +//------------------------------------------------------------------------------ +// RGB24/32 -> YUV converters + +// Load eight 16b-words from *src. +#define LOAD_16(src) _mm_loadu_si128((const __m128i*)(src)) +// Store either 16b-words into *dst +#define STORE_16(V, dst) _mm_storeu_si128((__m128i*)(dst), (V)) + +#define WEBP_SSE41_SHUFF(OUT) do { \ + const __m128i tmp0 = _mm_shuffle_epi8(A0, shuff0); \ + const __m128i tmp1 = _mm_shuffle_epi8(A1, shuff1); \ + const __m128i tmp2 = _mm_shuffle_epi8(A2, shuff2); \ + const __m128i tmp3 = _mm_shuffle_epi8(A3, shuff0); \ + const __m128i tmp4 = _mm_shuffle_epi8(A4, shuff1); \ + const __m128i tmp5 = _mm_shuffle_epi8(A5, shuff2); \ + \ + /* OR everything to get one channel */ \ + const __m128i tmp6 = _mm_or_si128(tmp0, tmp1); \ + const __m128i tmp7 = _mm_or_si128(tmp3, tmp4); \ + out[OUT + 0] = _mm_or_si128(tmp6, tmp2); \ + out[OUT + 1] = _mm_or_si128(tmp7, tmp5); \ +} while (0); + +// Unpack the 8b input rgbrgbrgbrgb ... as contiguous registers: +// rrrr... rrrr... gggg... gggg... bbbb... bbbb.... +// Similar to PlanarTo24bHelper(), but in reverse order. +static WEBP_INLINE void RGB24PackedToPlanar_SSE41( + const uint8_t* const rgb, __m128i* const out /*out[6]*/) { + const __m128i A0 = _mm_loadu_si128((const __m128i*)(rgb + 0)); + const __m128i A1 = _mm_loadu_si128((const __m128i*)(rgb + 16)); + const __m128i A2 = _mm_loadu_si128((const __m128i*)(rgb + 32)); + const __m128i A3 = _mm_loadu_si128((const __m128i*)(rgb + 48)); + const __m128i A4 = _mm_loadu_si128((const __m128i*)(rgb + 64)); + const __m128i A5 = _mm_loadu_si128((const __m128i*)(rgb + 80)); + + // Compute RR. + { + const __m128i shuff0 = _mm_set_epi8( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 12, 9, 6, 3, 0); + const __m128i shuff1 = _mm_set_epi8( + -1, -1, -1, -1, -1, 14, 11, 8, 5, 2, -1, -1, -1, -1, -1, -1); + const __m128i shuff2 = _mm_set_epi8( + 13, 10, 7, 4, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + WEBP_SSE41_SHUFF(0) + } + // Compute GG. + { + const __m128i shuff0 = _mm_set_epi8( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13, 10, 7, 4, 1); + const __m128i shuff1 = _mm_set_epi8( + -1, -1, -1, -1, -1, 15, 12, 9, 6, 3, 0, -1, -1, -1, -1, -1); + const __m128i shuff2 = _mm_set_epi8( + 14, 11, 8, 5, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + WEBP_SSE41_SHUFF(2) + } + // Compute BB. + { + const __m128i shuff0 = _mm_set_epi8( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14, 11, 8, 5, 2); + const __m128i shuff1 = _mm_set_epi8( + -1, -1, -1, -1, -1, -1, 13, 10, 7, 4, 1, -1, -1, -1, -1, -1); + const __m128i shuff2 = _mm_set_epi8( + 15, 12, 9, 6, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); + WEBP_SSE41_SHUFF(4) + } +} + +#undef WEBP_SSE41_SHUFF + +// Convert 8 packed ARGB to r[], g[], b[] +static WEBP_INLINE void RGB32PackedToPlanar_SSE41( + const uint32_t* const argb, __m128i* const rgb /*in[6]*/) { + const __m128i zero = _mm_setzero_si128(); + __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_SSE41(&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 +// It's a macro and not a function because we need to use immediate values with +// srai_epi32, e.g. +#define TRANSFORM(RG_LO, RG_HI, GB_LO, GB_HI, MULT_RG, MULT_GB, \ + ROUNDER, DESCALE_FIX, OUT) do { \ + const __m128i V0_lo = _mm_madd_epi16(RG_LO, MULT_RG); \ + const __m128i V0_hi = _mm_madd_epi16(RG_HI, MULT_RG); \ + const __m128i V1_lo = _mm_madd_epi16(GB_LO, MULT_GB); \ + const __m128i V1_hi = _mm_madd_epi16(GB_HI, MULT_GB); \ + const __m128i V2_lo = _mm_add_epi32(V0_lo, V1_lo); \ + const __m128i V2_hi = _mm_add_epi32(V0_hi, V1_hi); \ + const __m128i V3_lo = _mm_add_epi32(V2_lo, ROUNDER); \ + const __m128i V3_hi = _mm_add_epi32(V2_hi, ROUNDER); \ + const __m128i V5_lo = _mm_srai_epi32(V3_lo, DESCALE_FIX); \ + const __m128i V5_hi = _mm_srai_epi32(V3_hi, DESCALE_FIX); \ + (OUT) = _mm_packs_epi32(V5_lo, V5_hi); \ +} while (0) + +#define MK_CST_16(A, B) _mm_set_epi16((B), (A), (B), (A), (B), (A), (B), (A)) +static WEBP_INLINE void ConvertRGBToY_SSE41(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + __m128i* const Y) { + const __m128i kRG_y = MK_CST_16(16839, 33059 - 16384); + const __m128i kGB_y = MK_CST_16(16384, 6420); + const __m128i kHALF_Y = _mm_set1_epi32((16 << YUV_FIX) + YUV_HALF); + + const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G); + const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G); + const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B); + const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B); + TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_y, kGB_y, kHALF_Y, YUV_FIX, *Y); +} + +static WEBP_INLINE void ConvertRGBToUV_SSE41(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + __m128i* const U, + __m128i* const V) { + const __m128i kRG_u = MK_CST_16(-9719, -19081); + const __m128i kGB_u = MK_CST_16(0, 28800); + const __m128i kRG_v = MK_CST_16(28800, 0); + const __m128i kGB_v = MK_CST_16(-24116, -4684); + const __m128i kHALF_UV = _mm_set1_epi32(((128 << YUV_FIX) + YUV_HALF) << 2); + + const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G); + const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G); + const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B); + const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B); + TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_u, kGB_u, + kHALF_UV, YUV_FIX + 2, *U); + TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_v, kGB_v, + kHALF_UV, YUV_FIX + 2, *V); +} + +#undef MK_CST_16 +#undef TRANSFORM + +static void ConvertRGB24ToY_SSE41(const uint8_t* rgb, uint8_t* y, int width) { + const int max_width = width & ~31; + int i; + for (i = 0; i < max_width; rgb += 3 * 16 * 2) { + __m128i rgb_plane[6]; + int j; + + RGB24PackedToPlanar_SSE41(rgb, rgb_plane); + + for (j = 0; j < 2; ++j, i += 16) { + const __m128i zero = _mm_setzero_si128(); + __m128i r, g, b, Y0, Y1; + + // Convert to 16-bit Y. + r = _mm_unpacklo_epi8(rgb_plane[0 + j], zero); + g = _mm_unpacklo_epi8(rgb_plane[2 + j], zero); + b = _mm_unpacklo_epi8(rgb_plane[4 + j], zero); + ConvertRGBToY_SSE41(&r, &g, &b, &Y0); + + // Convert to 16-bit Y. + r = _mm_unpackhi_epi8(rgb_plane[0 + j], zero); + g = _mm_unpackhi_epi8(rgb_plane[2 + j], zero); + b = _mm_unpackhi_epi8(rgb_plane[4 + j], zero); + ConvertRGBToY_SSE41(&r, &g, &b, &Y1); + + // Cast to 8-bit and store. + STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + } + } + for (; i < width; ++i, rgb += 3) { // left-over + y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF); + } +} + +static void ConvertBGR24ToY_SSE41(const uint8_t* bgr, uint8_t* y, int width) { + const int max_width = width & ~31; + int i; + for (i = 0; i < max_width; bgr += 3 * 16 * 2) { + __m128i bgr_plane[6]; + int j; + + RGB24PackedToPlanar_SSE41(bgr, bgr_plane); + + for (j = 0; j < 2; ++j, i += 16) { + const __m128i zero = _mm_setzero_si128(); + __m128i r, g, b, Y0, Y1; + + // Convert to 16-bit Y. + b = _mm_unpacklo_epi8(bgr_plane[0 + j], zero); + g = _mm_unpacklo_epi8(bgr_plane[2 + j], zero); + r = _mm_unpacklo_epi8(bgr_plane[4 + j], zero); + ConvertRGBToY_SSE41(&r, &g, &b, &Y0); + + // Convert to 16-bit Y. + b = _mm_unpackhi_epi8(bgr_plane[0 + j], zero); + g = _mm_unpackhi_epi8(bgr_plane[2 + j], zero); + r = _mm_unpackhi_epi8(bgr_plane[4 + j], zero); + ConvertRGBToY_SSE41(&r, &g, &b, &Y1); + + // Cast to 8-bit and store. + STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + } + } + for (; i < width; ++i, bgr += 3) { // left-over + y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF); + } +} + +static void ConvertARGBToY_SSE41(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 Y0, Y1, rgb[6]; + RGB32PackedToPlanar_SSE41(&argb[i], rgb); + ConvertRGBToY_SSE41(&rgb[0], &rgb[2], &rgb[4], &Y0); + ConvertRGBToY_SSE41(&rgb[1], &rgb[3], &rgb[5], &Y1); + STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + } + for (; i < width; ++i) { // left-over + const uint32_t p = argb[i]; + y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff, + YUV_HALF); + } +} + +// Horizontal add (doubled) of two 16b values, result is 16b. +// in: A | B | C | D | ... -> out: 2*(A+B) | 2*(C+D) | ... +static void HorizontalAddPack_SSE41(const __m128i* const A, + const __m128i* const B, + __m128i* const out) { + const __m128i k2 = _mm_set1_epi16(2); + const __m128i C = _mm_madd_epi16(*A, k2); + const __m128i D = _mm_madd_epi16(*B, k2); + *out = _mm_packs_epi32(C, D); +} + +static void ConvertARGBToUV_SSE41(const uint32_t* argb, + uint8_t* u, uint8_t* v, + int src_width, int do_store) { + const int max_width = src_width & ~31; + int i; + for (i = 0; i < max_width; i += 32, u += 16, v += 16) { + __m128i rgb[6], U0, V0, U1, V1; + RGB32PackedToPlanar_SSE41(&argb[i], rgb); + HorizontalAddPack_SSE41(&rgb[0], &rgb[1], &rgb[0]); + HorizontalAddPack_SSE41(&rgb[2], &rgb[3], &rgb[2]); + HorizontalAddPack_SSE41(&rgb[4], &rgb[5], &rgb[4]); + ConvertRGBToUV_SSE41(&rgb[0], &rgb[2], &rgb[4], &U0, &V0); + + RGB32PackedToPlanar_SSE41(&argb[i + 16], rgb); + HorizontalAddPack_SSE41(&rgb[0], &rgb[1], &rgb[0]); + HorizontalAddPack_SSE41(&rgb[2], &rgb[3], &rgb[2]); + HorizontalAddPack_SSE41(&rgb[4], &rgb[5], &rgb[4]); + ConvertRGBToUV_SSE41(&rgb[0], &rgb[2], &rgb[4], &U1, &V1); + + U0 = _mm_packus_epi16(U0, U1); + V0 = _mm_packus_epi16(V0, V1); + if (!do_store) { + const __m128i prev_u = LOAD_16(u); + const __m128i prev_v = LOAD_16(v); + U0 = _mm_avg_epu8(U0, prev_u); + V0 = _mm_avg_epu8(V0, prev_v); + } + STORE_16(U0, u); + STORE_16(V0, v); + } + if (i < src_width) { // left-over + WebPConvertARGBToUV_C(argb + i, u, v, src_width - i, do_store); + } +} + +// Convert 16 packed ARGB 16b-values to r[], g[], b[] +static WEBP_INLINE void RGBA32PackedToPlanar_16b_SSE41( + const uint16_t* const rgbx, + __m128i* const r, __m128i* const g, __m128i* const b) { + const __m128i in0 = LOAD_16(rgbx + 0); // r0 | g0 | b0 |x| r1 | g1 | b1 |x + const __m128i in1 = LOAD_16(rgbx + 8); // r2 | g2 | b2 |x| r3 | g3 | b3 |x + const __m128i in2 = LOAD_16(rgbx + 16); // r4 | ... + const __m128i in3 = LOAD_16(rgbx + 24); // r6 | ... + // aarrggbb as 16-bit. + const __m128i shuff0 = + _mm_set_epi8(-1, -1, -1, -1, 13, 12, 5, 4, 11, 10, 3, 2, 9, 8, 1, 0); + const __m128i shuff1 = + _mm_set_epi8(13, 12, 5, 4, -1, -1, -1, -1, 11, 10, 3, 2, 9, 8, 1, 0); + const __m128i A0 = _mm_shuffle_epi8(in0, shuff0); + const __m128i A1 = _mm_shuffle_epi8(in1, shuff1); + const __m128i A2 = _mm_shuffle_epi8(in2, shuff0); + const __m128i A3 = _mm_shuffle_epi8(in3, shuff1); + // R0R1G0G1 + // B0B1**** + // R2R3G2G3 + // B2B3**** + // (OR is used to free port 5 for the unpack) + const __m128i B0 = _mm_unpacklo_epi32(A0, A1); + const __m128i B1 = _mm_or_si128(A0, A1); + const __m128i B2 = _mm_unpacklo_epi32(A2, A3); + const __m128i B3 = _mm_or_si128(A2, A3); + // Gather the channels. + *r = _mm_unpacklo_epi64(B0, B2); + *g = _mm_unpackhi_epi64(B0, B2); + *b = _mm_unpackhi_epi64(B1, B3); +} + +static void ConvertRGBA32ToUV_SSE41(const uint16_t* rgb, + uint8_t* u, uint8_t* v, int width) { + const int max_width = width & ~15; + const uint16_t* const last_rgb = rgb + 4 * max_width; + while (rgb < last_rgb) { + __m128i r, g, b, U0, V0, U1, V1; + RGBA32PackedToPlanar_16b_SSE41(rgb + 0, &r, &g, &b); + ConvertRGBToUV_SSE41(&r, &g, &b, &U0, &V0); + RGBA32PackedToPlanar_16b_SSE41(rgb + 32, &r, &g, &b); + ConvertRGBToUV_SSE41(&r, &g, &b, &U1, &V1); + STORE_16(_mm_packus_epi16(U0, U1), u); + STORE_16(_mm_packus_epi16(V0, V1), v); + u += 16; + v += 16; + rgb += 2 * 32; + } + if (max_width < width) { // left-over + WebPConvertRGBA32ToUV_C(rgb, u, v, width - max_width); + } +} + +//------------------------------------------------------------------------------ + +extern void WebPInitConvertARGBToYUVSSE41(void); + +WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVSSE41(void) { + WebPConvertARGBToY = ConvertARGBToY_SSE41; + WebPConvertARGBToUV = ConvertARGBToUV_SSE41; + + WebPConvertRGB24ToY = ConvertRGB24ToY_SSE41; + WebPConvertBGR24ToY = ConvertBGR24ToY_SSE41; + + WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_SSE41; +} + +//------------------------------------------------------------------------------ + +#else // !WEBP_USE_SSE41 + +WEBP_DSP_INIT_STUB(WebPInitSamplersSSE41) +WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVSSE41) + +#endif // WEBP_USE_SSE41 diff --git a/internal/libwebp-1.2.1/src/enc/Makefile.am b/internal/libwebp-1.2.1/src/enc/Makefile.am new file mode 100644 index 0000000..27d5228 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/Makefile.am @@ -0,0 +1,42 @@ +AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir) +noinst_LTLIBRARIES = libwebpencode.la + +libwebpencode_la_SOURCES = +libwebpencode_la_SOURCES += alpha_enc.c +libwebpencode_la_SOURCES += analysis_enc.c +libwebpencode_la_SOURCES += backward_references_cost_enc.c +libwebpencode_la_SOURCES += backward_references_enc.c +libwebpencode_la_SOURCES += backward_references_enc.h +libwebpencode_la_SOURCES += config_enc.c +libwebpencode_la_SOURCES += cost_enc.c +libwebpencode_la_SOURCES += cost_enc.h +libwebpencode_la_SOURCES += filter_enc.c +libwebpencode_la_SOURCES += frame_enc.c +libwebpencode_la_SOURCES += histogram_enc.c +libwebpencode_la_SOURCES += histogram_enc.h +libwebpencode_la_SOURCES += iterator_enc.c +libwebpencode_la_SOURCES += near_lossless_enc.c +libwebpencode_la_SOURCES += picture_enc.c +libwebpencode_la_SOURCES += picture_csp_enc.c +libwebpencode_la_SOURCES += picture_psnr_enc.c +libwebpencode_la_SOURCES += picture_rescale_enc.c +libwebpencode_la_SOURCES += picture_tools_enc.c +libwebpencode_la_SOURCES += predictor_enc.c +libwebpencode_la_SOURCES += quant_enc.c +libwebpencode_la_SOURCES += syntax_enc.c +libwebpencode_la_SOURCES += token_enc.c +libwebpencode_la_SOURCES += tree_enc.c +libwebpencode_la_SOURCES += vp8i_enc.h +libwebpencode_la_SOURCES += vp8l_enc.c +libwebpencode_la_SOURCES += vp8li_enc.h +libwebpencode_la_SOURCES += webp_enc.c + +libwebpencodeinclude_HEADERS = +libwebpencodeinclude_HEADERS += ../webp/encode.h +libwebpencodeinclude_HEADERS += ../webp/types.h +noinst_HEADERS = +noinst_HEADERS += ../webp/format_constants.h + +libwebpencode_la_LDFLAGS = -lm +libwebpencode_la_CPPFLAGS = $(AM_CPPFLAGS) +libwebpencodeincludedir = $(includedir)/webp diff --git a/internal/libwebp-1.2.1/src/enc/alpha_enc.c b/internal/libwebp-1.2.1/src/enc/alpha_enc.c new file mode 100644 index 0000000..0b54f3e --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/alpha_enc.c @@ -0,0 +1,443 @@ +// 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 "src/enc/vp8i_enc.h" +#include "src/dsp/dsp.h" +#include "src/utils/filters_utils.h" +#include "src/utils/quant_levels_utils.h" +#include "src/utils/utils.h" +#include "src/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 "src/enc/vp8li_enc.h" + +static int EncodeLossless(const uint8_t* const data, int width, int height, + int effort_level, // in [0..6] range + int use_quality_100, 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'. + // If the alpha quality is set to 100 and the method to 6, allow for a high + // lossless quality to trigger the cruncher. + config.quality = + (use_quality_100 && effort_level == 6) ? 100 : 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, + !reduce_levels, &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 !defined(WEBP_DISABLE_STATS) + 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; + } +#else + (void)stats; +#endif + *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 correctness 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 !defined(WEBP_DISABLE_STATS) + if (pic->stats != NULL) { // need stats? + pic->stats->coded_size += (int)(*output_size); + enc->sse_[3] = sse; + } +#endif + } + + WebPSafeFree(quant_alpha); + return ok; +} + +//------------------------------------------------------------------------------ +// Main calls + +static int CompressAlphaJob(void* arg1, void* unused) { + VP8Encoder* const enc = (VP8Encoder*)arg1; + 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) { // Soundness check. + WebPSafeFree(alpha_data); + return 0; + } + enc->alpha_data_size_ = (uint32_t)alpha_size; + enc->alpha_data_ = alpha_data; + (void)unused; + 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 = 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/internal/libwebp-1.2.1/src/enc/analysis_enc.c b/internal/libwebp-1.2.1/src/enc/analysis_enc.c new file mode 100644 index 0000000..ebb7842 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/analysis_enc.c @@ -0,0 +1,475 @@ +// 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 "src/enc/vp8i_enc.h" +#include "src/enc/cost_enc.h" +#include "src/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; +} + +//------------------------------------------------------------------------------ +// 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 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); + } + 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(void* arg1, void* arg2) { + SegmentJob* const job = (SegmentJob*)arg1; + VP8EncIterator* const it = (VP8EncIterator*)arg2; + 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 tasks to perform +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 = 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/internal/libwebp-1.2.1/src/enc/backward_references_cost_enc.c b/internal/libwebp-1.2.1/src/enc/backward_references_cost_enc.c new file mode 100644 index 0000000..516abd7 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/backward_references_cost_enc.c @@ -0,0 +1,790 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Improves a given set of backward references by analyzing its bit cost. +// The algorithm is similar to the Zopfli compression algorithm but tailored to +// images. +// +// Author: Vincent Rabaud (vrabaud@google.com) +// + +#include + +#include "src/enc/backward_references_enc.h" +#include "src/enc/histogram_enc.h" +#include "src/dsp/lossless_common.h" +#include "src/utils/color_cache_utils.h" +#include "src/utils/utils.h" + +#define VALUES_IN_BYTE 256 + +extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs); +extern int VP8LDistanceToPlaneCode(int xsize, int dist); +extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs, + const PixOrCopy v); + +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 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 xsize, int cache_bits, + const VP8LBackwardRefs* const refs) { + int ok = 0; + VP8LRefsCursor c = VP8LRefsCursorInit(refs); + VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits); + if (histo == NULL) goto Error; + + // The following code is similar to VP8LHistogramCreate but converts the + // distance to plane code. + VP8LHistogramInit(histo, cache_bits, /*init_arrays=*/ 1); + while (VP8LRefsCursorOk(&c)) { + VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos, VP8LDistanceToPlaneCode, + xsize); + VP8LRefsCursorNext(&c); + } + + 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 WEBP_INLINE void AddSingleLiteralWithCostModel( + const uint32_t* const argb, VP8LColorCache* const hashers, + const CostModel* const cost_model, int idx, int use_color_cache, + float prev_cost, float* const cost, uint16_t* const dist_array) { + double cost_val = prev_cost; + const uint32_t color = argb[idx]; + 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 500 + +// 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: +// cost_ = distance cost at index + GetLengthCost(cost_model, k) +// 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 of constant cost. +// 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 value, old intervals are split or removed. There are +// therefore no overlapping intervals. +typedef struct CostInterval CostInterval; +struct CostInterval { + float cost_; + int start_; + int end_; + int index_; + CostInterval* previous_; + CostInterval* next_; +}; + +// The GetLengthCost(cost_model, k) are cached in a CostCacheInterval. +typedef struct { + double cost_; + int start_; + int end_; // Exclusive. +} 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). + 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_; +} CostManager; + +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_); + + // 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; + + manager->costs_ = NULL; + manager->cache_intervals_ = 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] = GetLengthCost(cost_model, 0); + for (i = 1; i < cost_cache_size; ++i) { + manager->cost_cache_[i] = GetLengthCost(cost_model, i); + // Get the number of bound intervals. + if (manager->cost_cache_[i] != manager->cost_cache_[i - 1]) { + ++manager->cache_intervals_size_; + } + } + + // With the current cost model, we usually have below 20 intervals. + // The worst case scenario with a cost model would be if every length has a + // different cost, hence MAX_LENGTH but that is impossible with the current + // implementation that spirals around a pixel. + assert(manager->cache_intervals_size_ <= MAX_LENGTH); + 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_. + { + CostCacheInterval* cur = manager->cache_intervals_; + + // Consecutive values in cost_cache_ are compared and if a big enough + // difference is found, a new interval is created and bounded. + cur->start_ = 0; + cur->end_ = 1; + cur->cost_ = manager->cost_cache_[0]; + for (i = 1; i < cost_cache_size; ++i) { + const double cost_val = manager->cost_cache_[i]; + if (cost_val != cur->cost_) { + ++cur; + // Initialize an interval. + cur->start_ = i; + cur->cost_ = cost_val; + } + cur->end_ = i + 1; + } + } + + 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; + + return 1; +} + +// Given the cost and the position that define an interval, 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 position, float cost) { + const int k = i - position; + assert(k >= 0 && k < MAX_LENGTH); + + if (manager->costs_[i] > cost) { + manager->costs_[i] = cost; + manager->dist_array_[i] = k + 1; + } +} + +// Given the cost and the position that define an interval, 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 position, + float cost) { + int i; + for (i = start; i < end; ++i) UpdateCost(manager, i, position, 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) { + if (interval == NULL) return; + + ConnectIntervals(manager, interval->previous_, interval->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. +// If 'do_clean_intervals' is set to something different than 0, intervals that +// end before 'i' will be popped. +static WEBP_INLINE void UpdateCostAtIndex(CostManager* const manager, int i, + int do_clean_intervals) { + CostInterval* current = manager->head_; + + while (current != NULL && current->start_ <= i) { + CostInterval* const next = current->next_; + if (current->end_ <= i) { + if (do_clean_intervals) { + // We have an outdated interval, remove it. + PopInterval(manager, current); + } + } else { + UpdateCost(manager, i, current->index_, current->cost_); + } + 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, + float cost, int position, int start, + int end) { + CostInterval* interval_new; + + if (start >= end) return; + if (manager->count_ >= COST_CACHE_INTERVAL_SIZE_MAX) { + // Serialize the interval if we cannot store it. + UpdateCostPerInterval(manager, start, end, position, 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, position, cost); + return; + } + } + + interval_new->cost_ = cost; + interval_new->index_ = position; + interval_new->start_ = start; + interval_new->end_ = end; + PositionOrphanInterval(manager, interval_new, interval_in); + + ++manager->count_; +} + +// Given a new cost interval defined by its start at position, its length 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 position, + int len) { + size_t i; + CostInterval* interval = manager->head_; + CostInterval* interval_next; + const CostCacheInterval* const cost_cache_intervals = + manager->cache_intervals_; + // If the interval is small enough, no need to deal with the heavy + // interval logic, just serialize it right away. This constant is empirical. + const int kSkipDistance = 10; + + if (len < kSkipDistance) { + int j; + for (j = position; j < position + len; ++j) { + const int k = j - position; + float cost_tmp; + assert(k >= 0 && k < MAX_LENGTH); + cost_tmp = (float)(distance_cost + manager->cost_cache_[k]); + + if (manager->costs_[j] > cost_tmp) { + manager->costs_[j] = cost_tmp; + manager->dist_array_[j] = k + 1; + } + } + return; + } + + for (i = 0; i < manager->cache_intervals_size_ && + cost_cache_intervals[i].start_ < len; + ++i) { + // Define the intersection of the ith interval with the new one. + int start = position + cost_cache_intervals[i].start_; + const int end = position + (cost_cache_intervals[i].end_ > len + ? len + : cost_cache_intervals[i].end_); + const float cost = (float)(distance_cost + cost_cache_intervals[i].cost_); + + for (; interval != NULL && interval->start_ < end; + interval = interval_next) { + interval_next = interval->next_; + + // Make sure we have some overlap + if (start >= interval->end_) continue; + + if (cost >= interval->cost_) { + // 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, cost, position, start, + interval->start_); + start = start_new; + if (start >= end) break; + continue; + } + + if (start <= interval->start_) { + if (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 { + // [------------------------------------[ + // interval->start_ interval->end_ + // [*****************************[ + // start end + interval->start_ = end; + break; + } + } else { + if (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->cost_, interval->index_, + end, end_original); + interval = interval->next_; + break; + } else { + // [------------------------------------[ + // interval->start_ interval->end_ + // [*****************************[ + // start end + interval->end_ = start; + } + } + } + // Insert the remaining interval from start to end. + InsertInterval(manager, interval, cost, position, start, end); + } +} + +static int BackwardReferencesHashChainDistanceOnly( + int xsize, int ysize, const uint32_t* const argb, int cache_bits, + const VP8LHashChain* const hash_chain, const 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; + CostManager* cost_manager = + (CostManager*)WebPSafeMalloc(1ULL, sizeof(*cost_manager)); + int offset_prev = -1, len_prev = -1; + double offset_cost = -1; + int first_offset_is_constant = -1; // initialized with 'impossible' value + int reach = 0; + + 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, xsize, 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, &hashers, cost_model, 0, use_color_cache, + 0.f, cost_manager->costs_, dist_array); + + for (i = 1; i < pix_count; ++i) { + const float prev_cost = cost_manager->costs_[i - 1]; + int offset, len; + VP8LHashChainFindCopy(hash_chain, i, &offset, &len); + + // Try adding the pixel as a literal. + AddSingleLiteralWithCostModel(argb, &hashers, cost_model, i, + use_color_cache, prev_cost, + cost_manager->costs_, dist_array); + + // If we are dealing with a non-literal. + if (len >= 2) { + if (offset != offset_prev) { + const int code = VP8LDistanceToPlaneCode(xsize, offset); + offset_cost = GetDistanceCost(cost_model, code); + first_offset_is_constant = 1; + PushInterval(cost_manager, prev_cost + offset_cost, i, len); + } else { + assert(offset_cost >= 0); + assert(len_prev >= 0); + assert(first_offset_is_constant == 0 || first_offset_is_constant == 1); + // 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). + if (first_offset_is_constant) { + reach = i - 1 + len_prev - 1; + first_offset_is_constant = 0; + } + + if (i + len - 1 > reach) { + // We can only be go further with the same offset if the previous + // length was maxed, hence len_prev == len == MAX_LENGTH. + // TODO(vrabaud), bump i to the end right away (insert cache and + // update cost). + // TODO(vrabaud), check if one of the points in between does not have + // a lower cost. + // Already consider the pixel at "reach" to add intervals that are + // better than whatever we add. + int offset_j, len_j = 0; + int j; + assert(len == MAX_LENGTH || len == pix_count - i); + // Figure out the last consecutive pixel within [i, reach + 1] with + // the same offset. + for (j = i; j <= reach; ++j) { + VP8LHashChainFindCopy(hash_chain, j + 1, &offset_j, &len_j); + if (offset_j != offset) { + VP8LHashChainFindCopy(hash_chain, j, &offset_j, &len_j); + break; + } + } + // Update the cost at j - 1 and j. + UpdateCostAtIndex(cost_manager, j - 1, 0); + UpdateCostAtIndex(cost_manager, j, 0); + + PushInterval(cost_manager, cost_manager->costs_[j - 1] + offset_cost, + j, len_j); + reach = j + len_j - 1; + } + } + } + + UpdateCostAtIndex(cost_manager, i, 1); + offset_prev = offset; + len_prev = len; + } + + 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; + } + + VP8LClearBackwardRefs(refs); + for (ix = 0; ix < chosen_path_size; ++ix) { + const int len = chosen_path[ix]; + if (len != 1) { + int k; + const int offset = VP8LHashChainFindOffset(hash_chain, i); + VP8LBackwardRefsCursorAdd(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]); + } + VP8LBackwardRefsCursorAdd(refs, v); + ++i; + } + } + ok = !refs->error_; + Error: + if (cc_init) VP8LColorCacheClear(&hashers); + return ok; +} + +// Returns 1 on success. +extern int VP8LBackwardReferencesTraceBackwards( + int xsize, int ysize, const uint32_t* const argb, int cache_bits, + const VP8LHashChain* const hash_chain, + const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst); +int VP8LBackwardReferencesTraceBackwards(int xsize, int ysize, + const uint32_t* const argb, + int cache_bits, + const VP8LHashChain* const hash_chain, + const VP8LBackwardRefs* const refs_src, + VP8LBackwardRefs* const refs_dst) { + 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, cache_bits, hash_chain, refs_src, 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_dst)) { + goto Error; + } + ok = 1; + Error: + WebPSafeFree(dist_array); + return ok; +} diff --git a/internal/libwebp-1.2.1/src/enc/backward_references_enc.c b/internal/libwebp-1.2.1/src/enc/backward_references_enc.c new file mode 100644 index 0000000..519b36a --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/backward_references_enc.c @@ -0,0 +1,1030 @@ +// 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 + +#include "src/dsp/dsp.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" +#include "src/enc/backward_references_enc.h" +#include "src/enc/histogram_enc.h" +#include "src/utils/color_cache_utils.h" +#include "src/utils/utils.h" + +#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 ((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 + +// ----------------------------------------------------------------------------- + +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 +}; + +extern int VP8LDistanceToPlaneCode(int xsize, int dist); +int VP8LDistanceToPlaneCode(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 +}; + +extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs); +void VP8LClearBackwardRefs(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); + VP8LClearBackwardRefs(refs); + while (refs->free_blocks_ != NULL) { + PixOrCopyBlock* const next = refs->free_blocks_->next_; + WebPSafeFree(refs->free_blocks_); + refs->free_blocks_ = next; + } +} + +// Swaps the content of two VP8LBackwardRefs. +static void BackwardRefsSwap(VP8LBackwardRefs* const refs1, + VP8LBackwardRefs* const refs2) { + const int point_to_refs1 = + (refs1->tail_ != NULL && refs1->tail_ == &refs1->refs_); + const int point_to_refs2 = + (refs2->tail_ != NULL && refs2->tail_ == &refs2->refs_); + const VP8LBackwardRefs tmp = *refs1; + *refs1 = *refs2; + *refs2 = tmp; + if (point_to_refs2) refs1->tail_ = &refs1->refs_; + if (point_to_refs1) refs2->tail_ = &refs2->refs_; +} + +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; +} + +// Return 1 on success, 0 on error. +static int BackwardRefsClone(const VP8LBackwardRefs* const from, + VP8LBackwardRefs* const to) { + const PixOrCopyBlock* block_from = from->refs_; + VP8LClearBackwardRefs(to); + while (block_from != NULL) { + PixOrCopyBlock* const block_to = BackwardRefsNewBlock(to); + if (block_to == NULL) return 0; + memcpy(block_to->start_, block_from->start_, + block_from->size_ * sizeof(PixOrCopy)); + block_to->size_ = block_from->size_; + block_from = block_from->next_; + } + return 1; +} + +extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs, + const PixOrCopy v); +void VP8LBackwardRefsCursorAdd(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; +} + +// ----------------------------------------------------------------------------- +// 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; +} + +// ----------------------------------------------------------------------------- + +static const uint32_t kHashMultiplierHi = 0xc6a4a793u; +static const uint32_t kHashMultiplierLo = 0x5bd1e996u; + +static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE +uint32_t GetPixPairHash64(const uint32_t* const argb) { + uint32_t key; + key = argb[1] * kHashMultiplierHi; + key += argb[0] * kHashMultiplierLo; + 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 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); + } + VP8LBackwardRefsCursorAdd(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; + } + VP8LClearBackwardRefs(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) { + VP8LBackwardRefsCursorAdd(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) { + VP8LBackwardRefsCursorAdd(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; + } + VP8LClearBackwardRefs(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; + VP8LHashChainFindCopy(hash_chain, i, &offset, &len); + if (len >= MIN_LENGTH) { + const int len_ini = len; + int max_reach = 0; + const int j_max = + (i + len_ini >= pix_count) ? pix_count - 1 : i + len_ini; + // 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 <= j_max; ++j) { + const int len_j = VP8LHashChainFindLength(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; + if (max_reach >= pix_count) break; + } + } + } else { + len = 1; + } + // Go with literal or backward reference. + assert(len > 0); + if (len == 1) { + AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); + } else { + VP8LBackwardRefsCursorAdd(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; +} + +// Compute an LZ77 by forcing matches to happen within a given distance cost. +// We therefore limit the algorithm to the lowest 32 values in the PlaneCode +// definition. +#define WINDOW_OFFSETS_SIZE_MAX 32 +static int BackwardReferencesLz77Box(int xsize, int ysize, + const uint32_t* const argb, int cache_bits, + const VP8LHashChain* const hash_chain_best, + VP8LHashChain* hash_chain, + VP8LBackwardRefs* const refs) { + int i; + const int pix_count = xsize * ysize; + uint16_t* counts; + int window_offsets[WINDOW_OFFSETS_SIZE_MAX] = {0}; + int window_offsets_new[WINDOW_OFFSETS_SIZE_MAX] = {0}; + int window_offsets_size = 0; + int window_offsets_new_size = 0; + uint16_t* const counts_ini = + (uint16_t*)WebPSafeMalloc(xsize * ysize, sizeof(*counts_ini)); + int best_offset_prev = -1, best_length_prev = -1; + if (counts_ini == NULL) return 0; + + // counts[i] counts how many times a pixel is repeated starting at position i. + i = pix_count - 2; + counts = counts_ini + i; + counts[1] = 1; + for (; i >= 0; --i, --counts) { + if (argb[i] == argb[i + 1]) { + // Max out the counts to MAX_LENGTH. + counts[0] = counts[1] + (counts[1] != MAX_LENGTH); + } else { + counts[0] = 1; + } + } + + // Figure out the window offsets around a pixel. They are stored in a + // spiraling order around the pixel as defined by VP8LDistanceToPlaneCode. + { + int x, y; + for (y = 0; y <= 6; ++y) { + for (x = -6; x <= 6; ++x) { + const int offset = y * xsize + x; + int plane_code; + // Ignore offsets that bring us after the pixel. + if (offset <= 0) continue; + plane_code = VP8LDistanceToPlaneCode(xsize, offset) - 1; + if (plane_code >= WINDOW_OFFSETS_SIZE_MAX) continue; + window_offsets[plane_code] = offset; + } + } + // For narrow images, not all plane codes are reached, so remove those. + for (i = 0; i < WINDOW_OFFSETS_SIZE_MAX; ++i) { + if (window_offsets[i] == 0) continue; + window_offsets[window_offsets_size++] = window_offsets[i]; + } + // Given a pixel P, find the offsets that reach pixels unreachable from P-1 + // with any of the offsets in window_offsets[]. + for (i = 0; i < window_offsets_size; ++i) { + int j; + int is_reachable = 0; + for (j = 0; j < window_offsets_size && !is_reachable; ++j) { + is_reachable |= (window_offsets[i] == window_offsets[j] + 1); + } + if (!is_reachable) { + window_offsets_new[window_offsets_new_size] = window_offsets[i]; + ++window_offsets_new_size; + } + } + } + + hash_chain->offset_length_[0] = 0; + for (i = 1; i < pix_count; ++i) { + int ind; + int best_length = VP8LHashChainFindLength(hash_chain_best, i); + int best_offset; + int do_compute = 1; + + if (best_length >= MAX_LENGTH) { + // Do not recompute the best match if we already have a maximal one in the + // window. + best_offset = VP8LHashChainFindOffset(hash_chain_best, i); + for (ind = 0; ind < window_offsets_size; ++ind) { + if (best_offset == window_offsets[ind]) { + do_compute = 0; + break; + } + } + } + if (do_compute) { + // Figure out if we should use the offset/length from the previous pixel + // as an initial guess and therefore only inspect the offsets in + // window_offsets_new[]. + const int use_prev = + (best_length_prev > 1) && (best_length_prev < MAX_LENGTH); + const int num_ind = + use_prev ? window_offsets_new_size : window_offsets_size; + best_length = use_prev ? best_length_prev - 1 : 0; + best_offset = use_prev ? best_offset_prev : 0; + // Find the longest match in a window around the pixel. + for (ind = 0; ind < num_ind; ++ind) { + int curr_length = 0; + int j = i; + int j_offset = + use_prev ? i - window_offsets_new[ind] : i - window_offsets[ind]; + if (j_offset < 0 || argb[j_offset] != argb[i]) continue; + // The longest match is the sum of how many times each pixel is + // repeated. + do { + const int counts_j_offset = counts_ini[j_offset]; + const int counts_j = counts_ini[j]; + if (counts_j_offset != counts_j) { + curr_length += + (counts_j_offset < counts_j) ? counts_j_offset : counts_j; + break; + } + // The same color is repeated counts_pos times at j_offset and j. + curr_length += counts_j_offset; + j_offset += counts_j_offset; + j += counts_j_offset; + } while (curr_length <= MAX_LENGTH && j < pix_count && + argb[j_offset] == argb[j]); + if (best_length < curr_length) { + best_offset = + use_prev ? window_offsets_new[ind] : window_offsets[ind]; + if (curr_length >= MAX_LENGTH) { + best_length = MAX_LENGTH; + break; + } else { + best_length = curr_length; + } + } + } + } + + assert(i + best_length <= pix_count); + assert(best_length <= MAX_LENGTH); + if (best_length <= MIN_LENGTH) { + hash_chain->offset_length_[i] = 0; + best_offset_prev = 0; + best_length_prev = 0; + } else { + hash_chain->offset_length_[i] = + (best_offset << MAX_LENGTH_BITS) | (uint32_t)best_length; + best_offset_prev = best_offset; + best_length_prev = best_length; + } + } + hash_chain->offset_length_[0] = 0; + WebPSafeFree(counts_ini); + + return BackwardReferencesLz77(xsize, ysize, argb, cache_bits, hash_chain, + refs); +} + +// ----------------------------------------------------------------------------- + +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 = VP8LDistanceToPlaneCode(xsize, dist); + c.cur_pos->argb_or_distance = transformed_dist; + } + VP8LRefsCursorNext(&c); + } +} + +// 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* argb, int quality, + const VP8LBackwardRefs* const refs, + int* const best_cache_bits) { + int i; + const int cache_bits_max = (quality <= 25) ? 0 : *best_cache_bits; + double entropy_min = MAX_ENTROPY; + 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; + + assert(cache_bits_max >= 0 && cache_bits_max <= MAX_COLOR_CACHE_BITS); + + if (cache_bits_max == 0) { + *best_cache_bits = 0; + // Local color cache is disabled. + return 1; + } + + // Allocate data. + for (i = 0; i <= cache_bits_max; ++i) { + histos[i] = VP8LAllocateHistogram(i); + if (histos[i] == NULL) goto Error; + VP8LHistogramInit(histos[i], i, /*init_arrays=*/ 1); + if (i == 0) continue; + cc_init[i] = VP8LColorCacheInit(&hashers[i], i); + if (!cc_init[i]) goto Error; + } + + // 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. + while (VP8LRefsCursorOk(&c)) { + const PixOrCopy* const v = c.cur_pos; + if (PixOrCopyIsLiteral(v)) { + const uint32_t pix = *argb++; + const uint32_t a = (pix >> 24) & 0xff; + const uint32_t r = (pix >> 16) & 0xff; + const uint32_t g = (pix >> 8) & 0xff; + const uint32_t b = (pix >> 0) & 0xff; + // The keys of the caches can be derived from the longest one. + int key = VP8LHashPix(pix, 32 - cache_bits_max); + // Do not use the color cache for cache_bits = 0. + ++histos[0]->blue_[b]; + ++histos[0]->literal_[g]; + ++histos[0]->red_[r]; + ++histos[0]->alpha_[a]; + // Deal with cache_bits > 0. + 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_[b]; + ++histos[i]->literal_[g]; + ++histos[i]->red_[r]; + ++histos[i]->alpha_[a]; + } + } + } else { + int code, extra_bits, extra_bits_value; + // We should compute the contribution of the (distance,length) + // histograms but those are the same independently from the cache size. + // As those constant contributions are in the end added to the other + // histogram contributions, we can ignore them, except for the length + // prefix that is part of the literal_ histogram. + int len = PixOrCopyLength(v); + uint32_t argb_prev = *argb ^ 0xffffffffu; + VP8LPrefixEncode(len, &code, &extra_bits, &extra_bits_value); + for (i = 0; i <= cache_bits_max; ++i) { + ++histos[i]->literal_[NUM_LITERAL_CODES + code]; + } + // Update the color caches. + do { + if (*argb != argb_prev) { + // Efficiency: insert only if the color changes. + int key = VP8LHashPix(*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) { + const double entropy = VP8LHistogramEstimateBits(histos[i]); + if (i == 0 || entropy < entropy_min) { + entropy_min = entropy; + *best_cache_bits = i; + } + } + ok = 1; +Error: + for (i = 0; i <= cache_bits_max; ++i) { + if (cc_init[i]) VP8LColorCacheClear(&hashers[i]); + VP8LFreeHistogram(histos[i]); + } + return ok; +} + +// 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* const refs_lz77) { + *cache_bits = 0; + if (!BackwardReferencesLz77(width, height, argb, 0, hash_chain, refs_lz77)) { + return NULL; + } + BackwardReferences2DLocality(width, refs_lz77); + return refs_lz77; +} + +extern int VP8LBackwardReferencesTraceBackwards( + int xsize, int ysize, const uint32_t* const argb, int cache_bits, + const VP8LHashChain* const hash_chain, + const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst); +static int GetBackwardReferences(int width, int height, + const uint32_t* const argb, int quality, + int lz77_types_to_try, int cache_bits_max, + int do_no_cache, + const VP8LHashChain* const hash_chain, + VP8LBackwardRefs* const refs, + int* const cache_bits_best) { + VP8LHistogram* histo = NULL; + int i, lz77_type; + // Index 0 is for a color cache, index 1 for no cache (if needed). + int lz77_types_best[2] = {0, 0}; + double bit_costs_best[2] = {DBL_MAX, DBL_MAX}; + VP8LHashChain hash_chain_box; + VP8LBackwardRefs* const refs_tmp = &refs[do_no_cache ? 2 : 1]; + int status = 0; + memset(&hash_chain_box, 0, sizeof(hash_chain_box)); + + histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS); + if (histo == NULL) goto Error; + + for (lz77_type = 1; lz77_types_to_try; + lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) { + int res = 0; + double bit_cost = 0.; + if ((lz77_types_to_try & lz77_type) == 0) continue; + switch (lz77_type) { + case kLZ77RLE: + res = BackwardReferencesRle(width, height, argb, 0, refs_tmp); + break; + case kLZ77Standard: + // Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color + // cache is not that different in practice. + res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, + refs_tmp); + break; + case kLZ77Box: + if (!VP8LHashChainInit(&hash_chain_box, width * height)) goto Error; + res = BackwardReferencesLz77Box(width, height, argb, 0, hash_chain, + &hash_chain_box, refs_tmp); + break; + default: + assert(0); + } + if (!res) goto Error; + + // Start with the no color cache case. + for (i = 1; i >= 0; --i) { + int cache_bits = (i == 1) ? 0 : cache_bits_max; + + if (i == 1 && !do_no_cache) continue; + + if (i == 0) { + // Try with a color cache. + if (!CalculateBestCacheSize(argb, quality, refs_tmp, &cache_bits)) { + goto Error; + } + if (cache_bits > 0) { + if (!BackwardRefsWithLocalCache(argb, cache_bits, refs_tmp)) { + goto Error; + } + } + } + + if (i == 0 && do_no_cache && cache_bits == 0) { + // No need to re-compute bit_cost as it was computed at i == 1. + } else { + VP8LHistogramCreate(histo, refs_tmp, cache_bits); + bit_cost = VP8LHistogramEstimateBits(histo); + } + + if (bit_cost < bit_costs_best[i]) { + if (i == 1) { + // Do not swap as the full cache analysis would have the wrong + // VP8LBackwardRefs to start with. + if (!BackwardRefsClone(refs_tmp, &refs[1])) goto Error; + } else { + BackwardRefsSwap(refs_tmp, &refs[0]); + } + bit_costs_best[i] = bit_cost; + lz77_types_best[i] = lz77_type; + if (i == 0) *cache_bits_best = cache_bits; + } + } + } + assert(lz77_types_best[0] > 0); + assert(!do_no_cache || lz77_types_best[1] > 0); + + // Improve on simple LZ77 but only for high quality (TraceBackwards is + // costly). + for (i = 1; i >= 0; --i) { + if (i == 1 && !do_no_cache) continue; + if ((lz77_types_best[i] == kLZ77Standard || + lz77_types_best[i] == kLZ77Box) && + quality >= 25) { + const VP8LHashChain* const hash_chain_tmp = + (lz77_types_best[i] == kLZ77Standard) ? hash_chain : &hash_chain_box; + const int cache_bits = (i == 1) ? 0 : *cache_bits_best; + if (VP8LBackwardReferencesTraceBackwards(width, height, argb, cache_bits, + hash_chain_tmp, &refs[i], + refs_tmp)) { + double bit_cost_trace; + VP8LHistogramCreate(histo, refs_tmp, cache_bits); + bit_cost_trace = VP8LHistogramEstimateBits(histo); + if (bit_cost_trace < bit_costs_best[i]) { + BackwardRefsSwap(refs_tmp, &refs[i]); + } + } + } + + BackwardReferences2DLocality(width, &refs[i]); + + if (i == 1 && lz77_types_best[0] == lz77_types_best[1] && + *cache_bits_best == 0) { + // If the best cache size is 0 and we have the same best LZ77, just copy + // the data over and stop here. + if (!BackwardRefsClone(&refs[1], &refs[0])) goto Error; + break; + } + } + status = 1; + +Error: + VP8LHashChainClear(&hash_chain_box); + VP8LFreeHistogram(histo); + return status; +} + +WebPEncodingError VP8LGetBackwardReferences( + int width, int height, const uint32_t* const argb, int quality, + int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs, + int* const cache_bits_best) { + if (low_effort) { + VP8LBackwardRefs* refs_best; + *cache_bits_best = cache_bits_max; + refs_best = GetBackwardReferencesLowEffort( + width, height, argb, cache_bits_best, hash_chain, refs); + if (refs_best == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; + // Set it in first position. + BackwardRefsSwap(refs_best, &refs[0]); + } else { + if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try, + cache_bits_max, do_no_cache, hash_chain, refs, + cache_bits_best)) { + return VP8_ENC_ERROR_OUT_OF_MEMORY; + } + } + return VP8_ENC_OK; +} diff --git a/internal/libwebp-1.2.1/src/enc/backward_references_enc.h b/internal/libwebp-1.2.1/src/enc/backward_references_enc.h new file mode 100644 index 0000000..4c0267b --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/backward_references_enc.h @@ -0,0 +1,240 @@ +// 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_ENC_H_ +#define WEBP_ENC_BACKWARD_REFERENCES_ENC_H_ + +#include +#include +#include "src/webp/types.h" +#include "src/webp/encode.h" +#include "src/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 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) + +// If you change this, you need MAX_LENGTH_BITS + WINDOW_SIZE_BITS <= 32 as it +// is used in VP8LHashChain. +#define MAX_LENGTH_BITS 12 +#define WINDOW_SIZE_BITS 20 +// 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 + +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 + +static WEBP_INLINE int VP8LHashChainFindOffset(const VP8LHashChain* const p, + const int base_position) { + return p->offset_length_[base_position] >> MAX_LENGTH_BITS; +} + +static WEBP_INLINE int VP8LHashChainFindLength(const VP8LHashChain* const p, + const int base_position) { + return p->offset_length_[base_position] & ((1U << MAX_LENGTH_BITS) - 1); +} + +static WEBP_INLINE void VP8LHashChainFindCopy(const VP8LHashChain* const p, + int base_position, + int* const offset_ptr, + int* const length_ptr) { + *offset_ptr = VP8LHashChainFindOffset(p, base_position); + *length_ptr = VP8LHashChainFindLength(p, base_position); +} + +// ----------------------------------------------------------------------------- +// 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); + +// 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 + +enum VP8LLZ77Type { + kLZ77Standard = 1, + kLZ77RLE = 2, + kLZ77Box = 4 +}; + +// 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_best +// parameter with the matching refs_best. +// If do_no_cache == 0, refs is an array of 2 values and the best +// VP8LBackwardRefs is put in the first element. +// If do_no_cache != 0, refs is an array of 3 values and the best +// VP8LBackwardRefs is put in the first element, the best value with no-cache in +// the second element. +// In both cases, the last element is used as temporary internally. +WebPEncodingError VP8LGetBackwardReferences( + int width, int height, const uint32_t* const argb, int quality, + int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs, + int* const cache_bits_best); + +#ifdef __cplusplus +} +#endif + +#endif // WEBP_ENC_BACKWARD_REFERENCES_ENC_H_ diff --git a/internal/libwebp-1.2.1/src/enc/config_enc.c b/internal/libwebp-1.2.1/src/enc/config_enc.c new file mode 100644 index 0000000..3518b41 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/config_enc.c @@ -0,0 +1,157 @@ +// 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 "src/webp/config.h" +#endif + +#include "src/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->qmin = 0; + config->qmax = 100; + 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->qmin < 0 || config->qmax > 100 || config->qmin > config->qmax) { + 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/internal/libwebp-1.2.1/src/enc/cost_enc.c b/internal/libwebp-1.2.1/src/enc/cost_enc.c new file mode 100644 index 0000000..48fd9bc --- /dev/null +++ b/internal/libwebp-1.2.1/src/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 "src/enc/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/internal/libwebp-1.2.1/src/enc/cost_enc.h b/internal/libwebp-1.2.1/src/enc/cost_enc.h new file mode 100644 index 0000000..a4b177b --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_ENC_H_ +#define WEBP_ENC_COST_ENC_H_ + +#include +#include +#include "src/enc/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_ENC_H_ diff --git a/internal/libwebp-1.2.1/src/enc/filter_enc.c b/internal/libwebp-1.2.1/src/enc/filter_enc.c new file mode 100644 index 0000000..580800b --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/filter_enc.c @@ -0,0 +1,235 @@ +// 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 "src/enc/vp8i_enc.h" +#include "src/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 + +#if !defined(WEBP_REDUCE_SIZE) + +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; +} + +#endif // !defined(WEBP_REDUCE_SIZE) + +//------------------------------------------------------------------------------ +// Exposed APIs: Encoder should call the following 3 functions to adjust +// loop filter strength + +void VP8InitFilter(VP8EncIterator* const it) { +#if !defined(WEBP_REDUCE_SIZE) + 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(); + } +#else + (void)it; +#endif +} + +void VP8StoreFilterStats(VP8EncIterator* const it) { +#if !defined(WEBP_REDUCE_SIZE) + 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_); + } +#else // defined(WEBP_REDUCE_SIZE) + (void)it; +#endif // !defined(WEBP_REDUCE_SIZE) +} + +void VP8AdjustFilterStrength(VP8EncIterator* const it) { + VP8Encoder* const enc = it->enc_; +#if !defined(WEBP_REDUCE_SIZE) + 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; + } + return; + } +#endif // !defined(WEBP_REDUCE_SIZE) + 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/internal/libwebp-1.2.1/src/enc/frame_enc.c b/internal/libwebp-1.2.1/src/enc/frame_enc.c new file mode 100644 index 0000000..af538d8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/frame_enc.c @@ -0,0 +1,894 @@ +// 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 "src/enc/cost_enc.h" +#include "src/enc/vp8i_enc.h" +#include "src/dsp/dsp.h" +#include "src/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) + +static float Clamp(float v, float min, float max) { + return (v < min) ? min : (v > max) ? max : v; +} + +typedef struct { // struct for organizing convergence in either size or PSNR + int is_first; + float dq; + float q, last_q; + float qmin, qmax; + 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->qmin = 1.f * enc->config_->qmin; + s->qmax = 1.f * enc->config_->qmax; + s->q = s->last_q = Clamp(enc->config_->quality, s->qmin, s->qmax); + 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 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, s->qmin, s->qmax); + 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 !defined(WEBP_DISABLE_STATS) + if (enc->pic_->stats != NULL) { + for (n = 0; n < NUM_MB_SEGMENTS; ++n) { + enc->pic_->stats->segment_size[n] = p[n]; + } + } +#endif + 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 !defined(WEBP_DISABLE_STATS) + +#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 void ResetSideInfo(const VP8EncIterator* const it) { + VP8Encoder* const enc = it->enc_; + WebPPicture* const pic = enc->pic_; + if (pic->stats != NULL) { + memset(enc->block_count_, 0, sizeof(enc->block_count_)); + } + ResetSSE(enc); +} +#else // defined(WEBP_DISABLE_STATS) +static void ResetSSE(VP8Encoder* const enc) { + (void)enc; +} +static void StoreSideInfo(const VP8EncIterator* const it) { + VP8Encoder* const enc = it->enc_; + WebPPicture* const pic = enc->pic_; + if (pic->extra_info != NULL) { + if (it->x_ == 0 && it->y_ == 0) { // only do it once, at start + memset(pic->extra_info, 0, + enc->mb_w_ * enc->mb_h_ * sizeof(*pic->extra_info)); + } + } +} + +static void ResetSideInfo(const VP8EncIterator* const it) { + (void)it; +} +#endif // !defined(WEBP_DISABLE_STATS) + +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 uint8_t 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 !defined(WEBP_DISABLE_STATS) + 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); + } + } + } +#endif + 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 " + " range:[%.1f, %.1f]\n", + num_pass_left, stats.last_value, stats.value, + stats.last_q, stats.q, stats.dq, stats.qmin, stats.qmax); +#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... + if (is_last_pass) { + ResetSideInfo(&it); + } + 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/internal/libwebp-1.2.1/src/enc/histogram_enc.c b/internal/libwebp-1.2.1/src/enc/histogram_enc.c new file mode 100644 index 0000000..38a0ceb --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/histogram_enc.c @@ -0,0 +1,1252 @@ +// 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 "src/webp/config.h" +#endif + +#include + +#include "src/enc/backward_references_enc.h" +#include "src/enc/histogram_enc.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" +#include "src/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 literal_size = VP8LHistogramNumCodes(dst_cache_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; + memcpy(dst->literal_, src->literal_, literal_size * sizeof(*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, NULL, 0); + 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, + int init_arrays) { + p->palette_code_bits_ = palette_code_bits; + if (init_arrays) { + HistogramClear(p); + } else { + p->trivial_symbol_ = 0; + p->bit_cost_ = 0.; + p->literal_cost_ = 0.; + p->red_cost_ = 0.; + p->blue_cost_ = 0.; + memset(p->is_used_, 0, sizeof(p->is_used_)); + } +} + +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, /*init_arrays=*/ 0); + return histo; +} + +// Resets the pointers of the histograms to point to the bit buffer in the set. +static void HistogramSetResetPointers(VP8LHistogramSet* const set, + int cache_bits) { + int i; + const int histo_size = VP8LGetHistogramSize(cache_bits); + uint8_t* memory = (uint8_t*) (set->histograms); + memory += set->max_size * sizeof(*set->histograms); + for (i = 0; i < set->max_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)); + memory += histo_size; + } +} + +// Returns the total size of the VP8LHistogramSet. +static size_t HistogramSetTotalSize(int size, int cache_bits) { + const int histo_size = VP8LGetHistogramSize(cache_bits); + return (sizeof(VP8LHistogramSet) + size * (sizeof(VP8LHistogram*) + + histo_size + WEBP_ALIGN_CST)); +} + +VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) { + int i; + VP8LHistogramSet* set; + const size_t total_size = HistogramSetTotalSize(size, cache_bits); + 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; + set->max_size = size; + set->size = size; + HistogramSetResetPointers(set, cache_bits); + for (i = 0; i < size; ++i) { + VP8LHistogramInit(set->histograms[i], cache_bits, /*init_arrays=*/ 0); + } + return set; +} + +void VP8LHistogramSetClear(VP8LHistogramSet* const set) { + int i; + const int cache_bits = set->histograms[0]->palette_code_bits_; + const int size = set->max_size; + const size_t total_size = HistogramSetTotalSize(size, cache_bits); + uint8_t* memory = (uint8_t*)set; + + memset(memory, 0, total_size); + memory += sizeof(*set); + set->histograms = (VP8LHistogram**)memory; + set->max_size = size; + set->size = size; + HistogramSetResetPointers(set, cache_bits); + for (i = 0; i < size; ++i) { + set->histograms[i]->palette_code_bits_ = cache_bits; + } +} + +// Removes the histogram 'i' from 'set' by setting it to NULL. +static void HistogramSetRemoveHistogram(VP8LHistogramSet* const set, int i, + int* const num_used) { + assert(set->histograms[i] != NULL); + set->histograms[i] = NULL; + --*num_used; + // If we remove the last valid one, shrink until the next valid one. + if (i == set->size - 1) { + while (set->size >= 1 && set->histograms[set->size - 1] == NULL) { + --set->size; + } + } +} + +// ----------------------------------------------------------------------------- + +void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, + const PixOrCopy* const v, + int (*const distance_modifier)(int, int), + int distance_modifier_arg0) { + 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); + assert(histo->palette_code_bits_ != 0); + ++histo->literal_[literal_ix]; + } else { + int code, extra_bits; + VP8LPrefixEncodeBits(PixOrCopyLength(v), &code, &extra_bits); + ++histo->literal_[NUM_LITERAL_CODES + code]; + if (distance_modifier == NULL) { + VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits); + } else { + VP8LPrefixEncodeBits( + distance_modifier(distance_modifier_arg0, 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) { + VP8LBitEntropy entropy; + VP8LBitsEntropyUnrefined(array, n, &entropy); + + 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, + uint8_t* const is_used) { + 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; + } + // The histogram is used if there is at least one non-zero streak. + *is_used = (stats.streaks[1][0] != 0 || stats.streaks[1][1] != 0); + + 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 is_X_used, + int is_Y_used, + 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; + if (is_X_used) { + if (is_Y_used) { + VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats); + } else { + VP8LGetEntropyUnrefined(X, length, &bit_entropy, &stats); + } + } else { + if (is_Y_used) { + VP8LGetEntropyUnrefined(Y, length, &bit_entropy, &stats); + } else { + memset(&stats, 0, sizeof(stats)); + stats.counts[0] = 1; + stats.streaks[0][length > 3] = length; + VP8LBitEntropyInit(&bit_entropy); + } + } + + return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats); + } +} + +// Estimates the Entropy + Huffman + other block overhead size cost. +double VP8LHistogramEstimateBits(VP8LHistogram* const p) { + return + PopulationCost(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), + NULL, &p->is_used_[0]) + + PopulationCost(p->red_, NUM_LITERAL_CODES, NULL, &p->is_used_[1]) + + PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL, &p->is_used_[2]) + + PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL, &p->is_used_[3]) + + PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL, &p->is_used_[4]) + + 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), + a->is_used_[0], b->is_used_[0], 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, a->is_used_[1], + b->is_used_[1], trivial_at_end); + if (*cost > cost_threshold) return 0; + + *cost += + GetCombinedEntropy(a->blue_, b->blue_, NUM_LITERAL_CODES, a->is_used_[2], + b->is_used_[2], trivial_at_end); + if (*cost > cost_threshold) return 0; + + *cost += + GetCombinedEntropy(a->alpha_, b->alpha_, NUM_LITERAL_CODES, + a->is_used_[3], b->is_used_[3], trivial_at_end); + if (*cost > cost_threshold) return 0; + + *cost += + GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES, + a->is_used_[4], b->is_used_[4], 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; + assert(a != NULL && b != NULL); + 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, + &h->is_used_[3]); + const double distance_cost = + PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL, &h->is_used_[4]) + + VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES); + const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_); + h->literal_cost_ = + PopulationCost(h->literal_, num_codes, NULL, &h->is_used_[0]) + + VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES); + h->red_cost_ = + PopulationCost(h->red_, NUM_LITERAL_CODES, &red_sym, &h->is_used_[1]); + h->blue_cost_ = + PopulationCost(h->blue_, NUM_LITERAL_CODES, &blue_sym, &h->is_used_[2]); + 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); + VP8LHistogramSetClear(image_histo); + 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, NULL, 0); + x += PixOrCopyLength(v); + while (x >= xsize) { + x -= xsize; + ++y; + } + VP8LRefsCursorNext(&c); + } +} + +// Copies the histograms and computes its bit_cost. +static const uint16_t kInvalidHistogramSymbol = (uint16_t)(-1); +static void HistogramCopyAndAnalyze(VP8LHistogramSet* const orig_histo, + VP8LHistogramSet* const image_histo, + int* const num_used, + uint16_t* const histogram_symbols) { + int i, cluster_id; + int num_used_orig = *num_used; + VP8LHistogram** const orig_histograms = orig_histo->histograms; + VP8LHistogram** const histograms = image_histo->histograms; + assert(image_histo->max_size == orig_histo->max_size); + for (cluster_id = 0, i = 0; i < orig_histo->max_size; ++i) { + VP8LHistogram* const histo = orig_histograms[i]; + UpdateHistogramCost(histo); + + // Skip the histogram if it is completely empty, which can happen for tiles + // with no information (when they are skipped because of LZ77). + if (!histo->is_used_[0] && !histo->is_used_[1] && !histo->is_used_[2] + && !histo->is_used_[3] && !histo->is_used_[4]) { + // The first histogram is always used. If an histogram is empty, we set + // its id to be the same as the previous one: this will improve + // compressibility for later LZ77. + assert(i > 0); + HistogramSetRemoveHistogram(image_histo, i, num_used); + HistogramSetRemoveHistogram(orig_histo, i, &num_used_orig); + histogram_symbols[i] = kInvalidHistogramSymbol; + } else { + // Copy histograms from orig_histo[] to image_histo[]. + HistogramCopy(histo, histograms[i]); + histogram_symbols[i] = cluster_id++; + assert(cluster_id <= image_histo->max_size); + } + } +} + +// 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) { + if (histograms[i] == NULL) continue; + 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] is not set to a special value as its use will later be guarded + // by another (histograms[i] == NULL). + if (histograms[i] == NULL) continue; + bin_map[i] = GetHistoBinIndex(histograms[i], &cost_range, low_effort); + } +} + +// Merges some histograms with same bin_id together if it's advantageous. +// Sets the remaining histograms to NULL. +static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo, + int* num_used, + const uint16_t* const clusters, + uint16_t* const cluster_mappings, + VP8LHistogram* cur_combo, + const uint16_t* const bin_map, + int num_bins, + double combine_cost_factor, + int low_effort) { + VP8LHistogram** const histograms = image_histo->histograms; + int idx; + 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; + } + + // By default, a cluster matches itself. + for (idx = 0; idx < *num_used; ++idx) cluster_mappings[idx] = idx; + for (idx = 0; idx < image_histo->size; ++idx) { + int bin_id, first; + if (histograms[idx] == NULL) continue; + bin_id = bin_map[idx]; + first = bin_info[bin_id].first; + if (first == -1) { + bin_info[bin_id].first = idx; + } else if (low_effort) { + HistogramAdd(histograms[idx], histograms[first], histograms[first]); + HistogramSetRemoveHistogram(image_histo, idx, num_used); + cluster_mappings[clusters[idx]] = clusters[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]); + HistogramSetRemoveHistogram(image_histo, idx, num_used); + cluster_mappings[clusters[idx]] = clusters[first]; + } else { + ++bin_info[bin_id].num_combine_failures; + } + } + } + } + if (low_effort) { + // for low_effort case, update the final cost when everything is merged + for (idx = 0; idx < image_histo->size; ++idx) { + if (histograms[idx] == NULL) continue; + UpdateHistogramCost(histograms[idx]); + } + } +} + +// Implement a Lehmer random number generator with a multiplicative constant of +// 48271 and a modulo constant of 2^31 - 1. +static uint32_t MyRand(uint32_t* const seed) { + *seed = (uint32_t)(((uint64_t)(*seed) * 48271u) % 2147483647u); + assert(*seed > 0); + 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_size) { + histo_queue->size = 0; + histo_queue->max_size = max_size; + // 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); + histo_queue->size = 0; + histo_queue->max_size = 0; +} + +// Pop a specific pair in the queue by replacing it with the last one +// and shrinking the queue. +static void HistoQueuePopPair(HistoQueue* const histo_queue, + HistogramPair* const pair) { + assert(pair >= histo_queue->queue && + pair < (histo_queue->queue + histo_queue->size)); + assert(histo_queue->size > 0); + *pair = histo_queue->queue[histo_queue->size - 1]; + --histo_queue->size; +} + +// Check whether a pair in the queue should be updated as head or not. +static void HistoQueueUpdateHead(HistoQueue* const histo_queue, + HistogramPair* const pair) { + assert(pair->cost_diff < 0.); + assert(pair >= histo_queue->queue && + pair < (histo_queue->queue + histo_queue->size)); + assert(histo_queue->size > 0); + if (pair->cost_diff < histo_queue->queue[0].cost_diff) { + // Replace the best pair. + const HistogramPair tmp = histo_queue->queue[0]; + histo_queue->queue[0] = *pair; + *pair = tmp; + } +} + +// Update the cost diff and combo of a pair of histograms. This needs to be +// called when the the histograms have been merged with a third one. +static void HistoQueueUpdatePair(const VP8LHistogram* const h1, + const VP8LHistogram* const h2, + double threshold, + HistogramPair* const pair) { + const double sum_cost = h1->bit_cost_ + h2->bit_cost_; + pair->cost_combo = 0.; + GetCombinedHistogramEntropy(h1, h2, sum_cost + threshold, &pair->cost_combo); + pair->cost_diff = pair->cost_combo - sum_cost; +} + +// Create a pair from indices "idx1" and "idx2" provided its cost +// is inferior to "threshold", a negative entropy. +// It returns the cost of the pair, or 0. if it superior to threshold. +static double HistoQueuePush(HistoQueue* const histo_queue, + VP8LHistogram** const histograms, int idx1, + int idx2, double threshold) { + const VP8LHistogram* h1; + const VP8LHistogram* h2; + HistogramPair pair; + + // Stop here if the queue is full. + if (histo_queue->size == histo_queue->max_size) return 0.; + assert(threshold <= 0.); + if (idx1 > idx2) { + const int tmp = idx2; + idx2 = idx1; + idx1 = tmp; + } + pair.idx1 = idx1; + pair.idx2 = idx2; + h1 = histograms[idx1]; + h2 = histograms[idx2]; + + HistoQueueUpdatePair(h1, h2, threshold, &pair); + + // Do not even consider the pair if it does not improve the entropy. + if (pair.cost_diff >= threshold) return 0.; + + histo_queue->queue[histo_queue->size++] = pair; + HistoQueueUpdateHead(histo_queue, &histo_queue->queue[histo_queue->size - 1]); + + return pair.cost_diff; +} + +// ----------------------------------------------------------------------------- + +// Combines histograms by continuously choosing the one with the highest cost +// reduction. +static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, + int* const num_used) { + int ok = 0; + const int image_histo_size = image_histo->size; + int i, j; + VP8LHistogram** const histograms = image_histo->histograms; + // Priority queue of histogram pairs. + HistoQueue histo_queue; + + // image_histo_size^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: + // - image_histo_size*(image_histo_size-1)/2 (the first two for loops) + // - image_histo_size - 1 in the last for loop at the first iteration of + // the while loop, image_histo_size - 2 at the second iteration ... + // therefore image_histo_size*(image_histo_size-1)/2 overall too + if (!HistoQueueInit(&histo_queue, image_histo_size * image_histo_size)) { + goto End; + } + + for (i = 0; i < image_histo_size; ++i) { + if (image_histo->histograms[i] == NULL) continue; + for (j = i + 1; j < image_histo_size; ++j) { + // Initialize queue. + if (image_histo->histograms[j] == NULL) continue; + HistoQueuePush(&histo_queue, histograms, i, j, 0.); + } + } + + while (histo_queue.size > 0) { + 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. + HistogramSetRemoveHistogram(image_histo, idx2, num_used); + + // Remove pairs intersecting the just combined best pair. + for (i = 0; i < histo_queue.size;) { + HistogramPair* const p = histo_queue.queue + i; + if (p->idx1 == idx1 || p->idx2 == idx1 || + p->idx1 == idx2 || p->idx2 == idx2) { + HistoQueuePopPair(&histo_queue, p); + } else { + HistoQueueUpdateHead(&histo_queue, p); + ++i; + } + } + + // Push new pairs formed with combined histogram to the queue. + for (i = 0; i < image_histo->size; ++i) { + if (i == idx1 || image_histo->histograms[i] == NULL) continue; + HistoQueuePush(&histo_queue, image_histo->histograms, idx1, i, 0.); + } + } + + ok = 1; + + End: + HistoQueueClear(&histo_queue); + return ok; +} + +// Perform histogram aggregation using a stochastic approach. +// 'do_greedy' is set to 1 if a greedy approach needs to be performed +// afterwards, 0 otherwise. +static int PairComparison(const void* idx1, const void* idx2) { + // To be used with bsearch: <0 when *idx1<*idx2, >0 if >, 0 when ==. + return (*(int*) idx1 - *(int*) idx2); +} +static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo, + int* const num_used, int min_cluster_size, + int* const do_greedy) { + int j, iter; + uint32_t seed = 1; + int tries_with_no_success = 0; + const int outer_iters = *num_used; + const int num_tries_no_success = outer_iters / 2; + VP8LHistogram** const histograms = image_histo->histograms; + // Priority queue of histogram pairs. Its size of 'kHistoQueueSize' + // impacts the quality of the compression and the speed: the smaller the + // faster but the worse for the compression. + HistoQueue histo_queue; + const int kHistoQueueSize = 9; + int ok = 0; + // mapping from an index in image_histo with no NULL histogram to the full + // blown image_histo. + int* mappings; + + if (*num_used < min_cluster_size) { + *do_greedy = 1; + return 1; + } + + mappings = (int*) WebPSafeMalloc(*num_used, sizeof(*mappings)); + if (mappings == NULL) return 0; + if (!HistoQueueInit(&histo_queue, kHistoQueueSize)) goto End; + // Fill the initial mapping. + for (j = 0, iter = 0; iter < image_histo->size; ++iter) { + if (histograms[iter] == NULL) continue; + mappings[j++] = iter; + } + assert(j == *num_used); + + // Collapse similar histograms in 'image_histo'. + for (iter = 0; + iter < outer_iters && *num_used >= min_cluster_size && + ++tries_with_no_success < num_tries_no_success; + ++iter) { + int* mapping_index; + double best_cost = + (histo_queue.size == 0) ? 0. : histo_queue.queue[0].cost_diff; + int best_idx1 = -1, best_idx2 = 1; + const uint32_t rand_range = (*num_used - 1) * (*num_used); + // (*num_used) / 2 was chosen empirically. Less means faster but worse + // compression. + const int num_tries = (*num_used) / 2; + + // Pick random samples. + for (j = 0; *num_used >= 2 && j < num_tries; ++j) { + double curr_cost; + // Choose two different histograms at random and try to combine them. + const uint32_t tmp = MyRand(&seed) % rand_range; + uint32_t idx1 = tmp / (*num_used - 1); + uint32_t idx2 = tmp % (*num_used - 1); + if (idx2 >= idx1) ++idx2; + idx1 = mappings[idx1]; + idx2 = mappings[idx2]; + + // Calculate cost reduction on combination. + curr_cost = + HistoQueuePush(&histo_queue, histograms, idx1, idx2, best_cost); + if (curr_cost < 0) { // found a better pair? + best_cost = curr_cost; + // Empty the queue if we reached full capacity. + if (histo_queue.size == histo_queue.max_size) break; + } + } + if (histo_queue.size == 0) continue; + + // Get the best histograms. + best_idx1 = histo_queue.queue[0].idx1; + best_idx2 = histo_queue.queue[0].idx2; + assert(best_idx1 < best_idx2); + // Pop best_idx2 from mappings. + mapping_index = (int*) bsearch(&best_idx2, mappings, *num_used, + sizeof(best_idx2), &PairComparison); + assert(mapping_index != NULL); + memmove(mapping_index, mapping_index + 1, sizeof(*mapping_index) * + ((*num_used) - (mapping_index - mappings) - 1)); + // Merge the histograms and remove best_idx2 from the queue. + HistogramAdd(histograms[best_idx2], histograms[best_idx1], + histograms[best_idx1]); + histograms[best_idx1]->bit_cost_ = histo_queue.queue[0].cost_combo; + HistogramSetRemoveHistogram(image_histo, best_idx2, num_used); + // Parse the queue and update each pair that deals with best_idx1, + // best_idx2 or image_histo_size. + for (j = 0; j < histo_queue.size;) { + HistogramPair* const p = histo_queue.queue + j; + const int is_idx1_best = p->idx1 == best_idx1 || p->idx1 == best_idx2; + const int is_idx2_best = p->idx2 == best_idx1 || p->idx2 == best_idx2; + int do_eval = 0; + // The front pair could have been duplicated by a random pick so + // check for it all the time nevertheless. + if (is_idx1_best && is_idx2_best) { + HistoQueuePopPair(&histo_queue, p); + continue; + } + // Any pair containing one of the two best indices should only refer to + // best_idx1. Its cost should also be updated. + if (is_idx1_best) { + p->idx1 = best_idx1; + do_eval = 1; + } else if (is_idx2_best) { + p->idx2 = best_idx1; + do_eval = 1; + } + // Make sure the index order is respected. + if (p->idx1 > p->idx2) { + const int tmp = p->idx2; + p->idx2 = p->idx1; + p->idx1 = tmp; + } + if (do_eval) { + // Re-evaluate the cost of an updated pair. + HistoQueueUpdatePair(histograms[p->idx1], histograms[p->idx2], 0., p); + if (p->cost_diff >= 0.) { + HistoQueuePopPair(&histo_queue, p); + continue; + } + } + HistoQueueUpdateHead(&histo_queue, p); + ++j; + } + tries_with_no_success = 0; + } + *do_greedy = (*num_used <= min_cluster_size); + ok = 1; + +End: + HistoQueueClear(&histo_queue); + WebPSafeFree(mappings); + return ok; +} + +// ----------------------------------------------------------------------------- +// Histogram refinement + +// Find the best 'out' histogram for each of the 'in' histograms. +// At call-time, 'out' contains the histograms of the clusters. +// Note: we assume that out[]->bit_cost_ is already up-to-date. +static void HistogramRemap(const VP8LHistogramSet* const in, + 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 = out->max_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; + if (in_histo[i] == NULL) { + // Arbitrarily set to the previous value if unused to help future LZ77. + symbols[i] = symbols[i - 1]; + continue; + } + for (k = 0; k < out_size; ++k) { + double cur_bits; + 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. + VP8LHistogramSetClear(out); + out->size = out_size; + + for (i = 0; i < in_size; ++i) { + int idx; + if (in_histo[i] == NULL) continue; + 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; +} + +// Given a HistogramSet 'set', the mapping of clusters 'cluster_mapping' and the +// current assignment of the cells in 'symbols', merge the clusters and +// assign the smallest possible clusters values. +static void OptimizeHistogramSymbols(const VP8LHistogramSet* const set, + uint16_t* const cluster_mappings, + int num_clusters, + uint16_t* const cluster_mappings_tmp, + uint16_t* const symbols) { + int i, cluster_max; + int do_continue = 1; + // First, assign the lowest cluster to each pixel. + while (do_continue) { + do_continue = 0; + for (i = 0; i < num_clusters; ++i) { + int k; + k = cluster_mappings[i]; + while (k != cluster_mappings[k]) { + cluster_mappings[k] = cluster_mappings[cluster_mappings[k]]; + k = cluster_mappings[k]; + } + if (k != cluster_mappings[i]) { + do_continue = 1; + cluster_mappings[i] = k; + } + } + } + // Create a mapping from a cluster id to its minimal version. + cluster_max = 0; + memset(cluster_mappings_tmp, 0, + set->max_size * sizeof(*cluster_mappings_tmp)); + assert(cluster_mappings[0] == 0); + // Re-map the ids. + for (i = 0; i < set->max_size; ++i) { + int cluster; + if (symbols[i] == kInvalidHistogramSymbol) continue; + cluster = cluster_mappings[symbols[i]]; + assert(symbols[i] < num_clusters); + if (cluster > 0 && cluster_mappings_tmp[cluster] == 0) { + ++cluster_max; + cluster_mappings_tmp[cluster] = cluster_max; + } + symbols[i] = cluster_mappings_tmp[cluster]; + } + + // Make sure all cluster values are used. + cluster_max = 0; + for (i = 0; i < set->max_size; ++i) { + if (symbols[i] == kInvalidHistogramSymbol) continue; + if (symbols[i] <= cluster_max) continue; + ++cluster_max; + assert(symbols[i] == cluster_max); + } +} + +static void RemoveEmptyHistograms(VP8LHistogramSet* const image_histo) { + uint32_t size; + int i; + for (i = 0, size = 0; i < image_histo->size; ++i) { + if (image_histo->histograms[i] == NULL) continue; + image_histo->histograms[size++] = image_histo->histograms[i]; + } + image_histo->size = size; +} + +int VP8LGetHistoImageSymbols(int xsize, int ysize, + const VP8LBackwardRefs* const refs, + int quality, int low_effort, + int histogram_bits, int cache_bits, + VP8LHistogramSet* const image_histo, + VP8LHistogram* const tmp_histo, + uint16_t* const histogram_symbols) { + int ok = 0; + const int histo_xsize = + histogram_bits ? VP8LSubSampleSize(xsize, histogram_bits) : 1; + const int histo_ysize = + histogram_bits ? VP8LSubSampleSize(ysize, histogram_bits) : 1; + const int image_histo_raw_size = histo_xsize * histo_ysize; + VP8LHistogramSet* const orig_histo = + VP8LAllocateHistogramSet(image_histo_raw_size, cache_bits); + // 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; + int entropy_combine; + uint16_t* const map_tmp = + WebPSafeMalloc(2 * image_histo_raw_size, sizeof(map_tmp)); + uint16_t* const cluster_mappings = map_tmp + image_histo_raw_size; + int num_used = image_histo_raw_size; + if (orig_histo == NULL || map_tmp == NULL) goto Error; + + // Construct the histograms from backward references. + HistogramBuild(xsize, histogram_bits, refs, orig_histo); + // Copies the histograms and computes its bit_cost. + // histogram_symbols is optimized + HistogramCopyAndAnalyze(orig_histo, image_histo, &num_used, + histogram_symbols); + + entropy_combine = + (num_used > entropy_combine_num_bins * 2) && (quality < 100); + + if (entropy_combine) { + uint16_t* const bin_map = map_tmp; + const double combine_cost_factor = + GetCombineCostFactor(image_histo_raw_size, quality); + const uint32_t num_clusters = num_used; + + HistogramAnalyzeEntropyBin(image_histo, bin_map, low_effort); + // Collapse histograms with similar entropy. + HistogramCombineEntropyBin(image_histo, &num_used, histogram_symbols, + cluster_mappings, tmp_histo, bin_map, + entropy_combine_num_bins, combine_cost_factor, + low_effort); + OptimizeHistogramSymbols(image_histo, cluster_mappings, num_clusters, + map_tmp, histogram_symbols); + } + + // 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)); + int do_greedy; + if (!HistogramCombineStochastic(image_histo, &num_used, threshold_size, + &do_greedy)) { + goto Error; + } + if (do_greedy) { + RemoveEmptyHistograms(image_histo); + if (!HistogramCombineGreedy(image_histo, &num_used)) { + goto Error; + } + } + } + + // Find the optimal map from original histograms to the final ones. + RemoveEmptyHistograms(image_histo); + HistogramRemap(orig_histo, image_histo, histogram_symbols); + + ok = 1; + + Error: + VP8LFreeHistogramSet(orig_histo); + WebPSafeFree(map_tmp); + return ok; +} diff --git a/internal/libwebp-1.2.1/src/enc/histogram_enc.h b/internal/libwebp-1.2.1/src/enc/histogram_enc.h new file mode 100644 index 0000000..c3428b5 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/histogram_enc.h @@ -0,0 +1,128 @@ +// 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_ENC_H_ +#define WEBP_ENC_HISTOGRAM_ENC_H_ + +#include + +#include "src/enc/backward_references_enc.h" +#include "src/webp/format_constants.h" +#include "src/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_; + uint8_t is_used_[5]; // 5 for literal, red, blue, alpha, distance +} 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 cache_bits. +int VP8LGetHistogramSize(int cache_bits); + +// Set the palette_code_bits and reset the stats. +// If init_arrays is true, the arrays are also filled with 0's. +void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits, + int init_arrays); + +// 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); + +// Set the histograms in set to 0. +void VP8LHistogramSetClear(VP8LHistogramSet* const set); + +// 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, + int (*const distance_modifier)(int, int), + int distance_modifier_arg0); + +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_histo, + VP8LHistogram* const tmp_histo, + uint16_t* const histogram_symbols); + +// Returns the entropy for the symbols in the input array. +double VP8LBitsEntropy(const uint32_t* const array, int n); + +// Estimate how many bits the combined entropy of literals and distance +// approximately maps to. +double VP8LHistogramEstimateBits(VP8LHistogram* const p); + +#ifdef __cplusplus +} +#endif + +#endif // WEBP_ENC_HISTOGRAM_ENC_H_ diff --git a/internal/libwebp-1.2.1/src/enc/iterator_enc.c b/internal/libwebp-1.2.1/src/enc/iterator_enc.c new file mode 100644 index 0000000..29f91d8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/iterator_enc.c @@ -0,0 +1,459 @@ +// 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 "src/enc/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; + if (it->top_derr_ != NULL) { + memset(&it->left_derr_, 0, sizeof(it->left_derr_)); + } +} + +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_)); + if (enc->top_derr_ != NULL) { + memset(enc->top_derr_, 0, enc->mb_w_ * sizeof(*enc->top_derr_)); + } +} + +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; + it->top_derr_ = enc->top_derr_; + 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* const 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/internal/libwebp-1.2.1/src/enc/near_lossless_enc.c b/internal/libwebp-1.2.1/src/enc/near_lossless_enc.c new file mode 100644 index 0000000..5517a7e --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/near_lossless_enc.c @@ -0,0 +1,151 @@ +// 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 "src/dsp/lossless_common.h" +#include "src/utils/utils.h" +#include "src/enc/vp8li_enc.h" + +#if (WEBP_NEAR_LOSSLESS == 1) + +#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, const uint32_t* argb_src, + int stride, int limit_bits, uint32_t* copy_buffer, + uint32_t* argb_dst) { + 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(curr_row, argb_src, xsize * sizeof(argb_src[0])); + memcpy(next_row, argb_src + stride, xsize * sizeof(argb_src[0])); + + for (y = 0; y < ysize; ++y, argb_src += stride, argb_dst += xsize) { + if (y == 0 || y == ysize - 1) { + memcpy(argb_dst, argb_src, xsize * sizeof(argb_src[0])); + } else { + memcpy(next_row, argb_src + stride, xsize * sizeof(argb_src[0])); + argb_dst[0] = argb_src[0]; + argb_dst[xsize - 1] = argb_src[xsize - 1]; + for (x = 1; x < xsize - 1; ++x) { + if (IsSmooth(prev_row, curr_row, next_row, x, limit)) { + argb_dst[x] = curr_row[x]; + } else { + argb_dst[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(const WebPPicture* const picture, int quality, + uint32_t* const argb_dst) { + int i; + const int xsize = picture->width; + const int ysize = picture->height; + const int stride = picture->argb_stride; + uint32_t* const copy_buffer = + (uint32_t*)WebPSafeMalloc(xsize * 3, sizeof(*copy_buffer)); + const int limit_bits = VP8LNearLosslessBits(quality); + assert(argb_dst != 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) || + ysize < 3) { + for (i = 0; i < ysize; ++i) { + memcpy(argb_dst + i * xsize, picture->argb + i * picture->argb_stride, + xsize * sizeof(*argb_dst)); + } + WebPSafeFree(copy_buffer); + return 1; + } + + NearLossless(xsize, ysize, picture->argb, stride, limit_bits, copy_buffer, + argb_dst); + for (i = limit_bits - 1; i != 0; --i) { + NearLossless(xsize, ysize, argb_dst, xsize, i, copy_buffer, argb_dst); + } + WebPSafeFree(copy_buffer); + return 1; +} +#else // (WEBP_NEAR_LOSSLESS == 1) + +// Define a stub to suppress compiler warnings. +extern void VP8LNearLosslessStub(void); +void VP8LNearLosslessStub(void) {} + +#endif // (WEBP_NEAR_LOSSLESS == 1) diff --git a/internal/libwebp-1.2.1/src/enc/picture_csp_enc.c b/internal/libwebp-1.2.1/src/enc/picture_csp_enc.c new file mode 100644 index 0000000..35eede9 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/picture_csp_enc.c @@ -0,0 +1,1210 @@ +// 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 "src/enc/vp8i_enc.h" +#include "src/utils/random_utils.h" +#include "src/utils/utils.h" +#include "src/dsp/dsp.h" +#include "src/dsp/lossless.h" +#include "src/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 + +#ifdef WORDS_BIGENDIAN +// uint32_t 0xff000000 is 0xff,00,00,00 in memory +#define CHANNEL_OFFSET(i) (i) +#else +// uint32_t 0xff000000 is 0x00,00,00,ff in memory +#define CHANNEL_OFFSET(i) (3-(i)) +#endif + +#define ALPHA_OFFSET CHANNEL_OFFSET(0) + +//------------------------------------------------------------------------------ +// 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; + WebPInitAlphaProcessing(); + if (x_step == 1) { + for (; height-- > 0; alpha += y_step) { + if (WebPHasAlpha8b(alpha, width)) return 1; + } + } else { + for (; height-- > 0; alpha += y_step) { + if (WebPHasAlpha32b(alpha, width)) return 1; + } + } + return 0; +} + +// Checking for the presence of non-opaque alpha. +int WebPPictureHasTransparency(const WebPPicture* picture) { + if (picture == NULL) return 0; + if (picture->use_argb) { + const int alpha_offset = ALPHA_OFFSET; + return CheckNonOpaque((const uint8_t*)picture->argb + alpha_offset, + picture->width, picture->height, + 4, picture->argb_stride * sizeof(*picture->argb)); + } + return CheckNonOpaque(picture->a, picture->width, picture->height, + 1, picture->a_stride); +} + +//------------------------------------------------------------------------------ +// 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 void InitGammaTables(void); + +WEBP_DSP_INIT_FUNC(InitGammaTables) { + 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 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) + +// We use tables of different size and precision for the Rec709 / BT2020 +// transfer function. +#define kGammaF (1./0.45) +static uint32_t kLinearToGammaTabS[kGammaTabSize + 2]; +#define GAMMA_TO_LINEAR_BITS 14 +static uint32_t kGammaToLinearTabS[MAX_Y_T + 1]; // size scales with Y_FIX +static volatile int kGammaTablesSOk = 0; +static void InitGammaTablesS(void); + +WEBP_DSP_INIT_FUNC(InitGammaTablesS) { + assert(2 * GAMMA_TO_LINEAR_BITS < 32); // we use uint32_t intermediate values + if (!kGammaTablesSOk) { + int v; + const double norm = 1. / MAX_Y_T; + const double scale = 1. / kGammaTabSize; + const double a = 0.09929682680944; + const double thresh = 0.018053968510807; + const double final_scale = 1 << GAMMA_TO_LINEAR_BITS; + for (v = 0; v <= MAX_Y_T; ++v) { + const double g = norm * v; + double value; + if (g <= thresh * 4.5) { + value = g / 4.5; + } else { + const double a_rec = 1. / (1. + a); + value = pow(a_rec * (g + a), kGammaF); + } + kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5); + } + 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; + } + // we already incorporate the 1/2 rounding constant here + kLinearToGammaTabS[v] = + (uint32_t)(MAX_Y_T * value) + (1 << GAMMA_TO_LINEAR_BITS >> 1); + } + // to prevent small rounding errors to cause read-overflow: + kLinearToGammaTabS[kGammaTabSize + 1] = kLinearToGammaTabS[kGammaTabSize]; + kGammaTablesSOk = 1; + } +} + +// return value has a fixed-point precision of GAMMA_TO_LINEAR_BITS +static WEBP_INLINE uint32_t GammaToLinearS(int v) { + return kGammaToLinearTabS[v]; +} + +static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) { + // 'value' is in GAMMA_TO_LINEAR_BITS fractional precision + const uint32_t v = value * kGammaTabSize; + const uint32_t tab_pos = v >> GAMMA_TO_LINEAR_BITS; + // fractional part, in GAMMA_TO_LINEAR_BITS fixed-point precision + const uint32_t x = v - (tab_pos << GAMMA_TO_LINEAR_BITS); // fractional part + // v0 / v1 are in GAMMA_TO_LINEAR_BITS fixed-point precision (range [0..1]) + const uint32_t v0 = kLinearToGammaTabS[tab_pos + 0]; + const uint32_t v1 = kLinearToGammaTabS[tab_pos + 1]; + // Final interpolation. Note that rounding is already included. + const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0. + const uint32_t result = v0 + (v2 >> GAMMA_TO_LINEAR_BITS); + return result; +} + +#else + +static void InitGammaTablesS(void) {} +static WEBP_INLINE uint32_t GammaToLinearS(int v) { + return (v << GAMMA_TO_LINEAR_BITS) / MAX_Y_T; +} +static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) { + return (MAX_Y_T * value) >> GAMMA_TO_LINEAR_BITS; +} + +#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 uint32_t ScaleDown(int a, int b, int c, int d) { + const uint32_t A = GammaToLinearS(a); + const uint32_t B = GammaToLinearS(b); + const uint32_t C = GammaToLinearS(c); + const uint32_t D = GammaToLinearS(d); + return LinearToGammaS((A + B + C + D + 2) >> 2); +} + +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 uint32_t R = GammaToLinearS(src[0 * w + i]); + const uint32_t G = GammaToLinearS(src[1 * w + i]); + const uint32_t B = GammaToLinearS(src[2 * w + i]); + const uint32_t Y = RGBToGray(R, G, B); + dst[i] = (fixed_y_t)LinearToGammaS(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) { + assert(step == 4); +#if defined(USE_GAMMA_COMPRESSION) && defined(USE_INVERSE_ALPHA_TABLE) + assert(kAlphaFix + kGammaFix <= 31); +#endif + } + + if (use_iterative_conversion) { + InitGammaTablesS(); + 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 a = argb + CHANNEL_OFFSET(0); + const uint8_t* const r = argb + CHANNEL_OFFSET(1); + const uint8_t* const g = argb + CHANNEL_OFFSET(2); + const uint8_t* const b = argb + CHANNEL_OFFSET(3); + + 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_OFFSET > 0); + + // 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* rgb, int rgb_stride, + int step, int swap_rb, int import_alpha) { + int y; + // swap_rb -> b,g,r,a , !swap_rb -> r,g,b,a + 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 int width = picture->width; + const int height = picture->height; + + if (!picture->use_argb) { + const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL; + return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, + 0.f /* no dithering */, 0, picture); + } + if (!WebPPictureAlloc(picture)) return 0; + + VP8LDspInit(); + WebPInitAlphaProcessing(); + + if (import_alpha) { + // dst[] byte order is {a,r,g,b} for big-endian, {b,g,r,a} for little endian + uint32_t* dst = picture->argb; + const int do_copy = (ALPHA_OFFSET == 3) && swap_rb; + assert(step == 4); + if (do_copy) { + for (y = 0; y < height; ++y) { + memcpy(dst, rgb, width * 4); + rgb += rgb_stride; + dst += picture->argb_stride; + } + } else { + for (y = 0; y < height; ++y) { +#ifdef WORDS_BIGENDIAN + // BGRA or RGBA input order. + const uint8_t* a_ptr = rgb + 3; + WebPPackARGB(a_ptr, r_ptr, g_ptr, b_ptr, width, dst); + r_ptr += rgb_stride; + g_ptr += rgb_stride; + b_ptr += rgb_stride; +#else + // RGBA input order. Need to swap R and B. + VP8LConvertBGRAToRGBA((const uint32_t*)rgb, width, (uint8_t*)dst); +#endif + rgb += rgb_stride; + dst += picture->argb_stride; + } + } + } else { + uint32_t* dst = picture->argb; + assert(step >= 3); + for (y = 0; y < height; ++y) { + WebPPackRGB(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 + +#if !defined(WEBP_REDUCE_CSP) + +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 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 WebPPictureImportBGRX(WebPPicture* picture, + const uint8_t* rgba, int rgba_stride) { + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 1, 0) + : 0; +} + +#endif // WEBP_REDUCE_CSP + +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 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 WebPPictureImportRGBX(WebPPicture* picture, + const uint8_t* rgba, int rgba_stride) { + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 0, 0) + : 0; +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/src/enc/picture_enc.c b/internal/libwebp-1.2.1/src/enc/picture_enc.c new file mode 100644 index 0000000..c691622 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/picture_enc.c @@ -0,0 +1,296 @@ +// 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 "src/enc/vp8i_enc.h" +#include "src/dsp/dsp.h" +#include "src/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 + WEBP_ALIGN_CST, sizeof(*picture->argb)); + if (memory == NULL) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + } + picture->memory_argb_ = memory; + picture->argb = (uint32_t*)WEBP_ALIGN(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 = (int)(((int64_t)width + 1) >> 1); + const int uv_height = (int)(((int64_t)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(WebPEncodeRGBA, WebPPictureImportRGBA) +#if !defined(WEBP_REDUCE_CSP) +ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR) +ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA) +#endif // WEBP_REDUCE_CSP + +#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(WebPEncodeLosslessRGBA, WebPPictureImportRGBA) +#if !defined(WEBP_REDUCE_CSP) +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR) +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA) +#endif // WEBP_REDUCE_CSP + +#undef LOSSLESS_ENCODE_FUNC + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/src/enc/picture_psnr_enc.c b/internal/libwebp-1.2.1/src/enc/picture_psnr_enc.c new file mode 100644 index 0000000..1a2f0be --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/picture_psnr_enc.c @@ -0,0 +1,258 @@ +// 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 "src/webp/encode.h" + +#if !(defined(WEBP_DISABLE_STATS) || defined(WEBP_REDUCE_SIZE)) + +#include +#include + +#include "src/dsp/dsp.h" +#include "src/enc/vp8i_enc.h" +#include "src/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; +} + +#ifdef WORDS_BIGENDIAN +#define BLUE_OFFSET 3 // uint32_t 0x000000ff is 0x00,00,00,ff in memory +#else +#define BLUE_OFFSET 0 // uint32_t 0x000000ff is 0xff,00,00,00 in memory +#endif + +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; + // results are reported as BGRA + const int offset = c ^ BLUE_OFFSET; + if (!WebPPlaneDistortion((const uint8_t*)p0.argb + offset, stride0, + (const uint8_t*)p1.argb + offset, 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; +} + +#undef BLUE_OFFSET + +#else // defined(WEBP_DISABLE_STATS) +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) { + (void)src; + (void)src_stride; + (void)ref; + (void)ref_stride; + (void)width; + (void)height; + (void)x_step; + (void)type; + if (distortion == NULL || result == NULL) return 0; + *distortion = 0.f; + *result = 0.f; + return 1; +} + +int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref, + int type, float results[5]) { + int i; + (void)src; + (void)ref; + (void)type; + if (results == NULL) return 0; + for (i = 0; i < 5; ++i) results[i] = 0.f; + return 1; +} + +#endif // !defined(WEBP_DISABLE_STATS) diff --git a/internal/libwebp-1.2.1/src/enc/picture_rescale_enc.c b/internal/libwebp-1.2.1/src/enc/picture_rescale_enc.c new file mode 100644 index 0000000..a75f5d9 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/picture_rescale_enc.c @@ -0,0 +1,316 @@ +// 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 "src/webp/encode.h" + +#if !defined(WEBP_REDUCE_SIZE) + +#include +#include + +#include "src/enc/vp8i_enc.h" +#include "src/utils/rescaler_utils.h" +#include "src/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 int 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; + if (!WebPRescalerInit(&rescaler, src_width, src_height, + dst, dst_width, dst_height, dst_stride, + num_channels, work)) { + return 0; + } + while (y < src_height) { + y += WebPRescalerImport(&rescaler, src_height - y, + src + y * src_stride, src_stride); + WebPRescalerExport(&rescaler); + } + return 1; +} + +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(); + if (!RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, + tmp.a, width, height, tmp.a_stride, work, 1)) { + return 0; + } + } + + // 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); + if (!RescalePlane(pic->y, prev_width, prev_height, pic->y_stride, + tmp.y, width, height, tmp.y_stride, work, 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)) { + return 0; + } + AlphaMultiplyY(&tmp, 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); + if (!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)) { + return 0; + } + AlphaMultiplyARGB(&tmp, 1); + } + WebPPictureFree(pic); + WebPSafeFree(work); + *pic = tmp; + return 1; +} + +#else // defined(WEBP_REDUCE_SIZE) + +int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) { + (void)src; + (void)dst; + return 0; +} + +int WebPPictureIsView(const WebPPicture* picture) { + (void)picture; + return 0; +} + +int WebPPictureView(const WebPPicture* src, + int left, int top, int width, int height, + WebPPicture* dst) { + (void)src; + (void)left; + (void)top; + (void)width; + (void)height; + (void)dst; + return 0; +} + +int WebPPictureCrop(WebPPicture* pic, + int left, int top, int width, int height) { + (void)pic; + (void)left; + (void)top; + (void)width; + (void)height; + return 0; +} + +int WebPPictureRescale(WebPPicture* pic, int width, int height) { + (void)pic; + (void)width; + (void)height; + return 0; +} +#endif // !defined(WEBP_REDUCE_SIZE) diff --git a/internal/libwebp-1.2.1/src/enc/picture_tools_enc.c b/internal/libwebp-1.2.1/src/enc/picture_tools_enc.c new file mode 100644 index 0000000..38cb015 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/picture_tools_enc.c @@ -0,0 +1,273 @@ +// 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 "src/enc/vp8i_enc.h" +#include "src/dsp/yuv.h" + +//------------------------------------------------------------------------------ +// Helper: clean up fully transparent area to help compressibility. + +#define SIZE 8 +#define SIZE2 (SIZE / 2) +static int IsTransparentARGBArea(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 FlattenARGB(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; + } +} + +// Smoothen the luma components of transparent pixels. Return true if the whole +// block is transparent. +static int SmoothenBlock(const uint8_t* a_ptr, int a_stride, uint8_t* y_ptr, + int y_stride, int width, int height) { + int sum = 0, count = 0; + int x, y; + const uint8_t* alpha_ptr = a_ptr; + uint8_t* luma_ptr = y_ptr; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + if (alpha_ptr[x] != 0) { + ++count; + sum += luma_ptr[x]; + } + } + alpha_ptr += a_stride; + luma_ptr += y_stride; + } + if (count > 0 && count < width * height) { + const uint8_t avg_u8 = (uint8_t)(sum / count); + alpha_ptr = a_ptr; + luma_ptr = y_ptr; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + if (alpha_ptr[x] == 0) luma_ptr[x] = avg_u8; + } + alpha_ptr += a_stride; + luma_ptr += y_stride; + } + } + return (count == 0); +} + +void WebPReplaceTransparentPixels(WebPPicture* const pic, uint32_t color) { + if (pic != NULL && pic->use_argb) { + int y = pic->height; + uint32_t* argb = pic->argb; + color &= 0xffffffu; // force alpha=0 + WebPInitAlphaProcessing(); + while (y-- > 0) { + WebPAlphaReplace(argb, pic->width, color); + argb += pic->argb_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, except for SmoothenBlock(). + 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 (IsTransparentARGBArea(pic->argb + off, pic->argb_stride, SIZE)) { + if (need_reset) { + argb_value = pic->argb[off]; + need_reset = 0; + } + FlattenARGB(pic->argb + off, argb_value, pic->argb_stride, SIZE); + } else { + need_reset = 1; + } + } + } + } else { + const int width = pic->width; + const int height = pic->height; + const int y_stride = pic->y_stride; + const int uv_stride = pic->uv_stride; + const int a_stride = pic->a_stride; + uint8_t* y_ptr = pic->y; + uint8_t* u_ptr = pic->u; + uint8_t* v_ptr = pic->v; + const uint8_t* a_ptr = pic->a; + int values[3] = { 0 }; + if (a_ptr == NULL || y_ptr == NULL || u_ptr == NULL || v_ptr == NULL) { + return; + } + for (y = 0; y + SIZE <= height; y += SIZE) { + int need_reset = 1; + for (x = 0; x + SIZE <= width; x += SIZE) { + if (SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride, + SIZE, SIZE)) { + if (need_reset) { + values[0] = y_ptr[x]; + values[1] = u_ptr[x >> 1]; + values[2] = v_ptr[x >> 1]; + need_reset = 0; + } + Flatten(y_ptr + x, values[0], y_stride, SIZE); + Flatten(u_ptr + (x >> 1), values[1], uv_stride, SIZE2); + Flatten(v_ptr + (x >> 1), values[2], uv_stride, SIZE2); + } else { + need_reset = 1; + } + } + if (x < width) { + SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride, + width - x, SIZE); + } + a_ptr += SIZE * a_stride; + y_ptr += SIZE * y_stride; + u_ptr += SIZE2 * uv_stride; + v_ptr += SIZE2 * uv_stride; + } + if (y < height) { + const int sub_height = height - y; + for (x = 0; x + SIZE <= width; x += SIZE) { + SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride, + SIZE, sub_height); + } + if (x < width) { + SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride, + width - x, sub_height); + } + } + } +} + +#undef SIZE +#undef SIZE2 + +//------------------------------------------------------------------------------ +// Blend color and remove transparency info + +#define BLEND(V0, V1, ALPHA) \ + ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101 + 256) >> 16) +#define BLEND_10BIT(V0, V1, ALPHA) \ + ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101 + 1024) >> 18) + +static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) { + return (0xff000000u | (r << 16) | (g << 8) | b); +} + +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; + uint8_t* y_ptr = pic->y; + uint8_t* u_ptr = pic->u; + uint8_t* v_ptr = pic->v; + uint8_t* a_ptr = pic->a; + if (!has_alpha || a_ptr == NULL) return; // nothing to do + for (y = 0; y < pic->height; ++y) { + // Luma blending + for (x = 0; x < pic->width; ++x) { + const uint8_t alpha = a_ptr[x]; + if (alpha < 0xff) { + y_ptr[x] = BLEND(Y0, y_ptr[x], alpha); + } + } + // Chroma blending every even line + if ((y & 1) == 0) { + 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 uint32_t alpha = + a_ptr[2 * x + 0] + a_ptr[2 * x + 1] + + a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1]; + u_ptr[x] = BLEND_10BIT(U0, u_ptr[x], alpha); + v_ptr[x] = BLEND_10BIT(V0, v_ptr[x], alpha); + } + if (pic->width & 1) { // rightmost pixel + const uint32_t alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]); + u_ptr[x] = BLEND_10BIT(U0, u_ptr[x], alpha); + v_ptr[x] = BLEND_10BIT(V0, v_ptr[x], alpha); + } + } else { + u_ptr += pic->uv_stride; + v_ptr += pic->uv_stride; + } + memset(a_ptr, 0xff, pic->width); // reset alpha value to opaque + a_ptr += pic->a_stride; + y_ptr += pic->y_stride; + } + } 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/internal/libwebp-1.2.1/src/enc/predictor_enc.c b/internal/libwebp-1.2.1/src/enc/predictor_enc.c new file mode 100644 index 0000000..2b5c767 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/predictor_enc.c @@ -0,0 +1,772 @@ +// 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 "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" +#include "src/enc/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; } + +//------------------------------------------------------------------------------ +// 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); + } +} + +#if (WEBP_NEAR_LOSSLESS == 1) +static WEBP_INLINE int GetMax(int a, int b) { return (a < b) ? b : a; } + +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; + } +} + +static WEBP_INLINE uint8_t NearLosslessDiff(uint8_t a, uint8_t b) { + return (uint8_t)((((int)(a) - (int)(b))) & 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 = NearLosslessDiff((value >> 24) & 0xff, (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 = NearLosslessDiff(new_green, (value >> 8) & 0xff); + } + r = NearLosslessComponent(NearLosslessDiff((value >> 16) & 0xff, green_diff), + (predict >> 16) & 0xff, 0xff - new_green, + quantization); + b = NearLosslessComponent(NearLosslessDiff(value & 0xff, green_diff), + predict & 0xff, 0xff - new_green, quantization); + return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; +} +#endif // (WEBP_NEAR_LOSSLESS == 1) + +// 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(¤t_row[x - 1], upper_row + x); + } +#if (WEBP_NEAR_LOSSLESS == 1) + 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. + } +#else + (void)max_diffs; + (void)height; + (void)max_quantization; + (void)used_subtract_green; + residual = VP8LSubPixels(current_row[x], predict); +#endif + 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); + // Position and size of the strip covering the tile and adjacent columns if + // they exist. + const int context_start_x = start_x - have_left; +#if (WEBP_NEAR_LOSSLESS == 1) + const int context_width = max_x + have_left + (max_x < width - start_x); +#endif + 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 (WEBP_NEAR_LOSSLESS == 1) + 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); + } +#endif + + 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); +#if (WEBP_NEAR_LOSSLESS == 1) + uint8_t* lower_max_diffs = current_max_diffs + width; +#endif + 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 (WEBP_NEAR_LOSSLESS == 1) + 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); + } + } +#endif + 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 & 0xff); +} + +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 & 0xff; + best_tx->red_to_blue_ = red_to_blue_best & 0xff; +} +#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/internal/libwebp-1.2.1/src/enc/quant_enc.c b/internal/libwebp-1.2.1/src/enc/quant_enc.c new file mode 100644 index 0000000..6cede28 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/quant_enc.c @@ -0,0 +1,1388 @@ +// 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 "src/dsp/quant.h" +#include "src/enc/vp8i_enc.h" +#include "src/enc/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 0 // I16 mode (special case) +#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 uint16_t VP8I16ModeOffsets[4] = { I16DC16, I16TM16, I16VE16, I16HE16 }; +const uint16_t VP8UVModeOffsets[4] = { C8DC8, C8TM8, C8VE8, C8HE8 }; + +// Must be indexed using {B_DC_PRED -> B_HU_PRED} as index +const uint16_t 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 uint16_t 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 uint16_t 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; +} + +// Coefficient type. +enum { TYPE_I16_AC = 0, TYPE_I16_DC = 1, TYPE_CHROMA_A = 2, TYPE_I4_AC = 3 }; + +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 == TYPE_I16_AC) ? 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); + const 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; + int best_prev; + score_t cost, score; + + ss_cur[m].costs = costs[n + 1][ctx]; + if (level < 0 || level > thresh_level) { + ss_cur[m].score = MAX_COST; + // 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. + // The base_score is added to all scores so it is only added for the final + // value after the loop. + cost = VP8LevelCost(ss_prev[-MIN_DELTA].costs, level); + best_cur_score = + ss_prev[-MIN_DELTA].score + RDScoreTrellis(lambda, cost, 0); + best_prev = -MIN_DELTA; + for (p = -MIN_DELTA + 1; 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. + cost = VP8LevelCost(ss_prev[p].costs, level); + // Examine node assuming it's a non-terminal one. + score = ss_prev[p].score + RDScoreTrellis(lambda, cost, 0); + if (score < best_cur_score) { + best_cur_score = score; + best_prev = p; + } + } + best_cur_score += base_score; + // 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 && best_cur_score < best_score) { + 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); + 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 + // Beware! We must preserve in[0]/out[0] value for TYPE_I16_AC case. + if (coeff_type == TYPE_I16_AC) { + memset(in + 1, 0, 15 * sizeof(*in)); + memset(out + 1, 0, 15 * sizeof(*out)); + } else { + memset(in, 0, 16 * sizeof(*in)); + memset(out, 0, 16 * 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, TYPE_I16_AC, &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, TYPE_I4_AC, &dqm->y1_, + dqm->lambda_trellis_i4_); + } else { + nz = VP8EncQuantizeBlock(tmp, levels, &dqm->y1_); + } + VP8ITransform(ref, tmp, yuv_out, 0); + return nz; +} + +//------------------------------------------------------------------------------ +// DC-error diffusion + +// Diffusion weights. We under-correct a bit (15/16th of the error is actually +// diffused) to avoid 'rainbow' chessboard pattern of blocks at q~=0. +#define C1 7 // fraction of error sent to the 4x4 block below +#define C2 8 // fraction of error sent to the 4x4 block on the right +#define DSHIFT 4 +#define DSCALE 1 // storage descaling, needed to make the error fit int8_t + +// Quantize as usual, but also compute and return the quantization error. +// Error is already divided by DSHIFT. +static int QuantizeSingle(int16_t* const v, const VP8Matrix* const mtx) { + int V = *v; + const int sign = (V < 0); + if (sign) V = -V; + if (V > (int)mtx->zthresh_[0]) { + const int qV = QUANTDIV(V, mtx->iq_[0], mtx->bias_[0]) * mtx->q_[0]; + const int err = (V - qV); + *v = sign ? -qV : qV; + return (sign ? -err : err) >> DSCALE; + } + *v = 0; + return (sign ? -V : V) >> DSCALE; +} + +static void CorrectDCValues(const VP8EncIterator* const it, + const VP8Matrix* const mtx, + int16_t tmp[][16], VP8ModeScore* const rd) { + // | top[0] | top[1] + // --------+--------+--------- + // left[0] | tmp[0] tmp[1] <-> err0 err1 + // left[1] | tmp[2] tmp[3] err2 err3 + // + // Final errors {err1,err2,err3} are preserved and later restored + // as top[]/left[] on the next block. + int ch; + for (ch = 0; ch <= 1; ++ch) { + const int8_t* const top = it->top_derr_[it->x_][ch]; + const int8_t* const left = it->left_derr_[ch]; + int16_t (* const c)[16] = &tmp[ch * 4]; + int err0, err1, err2, err3; + c[0][0] += (C1 * top[0] + C2 * left[0]) >> (DSHIFT - DSCALE); + err0 = QuantizeSingle(&c[0][0], mtx); + c[1][0] += (C1 * top[1] + C2 * err0) >> (DSHIFT - DSCALE); + err1 = QuantizeSingle(&c[1][0], mtx); + c[2][0] += (C1 * err0 + C2 * left[1]) >> (DSHIFT - DSCALE); + err2 = QuantizeSingle(&c[2][0], mtx); + c[3][0] += (C1 * err1 + C2 * err2) >> (DSHIFT - DSCALE); + err3 = QuantizeSingle(&c[3][0], mtx); + // error 'err' is bounded by mtx->q_[0] which is 132 at max. Hence + // err >> DSCALE will fit in an int8_t type if DSCALE>=1. + assert(abs(err1) <= 127 && abs(err2) <= 127 && abs(err3) <= 127); + rd->derr[ch][0] = (int8_t)err1; + rd->derr[ch][1] = (int8_t)err2; + rd->derr[ch][2] = (int8_t)err3; + } +} + +static void StoreDiffusionErrors(VP8EncIterator* const it, + const VP8ModeScore* const rd) { + int ch; + for (ch = 0; ch <= 1; ++ch) { + int8_t* const top = it->top_derr_[it->x_][ch]; + int8_t* const left = it->left_derr_[ch]; + left[0] = rd->derr[ch][0]; // restore err1 + left[1] = 3 * rd->derr[ch][2] >> 2; // ... 3/4th of err3 + top[0] = rd->derr[ch][1]; // ... err2 + top[1] = rd->derr[ch][2] - left[1]; // ... 1/4th of err3. + } +} + +#undef C1 +#undef C2 +#undef DSHIFT +#undef DSCALE + +//------------------------------------------------------------------------------ + +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 (it->top_derr_ != NULL) CorrectDCValues(it, &dqm->uv_, tmp, rd); + + 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, TYPE_CHROMA_A, &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 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; + int is_flat = IsFlatSource16(it->yuv_in_ + Y_OFF_ENC); + + 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 (is_flat) { + // refine the first impression (which was in pixel space) + is_flat = IsFlat(rd_cur->y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16); + if (is_flat) { + // Block is very flat. We put emphasis on the distortion being very low! + rd_cur->D *= 2; + rd_cur->SD *= 2; + } + } + + // 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, to avoid flat area to be mispredicted + // by a complex mode. + 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)); + if (it->top_derr_ != NULL) { + memcpy(rd->derr, rd_uv.derr, sizeof(rd_uv.derr)); + } + SwapPtr(&dst, &tmp_dst); + } + } + VP8SetIntraUVMode(it, rd->mode_uv); + AddScore(rd, &rd_best); + if (dst != dst0) { // copy 16x8 block if needed + VP8Copy16x8(dst, dst0); + } + if (it->top_derr_ != NULL) { // store diffusion errors for next block + StoreDiffusionErrors(it, rd); + } +} + +//------------------------------------------------------------------------------ +// 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 = (score_t)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; + } + } + if (it->x_ == 0 || it->y_ == 0) { + // avoid starting a checkerboard resonance from the border. See bug #432. + if (IsFlatSource16(src)) { + best_mode = (it->x_ == 0) ? 0 : 2; + try_both_modes = 0; // stick to i16 + } + } + 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/internal/libwebp-1.2.1/src/enc/syntax_enc.c b/internal/libwebp-1.2.1/src/enc/syntax_enc.c new file mode 100644 index 0000000..e18cf65 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/syntax_enc.c @@ -0,0 +1,388 @@ +// 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 "src/utils/utils.h" +#include "src/webp/format_constants.h" // RIFF constants +#include "src/webp/mux_types.h" // ALPHA_FLAG +#include "src/enc/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 !defined(WEBP_DISABLE_STATS) + 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_; + } +#else + (void)pos1; + (void)pos2; + (void)pos3; +#endif + 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; + } + // RIFF size should fit in 32-bits. + 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/internal/libwebp-1.2.1/src/enc/token_enc.c b/internal/libwebp-1.2.1/src/enc/token_enc.c new file mode 100644 index 0000000..3a2192a --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/token_enc.c @@ -0,0 +1,262 @@ +// 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 "src/enc/cost_enc.h" +#include "src/enc/vp8i_enc.h" +#include "src/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 + +//------------------------------------------------------------------------------ +// 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, int page_size) { + (void)b; + (void)page_size; +} +void VP8TBufferClear(VP8TBuffer* const b) { + (void)b; +} + +#endif // !DISABLE_TOKEN_BUFFER + diff --git a/internal/libwebp-1.2.1/src/enc/tree_enc.c b/internal/libwebp-1.2.1/src/enc/tree_enc.c new file mode 100644 index 0000000..64ed283 --- /dev/null +++ b/internal/libwebp-1.2.1/src/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 "src/enc/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/internal/libwebp-1.2.1/src/enc/vp8i_enc.h b/internal/libwebp-1.2.1/src/enc/vp8i_enc.h new file mode 100644 index 0000000..67e9509 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/vp8i_enc.h @@ -0,0 +1,517 @@ +// 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_VP8I_ENC_H_ +#define WEBP_ENC_VP8I_ENC_H_ + +#include // for memcpy() +#include "src/dec/common_dec.h" +#include "src/dsp/dsp.h" +#include "src/utils/bit_writer_utils.h" +#include "src/utils/thread_utils.h" +#include "src/utils/utils.h" +#include "src/webp/encode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Various defines and enums + +// version numbers +#define ENC_MAJ_VERSION 1 +#define ENC_MIN_VERSION 2 +#define ENC_REV_VERSION 1 + +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 uint16_t VP8Scan[16]; +extern const uint16_t VP8UVModeOffsets[4]; +extern const uint16_t VP8I16ModeOffsets[4]; +extern const uint16_t 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 + +// quality below which error-diffusion is enabled +#define ERROR_DIFFUSION_QUALITY 98 + +//------------------------------------------------------------------------------ +// 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; + +typedef int8_t DError[2 /* u/v */][2 /* top or left */]; + +// 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 + int8_t derr[2][3]; // DC diffusion errors for U/V for blocks #1/2/3 +} 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 + + DError left_derr_; // left error diffusion (u/v) + DError* top_derr_; // top diffusion error - NULL if disabled + + 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* const 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 delta); +// 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); + +#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) + DError* top_derr_; // diffusion error (NULL if disabled) +}; + +//------------------------------------------------------------------------------ +// 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); + +// Replace samples that are fully transparent by 'color' to help compressibility +// (no guarantee, though). Assumes pic->use_argb is true. +void WebPReplaceTransparentPixels(WebPPicture* const pic, uint32_t color); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_ENC_VP8I_ENC_H_ diff --git a/internal/libwebp-1.2.1/src/enc/vp8l_enc.c b/internal/libwebp-1.2.1/src/enc/vp8l_enc.c new file mode 100644 index 0000000..e330e71 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/vp8l_enc.c @@ -0,0 +1,2138 @@ +// 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 "src/enc/backward_references_enc.h" +#include "src/enc/histogram_enc.h" +#include "src/enc/vp8i_enc.h" +#include "src/enc/vp8li_enc.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" +#include "src/utils/bit_writer_utils.h" +#include "src/utils/huffman_encode_utils.h" +#include "src/utils/utils.h" +#include "src/webp/format_constants.h" + +// 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 WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, + int num_colors) { + int low = 0, hi = num_colors; + 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; + } + } + assert(0); + return 0; +} + +// 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(const uint32_t* const 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. +} + +static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted, + int num_colors, uint32_t* const palette) { + uint32_t predict = 0x00000000; + int i, k; + memcpy(palette, palette_sorted, num_colors * sizeof(*palette)); + if (!PaletteHasNonMonotonousDeltas(palette_sorted, num_colors)) return; + // 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. + 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]; + } +} + +// Sort palette in increasing order and prepare an inverse mapping array. +static void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors, + uint32_t sorted[], uint32_t idx_map[]) { + uint32_t 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; + } +} + +// ----------------------------------------------------------------------------- +// Modified Zeng method from "A Survey on Palette Reordering +// Methods for Improving the Compression of Color-Indexed Images" by Armando J. +// Pinho and Antonio J. R. Neves. + +// Finds the biggest cooccurrence in the matrix. +static void CoOccurrenceFindMax(const uint32_t* const cooccurrence, + uint32_t num_colors, uint8_t* const c1, + uint8_t* const c2) { + // Find the index that is most frequently located adjacent to other + // (different) indexes. + uint32_t best_sum = 0u; + uint32_t i, j, best_cooccurrence; + *c1 = 0u; + for (i = 0; i < num_colors; ++i) { + uint32_t sum = 0; + for (j = 0; j < num_colors; ++j) sum += cooccurrence[i * num_colors + j]; + if (sum > best_sum) { + best_sum = sum; + *c1 = i; + } + } + // Find the index that is most frequently found adjacent to *c1. + *c2 = 0u; + best_cooccurrence = 0u; + for (i = 0; i < num_colors; ++i) { + if (cooccurrence[*c1 * num_colors + i] > best_cooccurrence) { + best_cooccurrence = cooccurrence[*c1 * num_colors + i]; + *c2 = i; + } + } + assert(*c1 != *c2); +} + +// Builds the cooccurrence matrix +static WebPEncodingError CoOccurrenceBuild(const WebPPicture* const pic, + const uint32_t* const palette, + uint32_t num_colors, + uint32_t* cooccurrence) { + uint32_t *lines, *line_top, *line_current, *line_tmp; + int x, y; + const uint32_t* src = pic->argb; + uint32_t prev_pix = ~src[0]; + uint32_t prev_idx = 0u; + uint32_t idx_map[MAX_PALETTE_SIZE] = {0}; + uint32_t palette_sorted[MAX_PALETTE_SIZE]; + lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines)); + if (lines == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; + line_top = &lines[0]; + line_current = &lines[pic->width]; + PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map); + for (y = 0; y < pic->height; ++y) { + for (x = 0; x < pic->width; ++x) { + const uint32_t pix = src[x]; + if (pix != prev_pix) { + prev_idx = idx_map[SearchColorNoIdx(palette_sorted, pix, num_colors)]; + prev_pix = pix; + } + line_current[x] = prev_idx; + // 4-connectivity is what works best as mentioned in "On the relation + // between Memon's and the modified Zeng's palette reordering methods". + if (x > 0 && prev_idx != line_current[x - 1]) { + const uint32_t left_idx = line_current[x - 1]; + ++cooccurrence[prev_idx * num_colors + left_idx]; + ++cooccurrence[left_idx * num_colors + prev_idx]; + } + if (y > 0 && prev_idx != line_top[x]) { + const uint32_t top_idx = line_top[x]; + ++cooccurrence[prev_idx * num_colors + top_idx]; + ++cooccurrence[top_idx * num_colors + prev_idx]; + } + } + line_tmp = line_top; + line_top = line_current; + line_current = line_tmp; + src += pic->argb_stride; + } + WebPSafeFree(lines); + return VP8_ENC_OK; +} + +struct Sum { + uint8_t index; + uint32_t sum; +}; + +// Implements the modified Zeng method from "A Survey on Palette Reordering +// Methods for Improving the Compression of Color-Indexed Images" by Armando J. +// Pinho and Antonio J. R. Neves. +static WebPEncodingError PaletteSortModifiedZeng( + const WebPPicture* const pic, const uint32_t* const palette_sorted, + uint32_t num_colors, uint32_t* const palette) { + uint32_t i, j, ind; + uint8_t remapping[MAX_PALETTE_SIZE]; + uint32_t* cooccurrence; + struct Sum sums[MAX_PALETTE_SIZE]; + uint32_t first, last; + uint32_t num_sums; + // TODO(vrabaud) check whether one color images should use palette or not. + if (num_colors <= 1) return VP8_ENC_OK; + // Build the co-occurrence matrix. + cooccurrence = + (uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence)); + if (cooccurrence == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; + if (CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence) != + VP8_ENC_OK) { + WebPSafeFree(cooccurrence); + return VP8_ENC_ERROR_OUT_OF_MEMORY; + } + + // Initialize the mapping list with the two best indices. + CoOccurrenceFindMax(cooccurrence, num_colors, &remapping[0], &remapping[1]); + + // We need to append and prepend to the list of remapping. To this end, we + // actually define the next start/end of the list as indices in a vector (with + // a wrap around when the end is reached). + first = 0; + last = 1; + num_sums = num_colors - 2; // -2 because we know the first two values + if (num_sums > 0) { + // Initialize the sums with the first two remappings and find the best one + struct Sum* best_sum = &sums[0]; + best_sum->index = 0u; + best_sum->sum = 0u; + for (i = 0, j = 0; i < num_colors; ++i) { + if (i == remapping[0] || i == remapping[1]) continue; + sums[j].index = i; + sums[j].sum = cooccurrence[i * num_colors + remapping[0]] + + cooccurrence[i * num_colors + remapping[1]]; + if (sums[j].sum > best_sum->sum) best_sum = &sums[j]; + ++j; + } + + while (num_sums > 0) { + const uint8_t best_index = best_sum->index; + // Compute delta to know if we need to prepend or append the best index. + int32_t delta = 0; + const int32_t n = num_colors - num_sums; + for (ind = first, j = 0; (ind + j) % num_colors != last + 1; ++j) { + const uint16_t l_j = remapping[(ind + j) % num_colors]; + delta += (n - 1 - 2 * (int32_t)j) * + (int32_t)cooccurrence[best_index * num_colors + l_j]; + } + if (delta > 0) { + first = (first == 0) ? num_colors - 1 : first - 1; + remapping[first] = best_index; + } else { + ++last; + remapping[last] = best_index; + } + // Remove best_sum from sums. + *best_sum = sums[num_sums - 1]; + --num_sums; + // Update all the sums and find the best one. + best_sum = &sums[0]; + for (i = 0; i < num_sums; ++i) { + sums[i].sum += cooccurrence[best_index * num_colors + sums[i].index]; + if (sums[i].sum > best_sum->sum) best_sum = &sums[i]; + } + } + } + assert((last + 1) % num_colors == first); + WebPSafeFree(cooccurrence); + + // Re-map the palette. + for (i = 0; i < num_colors; ++i) { + palette[i] = palette_sorted[remapping[(first + i) % num_colors]]; + } + return VP8_ENC_OK; +} + +// ----------------------------------------------------------------------------- +// Palette + +// These five modes are evaluated and their respective entropy is computed. +typedef enum { + kDirect = 0, + kSpatial = 1, + kSubGreen = 2, + kSpatialSubGreen = 3, + kPalette = 4, + kPaletteAndSpatial = 5, + kNumEntropyIx = 6 +} EntropyIx; + +typedef enum { + kSortedDefault = 0, + kMinimizeDelta = 1, + kModifiedZeng = 2, + kUnusedPalette = 3, +} PaletteSorting; + +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, + int palette_size, int transform_bits, + EntropyIx* const min_entropy_ix, + int* const red_and_blue_always_zero) { + // Allocate histogram set with cache_bits = 0. + uint32_t* histo; + + if (use_palette && palette_size <= 16) { + // In the case of small palettes, we pack 2, 4 or 8 pixels together. In + // practice, small palettes are better than any other transform. + *min_entropy_ix = kPalette; + *red_and_blue_always_zero = 1; + return 1; + } + histo = (uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256); + if (histo != NULL) { + int i, x, y; + const uint32_t* prev_row = NULL; + const uint32_t* curr_row = argb; + uint32_t pix_prev = argb[0]; // Skip the first pixel. + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const uint32_t pix = curr_row[x]; + const uint32_t pix_diff = VP8LSubPixels(pix, pix_prev); + pix_prev = pix; + if ((pix_diff == 0) || (prev_row != NULL && pix == prev_row[x])) { + continue; + } + 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); + } + 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]; + entropy[kPalette] = entropy_comp[kHistoPalette]; + + // When including transforms, there is an overhead in bits from + // storing them. This overhead is small but matters for small images. + // For spatial, there are 14 transformations. + entropy[kSpatial] += VP8LSubSampleSize(width, transform_bits) * + VP8LSubSampleSize(height, transform_bits) * + VP8LFastLog2(14); + // For color transforms: 24 as only 3 channels are considered in a + // ColorTransformElement. + entropy[kSpatialSubGreen] += VP8LSubSampleSize(width, transform_bits) * + VP8LSubSampleSize(height, transform_bits) * + VP8LFastLog2(24); + // For palettes, add the cost of storing the palette. + // We empirically estimate the cost of a compressed entry as 8 bits. + // The palette is differential-coded when compressed hence a much + // lower cost than sizeof(uint32_t)*8. + entropy[kPalette] += palette_size * 8; + + *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; + } + } + assert((int)*min_entropy_ix <= last_mode_to_analyze); + *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; +} + +// Set of parameters to be used in each iteration of the cruncher. +#define CRUNCH_SUBCONFIGS_MAX 2 +typedef struct { + int lz77_; + int do_no_cache_; +} CrunchSubConfig; +typedef struct { + int entropy_idx_; + PaletteSorting palette_sorting_type_; + CrunchSubConfig sub_configs_[CRUNCH_SUBCONFIGS_MAX]; + int sub_configs_size_; +} CrunchConfig; + +// +2 because we add a palette sorting configuration for kPalette and +// kPaletteAndSpatial. +#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2) + +static int EncoderAnalyze(VP8LEncoder* const enc, + CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX], + int* const crunch_configs_size, + int* const red_and_blue_always_zero) { + const WebPPicture* const pic = enc->pic_; + const int width = pic->width; + const int height = pic->height; + const WebPConfig* const config = enc->config_; + const int method = config->method; + const int low_effort = (config->method == 0); + int i; + int use_palette; + int n_lz77s; + // If set to 0, analyze the cache with the computed cache value. If 1, also + // analyze with no-cache. + int do_no_cache = 0; + assert(pic != NULL && pic->argb != NULL); + + // Check whether a palette is possible. + enc->palette_size_ = WebPGetColorPalette(pic, enc->palette_sorted_); + use_palette = (enc->palette_size_ <= MAX_PALETTE_SIZE); + if (!use_palette) { + enc->palette_size_ = 0; + } else { + qsort(enc->palette_sorted_, enc->palette_size_, + sizeof(*enc->palette_sorted_), PaletteCompareColorsForQsort); + } + + // Empirical bit sizes. + enc->histo_bits_ = GetHistoBits(method, use_palette, + pic->width, pic->height); + enc->transform_bits_ = GetTransformBits(method, enc->histo_bits_); + + if (low_effort) { + // AnalyzeEntropy is somewhat slow. + crunch_configs[0].entropy_idx_ = use_palette ? kPalette : kSpatialSubGreen; + crunch_configs[0].palette_sorting_type_ = + use_palette ? kSortedDefault : kUnusedPalette; + n_lz77s = 1; + *crunch_configs_size = 1; + } else { + EntropyIx min_entropy_ix; + // Try out multiple LZ77 on images with few colors. + n_lz77s = (enc->palette_size_ > 0 && enc->palette_size_ <= 16) ? 2 : 1; + if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, use_palette, + enc->palette_size_, enc->transform_bits_, + &min_entropy_ix, red_and_blue_always_zero)) { + return 0; + } + if (method == 6 && config->quality == 100) { + do_no_cache = 1; + // Go brute force on all transforms. + *crunch_configs_size = 0; + for (i = 0; i < kNumEntropyIx; ++i) { + // We can only apply kPalette or kPaletteAndSpatial if we can indeed use + // a palette. + if ((i != kPalette && i != kPaletteAndSpatial) || use_palette) { + assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX); + crunch_configs[(*crunch_configs_size)].entropy_idx_ = i; + if (use_palette && (i == kPalette || i == kPaletteAndSpatial)) { + crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = + kMinimizeDelta; + ++*crunch_configs_size; + // Also add modified Zeng's method. + crunch_configs[(*crunch_configs_size)].entropy_idx_ = i; + crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = + kModifiedZeng; + } else { + crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = + kUnusedPalette; + } + ++*crunch_configs_size; + } + } + } else { + // Only choose the guessed best transform. + *crunch_configs_size = 1; + crunch_configs[0].entropy_idx_ = min_entropy_ix; + crunch_configs[0].palette_sorting_type_ = + use_palette ? kMinimizeDelta : kUnusedPalette; + if (config->quality >= 75 && method == 5) { + // Test with and without color cache. + do_no_cache = 1; + // If we have a palette, also check in combination with spatial. + if (min_entropy_ix == kPalette) { + *crunch_configs_size = 2; + crunch_configs[1].entropy_idx_ = kPaletteAndSpatial; + crunch_configs[1].palette_sorting_type_ = kMinimizeDelta; + } + } + } + } + // Fill in the different LZ77s. + assert(n_lz77s <= CRUNCH_SUBCONFIGS_MAX); + for (i = 0; i < *crunch_configs_size; ++i) { + int j; + for (j = 0; j < n_lz77s; ++j) { + assert(j < CRUNCH_SUBCONFIGS_MAX); + crunch_configs[i].sub_configs_[j].lz77_ = + (j == 0) ? kLZ77Standard | kLZ77RLE : kLZ77Box; + crunch_configs[i].sub_configs_[j].do_no_cache_ = do_no_cache; + } + crunch_configs[i].sub_configs_size_ = n_lz77s; + } + return 1; +} + +static int EncoderInit(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; + // we round the block size up, so we're guaranteed to have + // at most MAX_REFS_BLOCK_PER_IMAGE blocks used: + const int refs_block_size = (pix_cnt - 1) / MAX_REFS_BLOCK_PER_IMAGE + 1; + int i; + if (!VP8LHashChainInit(&enc->hash_chain_, pix_cnt)) return 0; + + for (i = 0; i < 4; ++i) VP8LBackwardRefsInit(&enc->refs_[i], 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]; + assert(histo != NULL); + 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) { + if (trimmed_length == 2) { + VP8LPutBits(bw, 0, 3 + 2); // nbitpairs=1, trimmed_length=2 + } else { + const int nbits = BitsLog2Floor(trimmed_length - 2); + const int nbitpairs = nbits / 2 + 1; + assert(trimmed_length > 2); + assert(nbitpairs - 1 < 8); + VP8LPutBits(bw, nbitpairs - 1, 3); + 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, + const 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 uint8_t 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. + 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* const refs_array, + 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; + } + err = VP8LGetBackwardReferences( + width, height, argb, quality, /*low_effort=*/0, kLZ77Standard | kLZ77RLE, + cache_bits, /*do_no_cache=*/0, hash_chain, refs_array, &cache_bits); + if (err != VP8_ENC_OK) goto Error; + refs = &refs_array[0]; + histogram_image = VP8LAllocateHistogramSet(1, cache_bits); + if (histogram_image == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + VP8LHistogramSetClear(histogram_image); + + // 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[4], int width, + int height, int quality, int low_effort, int use_cache, + const CrunchConfig* const config, int* cache_bits, int histogram_bits, + size_t init_byte_position, int* const hdr_size, int* const data_size) { + WebPEncodingError err = VP8_ENC_ERROR_OUT_OF_MEMORY; + const uint32_t histogram_image_xysize = + VP8LSubSampleSize(width, histogram_bits) * + VP8LSubSampleSize(height, histogram_bits); + VP8LHistogramSet* histogram_image = NULL; + VP8LHistogram* tmp_histo = NULL; + int histogram_image_size = 0; + size_t bit_array_size = 0; + HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc( + 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree)); + HuffmanTreeToken* tokens = NULL; + HuffmanTreeCode* huffman_codes = NULL; + uint16_t* const histogram_symbols = + (uint16_t*)WebPSafeMalloc(histogram_image_xysize, + sizeof(*histogram_symbols)); + int sub_configs_idx; + int cache_bits_init, write_histogram_image; + VP8LBitWriter bw_init = *bw, bw_best; + int hdr_size_tmp; + VP8LHashChain hash_chain_histogram; // histogram image hash chain + size_t bw_size_best = ~(size_t)0; + assert(histogram_bits >= MIN_HUFFMAN_BITS); + assert(histogram_bits <= MAX_HUFFMAN_BITS); + assert(hdr_size != NULL); + assert(data_size != NULL); + + // Make sure we can allocate the different objects. + memset(&hash_chain_histogram, 0, sizeof(hash_chain_histogram)); + if (huff_tree == NULL || histogram_symbols == NULL || + !VP8LHashChainInit(&hash_chain_histogram, histogram_image_xysize) || + !VP8LHashChainFill(hash_chain, quality, argb, width, height, + low_effort)) { + goto Error; + } + if (use_cache) { + // If the value is different from zero, it has been set during the + // palette analysis. + cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits; + } else { + cache_bits_init = 0; + } + // If several iterations will happen, clone into bw_best. + if (!VP8LBitWriterInit(&bw_best, 0) || + ((config->sub_configs_size_ > 1 || + config->sub_configs_[0].do_no_cache_) && + !VP8LBitWriterClone(bw, &bw_best))) { + goto Error; + } + for (sub_configs_idx = 0; sub_configs_idx < config->sub_configs_size_; + ++sub_configs_idx) { + const CrunchSubConfig* const sub_config = + &config->sub_configs_[sub_configs_idx]; + int cache_bits_best, i_cache; + err = VP8LGetBackwardReferences(width, height, argb, quality, low_effort, + sub_config->lz77_, cache_bits_init, + sub_config->do_no_cache_, hash_chain, + &refs_array[0], &cache_bits_best); + if (err != VP8_ENC_OK) goto Error; + + for (i_cache = 0; i_cache < (sub_config->do_no_cache_ ? 2 : 1); ++i_cache) { + const int cache_bits_tmp = (i_cache == 0) ? cache_bits_best : 0; + // Speed-up: no need to study the no-cache case if it was already studied + // in i_cache == 0. + if (i_cache == 1 && cache_bits_best == 0) break; + + // Reset the bit writer for this iteration. + VP8LBitWriterReset(&bw_init, bw); + + // Build histogram image and symbols from backward references. + histogram_image = + VP8LAllocateHistogramSet(histogram_image_xysize, cache_bits_tmp); + tmp_histo = VP8LAllocateHistogram(cache_bits_tmp); + if (histogram_image == NULL || tmp_histo == NULL || + !VP8LGetHistoImageSymbols(width, height, &refs_array[i_cache], + quality, low_effort, histogram_bits, + cache_bits_tmp, histogram_image, tmp_histo, + histogram_symbols)) { + 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)) { + goto Error; + } + // Free combined histograms. + VP8LFreeHistogramSet(histogram_image); + histogram_image = NULL; + + // Free scratch histograms. + VP8LFreeHistogram(tmp_histo); + tmp_histo = NULL; + + // Color Cache parameters. + if (cache_bits_tmp > 0) { + VP8LPutBits(bw, 1, 1); + VP8LPutBits(bw, cache_bits_tmp, 4); + } else { + VP8LPutBits(bw, 0, 1); + } + + // Huffman image + meta huffman. + 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) 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_histogram, &refs_array[2], + 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; + // 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) 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); + } + } + // Store actual literals. + hdr_size_tmp = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position); + err = StoreImageToBitMask(bw, width, histogram_bits, &refs_array[i_cache], + histogram_symbols, huffman_codes); + if (err != VP8_ENC_OK) goto Error; + // Keep track of the smallest image so far. + if (VP8LBitWriterNumBytes(bw) < bw_size_best) { + bw_size_best = VP8LBitWriterNumBytes(bw); + *cache_bits = cache_bits_tmp; + *hdr_size = hdr_size_tmp; + *data_size = + (int)(VP8LBitWriterNumBytes(bw) - init_byte_position - *hdr_size); + VP8LBitWriterSwap(bw, &bw_best); + } + WebPSafeFree(tokens); + tokens = NULL; + if (huffman_codes != NULL) { + WebPSafeFree(huffman_codes->codes); + WebPSafeFree(huffman_codes); + huffman_codes = NULL; + } + } + } + VP8LBitWriterSwap(bw, &bw_best); + err = VP8_ENC_OK; + + Error: + WebPSafeFree(tokens); + WebPSafeFree(huff_tree); + VP8LFreeHistogramSet(histogram_image); + VP8LFreeHistogram(tmp_histo); + VP8LHashChainClear(&hash_chain_histogram); + if (huffman_codes != NULL) { + WebPSafeFree(huffman_codes->codes); + WebPSafeFree(huffman_codes); + } + WebPSafeFree(histogram_symbols); + VP8LBitWriterWipeOut(&bw_best); + 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_[0], 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_[0], 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_content_ = kEncoderNone; + } + 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; + + err = AllocateTransformBuffer(enc, width, height); + if (err != VP8_ENC_OK) return err; + if (enc->argb_content_ == kEncoderARGB) return VP8_ENC_OK; + + { + uint32_t* dst = enc->argb_; + const uint32_t* src = picture->argb; + int y; + for (y = 0; y < height; ++y) { + memcpy(dst, src, width * sizeof(*dst)); + dst += width; + src += picture->argb_stride; + } + } + enc->argb_content_ = kEncoderARGB; + assert(enc->current_width_ == width); + return VP8_ENC_OK; +} + +// ----------------------------------------------------------------------------- + +#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 ((uint32_t)((color & 0x00ffffffu) * 4222244071ull)) >> + (32 - PALETTE_INV_SIZE_BITS); +} + +static WEBP_INLINE uint32_t ApplyPaletteHash2(uint32_t color) { + // Forget about alpha. + return ((uint32_t)((color & 0x00ffffffu) * ((1ull << 31) - 1))) >> + (32 - PALETTE_INV_SIZE_BITS); +} + +// 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); + enc->argb_content_ = kEncoderPalette; + 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_[0], palette_size, 1, /*quality=*/20, + low_effort); +} + +// ----------------------------------------------------------------------------- +// 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; + enc->argb_content_ = kEncoderNone; + + VP8LEncDspInit(); + + return enc; +} + +static void VP8LEncoderDelete(VP8LEncoder* enc) { + if (enc != NULL) { + int i; + VP8LHashChainClear(&enc->hash_chain_); + for (i = 0; i < 4; ++i) VP8LBackwardRefsClear(&enc->refs_[i]); + ClearTransformBuffer(enc); + WebPSafeFree(enc); + } +} + +// ----------------------------------------------------------------------------- +// Main call + +typedef struct { + const WebPConfig* config_; + const WebPPicture* picture_; + VP8LBitWriter* bw_; + VP8LEncoder* enc_; + int use_cache_; + CrunchConfig crunch_configs_[CRUNCH_CONFIGS_MAX]; + int num_crunch_configs_; + int red_and_blue_always_zero_; + WebPEncodingError err_; + WebPAuxStats* stats_; +} StreamEncodeContext; + +static int EncodeStreamHook(void* input, void* data2) { + StreamEncodeContext* const params = (StreamEncodeContext*)input; + const WebPConfig* const config = params->config_; + const WebPPicture* const picture = params->picture_; + VP8LBitWriter* const bw = params->bw_; + VP8LEncoder* const enc = params->enc_; + const int use_cache = params->use_cache_; + const CrunchConfig* const crunch_configs = params->crunch_configs_; + const int num_crunch_configs = params->num_crunch_configs_; + const int red_and_blue_always_zero = params->red_and_blue_always_zero_; +#if !defined(WEBP_DISABLE_STATS) + WebPAuxStats* const stats = params->stats_; +#endif + WebPEncodingError err = VP8_ENC_OK; + const int quality = (int)config->quality; + const int low_effort = (config->method == 0); +#if (WEBP_NEAR_LOSSLESS == 1) + const int width = picture->width; +#endif + const int height = picture->height; + const size_t byte_position = VP8LBitWriterNumBytes(bw); +#if (WEBP_NEAR_LOSSLESS == 1) + int use_near_lossless = 0; +#endif + int hdr_size = 0; + int data_size = 0; + int use_delta_palette = 0; + int idx; + size_t best_size = ~(size_t)0; + VP8LBitWriter bw_init = *bw, bw_best; + (void)data2; + + if (!VP8LBitWriterInit(&bw_best, 0) || + (num_crunch_configs > 1 && !VP8LBitWriterClone(bw, &bw_best))) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + for (idx = 0; idx < num_crunch_configs; ++idx) { + const int entropy_idx = crunch_configs[idx].entropy_idx_; + enc->use_palette_ = + (entropy_idx == kPalette) || (entropy_idx == kPaletteAndSpatial); + enc->use_subtract_green_ = + (entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen); + enc->use_predict_ = (entropy_idx == kSpatial) || + (entropy_idx == kSpatialSubGreen) || + (entropy_idx == kPaletteAndSpatial); + // When using a palette, R/B==0, hence no need to test for cross-color. + if (low_effort || enc->use_palette_) { + enc->use_cross_color_ = 0; + } else { + enc->use_cross_color_ = red_and_blue_always_zero ? 0 : enc->use_predict_; + } + // Reset any parameter in the encoder that is set in the previous iteration. + enc->cache_bits_ = 0; + VP8LBackwardRefsClear(&enc->refs_[0]); + VP8LBackwardRefsClear(&enc->refs_[1]); + +#if (WEBP_NEAR_LOSSLESS == 1) + // Apply near-lossless preprocessing. + use_near_lossless = (config->near_lossless < 100) && !enc->use_palette_ && + !enc->use_predict_; + if (use_near_lossless) { + err = AllocateTransformBuffer(enc, width, height); + if (err != VP8_ENC_OK) goto Error; + if ((enc->argb_content_ != kEncoderNearLossless) && + !VP8ApplyNearLossless(picture, config->near_lossless, enc->argb_)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + enc->argb_content_ = kEncoderNearLossless; + } else { + enc->argb_content_ = kEncoderNone; + } +#else + enc->argb_content_ = kEncoderNone; +#endif + + // Encode palette + if (enc->use_palette_) { + if (crunch_configs[idx].palette_sorting_type_ == kSortedDefault) { + // Nothing to do, we have already sorted the palette. + memcpy(enc->palette_, enc->palette_sorted_, + enc->palette_size_ * sizeof(*enc->palette_)); + } else if (crunch_configs[idx].palette_sorting_type_ == kMinimizeDelta) { + PaletteSortMinimizeDeltas(enc->palette_sorted_, enc->palette_size_, + enc->palette_); + } else { + assert(crunch_configs[idx].palette_sorting_type_ == kModifiedZeng); + err = PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_, + enc->palette_size_, enc->palette_); + if (err != VP8_ENC_OK) goto Error; + } + 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_content_ != kEncoderNearLossless && + enc->argb_content_ != kEncoderPalette) { + 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, &crunch_configs[idx], + &enc->cache_bits_, enc->histo_bits_, + byte_position, &hdr_size, &data_size); + if (err != VP8_ENC_OK) goto Error; + + // If we are better than what we already have. + if (VP8LBitWriterNumBytes(bw) < best_size) { + best_size = VP8LBitWriterNumBytes(bw); + // Store the BitWriter. + VP8LBitWriterSwap(bw, &bw_best); +#if !defined(WEBP_DISABLE_STATS) + // Update the stats. + if (stats != NULL) { + 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)(best_size - byte_position); + stats->lossless_hdr_size = hdr_size; + stats->lossless_data_size = data_size; + } +#endif + } + // Reset the bit writer for the following iteration if any. + if (num_crunch_configs > 1) VP8LBitWriterReset(&bw_init, bw); + } + VP8LBitWriterSwap(&bw_best, bw); + +Error: + VP8LBitWriterWipeOut(&bw_best); + params->err_ = err; + // The hook should return false in case of error. + return (err == VP8_ENC_OK); +} + +WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, + const WebPPicture* const picture, + VP8LBitWriter* const bw_main, + int use_cache) { + WebPEncodingError err = VP8_ENC_OK; + VP8LEncoder* const enc_main = VP8LEncoderNew(config, picture); + VP8LEncoder* enc_side = NULL; + CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX]; + int num_crunch_configs_main, num_crunch_configs_side = 0; + int idx; + int red_and_blue_always_zero = 0; + WebPWorker worker_main, worker_side; + StreamEncodeContext params_main, params_side; + // The main thread uses picture->stats, the side thread uses stats_side. + WebPAuxStats stats_side; + VP8LBitWriter bw_side; + const WebPWorkerInterface* const worker_interface = WebPGetWorkerInterface(); + int ok_main; + + // Analyze image (entropy, num_palettes etc) + if (enc_main == NULL || + !EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main, + &red_and_blue_always_zero) || + !EncoderInit(enc_main) || !VP8LBitWriterInit(&bw_side, 0)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + // Split the configs between the main and side threads (if any). + if (config->thread_level > 0) { + num_crunch_configs_side = num_crunch_configs_main / 2; + for (idx = 0; idx < num_crunch_configs_side; ++idx) { + params_side.crunch_configs_[idx] = + crunch_configs[num_crunch_configs_main - num_crunch_configs_side + + idx]; + } + params_side.num_crunch_configs_ = num_crunch_configs_side; + } + num_crunch_configs_main -= num_crunch_configs_side; + for (idx = 0; idx < num_crunch_configs_main; ++idx) { + params_main.crunch_configs_[idx] = crunch_configs[idx]; + } + params_main.num_crunch_configs_ = num_crunch_configs_main; + + // Fill in the parameters for the thread workers. + { + const int params_size = (num_crunch_configs_side > 0) ? 2 : 1; + for (idx = 0; idx < params_size; ++idx) { + // Create the parameters for each worker. + WebPWorker* const worker = (idx == 0) ? &worker_main : &worker_side; + StreamEncodeContext* const param = + (idx == 0) ? ¶ms_main : ¶ms_side; + param->config_ = config; + param->picture_ = picture; + param->use_cache_ = use_cache; + param->red_and_blue_always_zero_ = red_and_blue_always_zero; + if (idx == 0) { + param->stats_ = picture->stats; + param->bw_ = bw_main; + param->enc_ = enc_main; + } else { + param->stats_ = (picture->stats == NULL) ? NULL : &stats_side; + // Create a side bit writer. + if (!VP8LBitWriterClone(bw_main, &bw_side)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + param->bw_ = &bw_side; + // Create a side encoder. + enc_side = VP8LEncoderNew(config, picture); + if (enc_side == NULL || !EncoderInit(enc_side)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + // Copy the values that were computed for the main encoder. + enc_side->histo_bits_ = enc_main->histo_bits_; + enc_side->transform_bits_ = enc_main->transform_bits_; + enc_side->palette_size_ = enc_main->palette_size_; + memcpy(enc_side->palette_, enc_main->palette_, + sizeof(enc_main->palette_)); + memcpy(enc_side->palette_sorted_, enc_main->palette_sorted_, + sizeof(enc_main->palette_sorted_)); + param->enc_ = enc_side; + } + // Create the workers. + worker_interface->Init(worker); + worker->data1 = param; + worker->data2 = NULL; + worker->hook = EncodeStreamHook; + } + } + + // Start the second thread if needed. + if (num_crunch_configs_side != 0) { + if (!worker_interface->Reset(&worker_side)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } +#if !defined(WEBP_DISABLE_STATS) + // This line is here and not in the param initialization above to remove a + // Clang static analyzer warning. + if (picture->stats != NULL) { + memcpy(&stats_side, picture->stats, sizeof(stats_side)); + } +#endif + // This line is only useful to remove a Clang static analyzer warning. + params_side.err_ = VP8_ENC_OK; + worker_interface->Launch(&worker_side); + } + // Execute the main thread. + worker_interface->Execute(&worker_main); + ok_main = worker_interface->Sync(&worker_main); + worker_interface->End(&worker_main); + if (num_crunch_configs_side != 0) { + // Wait for the second thread. + const int ok_side = worker_interface->Sync(&worker_side); + worker_interface->End(&worker_side); + if (!ok_main || !ok_side) { + err = ok_main ? params_side.err_ : params_main.err_; + goto Error; + } + if (VP8LBitWriterNumBytes(&bw_side) < VP8LBitWriterNumBytes(bw_main)) { + VP8LBitWriterSwap(bw_main, &bw_side); +#if !defined(WEBP_DISABLE_STATS) + if (picture->stats != NULL) { + memcpy(picture->stats, &stats_side, sizeof(*picture->stats)); + } +#endif + } + } else { + if (!ok_main) { + err = params_main.err_; + goto Error; + } + } + +Error: + VP8LBitWriterWipeOut(&bw_side); + VP8LEncoderDelete(enc_main); + VP8LEncoderDelete(enc_side); + return err; +} + +#undef CRUNCH_CONFIGS_MAX +#undef CRUNCH_SUBCONFIGS_MAX + +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; + + 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; + +#if !defined(WEBP_DISABLE_STATS) + // Save size. + if (picture->stats != NULL) { + picture->stats->coded_size += (int)coded_size; + picture->stats->lossless_size = (int)coded_size; + } +#endif + + 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/internal/libwebp-1.2.1/src/enc/vp8li_enc.h b/internal/libwebp-1.2.1/src/enc/vp8li_enc.h new file mode 100644 index 0000000..00de489 --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/vp8li_enc.h @@ -0,0 +1,120 @@ +// 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_ENC_H_ +#define WEBP_ENC_VP8LI_ENC_H_ + +#ifdef HAVE_CONFIG_H +#include "src/webp/config.h" +#endif +// Either WEBP_NEAR_LOSSLESS is defined as 0 in config.h when compiling to +// disable near-lossless, or it is enabled by default. +#ifndef WEBP_NEAR_LOSSLESS +#define WEBP_NEAR_LOSSLESS 1 +#endif + +#include "src/enc/backward_references_enc.h" +#include "src/enc/histogram_enc.h" +#include "src/utils/bit_writer_utils.h" +#include "src/webp/encode.h" +#include "src/webp/format_constants.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// maximum value of transform_bits_ in VP8LEncoder. +#define MAX_TRANSFORM_BITS 6 + +typedef enum { + kEncoderNone = 0, + kEncoderARGB, + kEncoderNearLossless, + kEncoderPalette +} VP8LEncoderARGBContent; + +typedef struct { + const WebPConfig* config_; // user configuration and parameters + const WebPPicture* pic_; // input picture. + + uint32_t* argb_; // Transformed argb image data. + VP8LEncoderARGBContent argb_content_; // Content type of the argb buffer. + 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]; + // Sorted version of palette_ for cache purposes. + uint32_t palette_sorted_[MAX_PALETTE_SIZE]; + + // Some 'scratch' (potentially large) objects. + struct VP8LBackwardRefs refs_[4]; // Backward Refs array for temporaries. + 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); + +#if (WEBP_NEAR_LOSSLESS == 1) +// in near_lossless.c +// Near lossless preprocessing in RGB color-space. +int VP8ApplyNearLossless(const WebPPicture* const picture, int quality, + uint32_t* const argb_dst); +#endif + +//------------------------------------------------------------------------------ +// 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_ENC_H_ diff --git a/internal/libwebp-1.2.1/src/enc/webp_enc.c b/internal/libwebp-1.2.1/src/enc/webp_enc.c new file mode 100644 index 0000000..ce2db2e --- /dev/null +++ b/internal/libwebp-1.2.1/src/enc/webp_enc.c @@ -0,0 +1,410 @@ +// 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 "src/enc/cost_enc.h" +#include "src/enc/vp8i_enc.h" +#include "src/enc/vp8li_enc.h" +#include "src/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; + const size_t top_derr_size = + (config->quality <= ERROR_DIFFUSION_QUALITY || config->pass > 1) ? + mb_w * sizeof(*enc->top_derr_) : 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 + + top_derr_size // top diffusion error + + 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" + " top diffusion: %ld\n" + " non-zero: %ld\n" + " lf-stats: %ld\n" + " total: %ld\n", + sizeof(*enc) + WEBP_ALIGN_CST, info_size, + preds_size, samples_size, top_derr_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_ = 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_ = mem; + enc->uv_top_ = enc->y_top_ + top_stride; + mem += 2 * top_stride; + enc->top_derr_ = top_derr_size ? (DError*)mem : NULL; + mem += top_derr_size; + 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; +} + +//------------------------------------------------------------------------------ + +#if !defined(WEBP_DISABLE_STATS) +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); +} +#endif // !defined(WEBP_DISABLE_STATS) + +static void StoreStats(VP8Encoder* const enc) { +#if !defined(WEBP_DISABLE_STATS) + 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]; + } + } +#else // defined(WEBP_DISABLE_STATS) + WebPReportProgress(enc->pic_, 100, &enc->percent_); // done! +#endif // !defined(WEBP_DISABLE_STATS) +} + +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 (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; + } + } + } + + if (!config->exact) { + WebPCleanupTransparentArea(pic); + } + + 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) { + WebPReplaceTransparentPixels(pic, 0x000000); + } + + ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem. + } + + return ok; +} diff --git a/internal/libwebp-1.2.1/src/libwebp.pc.in b/internal/libwebp-1.2.1/src/libwebp.pc.in new file mode 100644 index 0000000..733bb6d --- /dev/null +++ b/internal/libwebp-1.2.1/src/libwebp.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libwebp +Description: Library for the WebP graphics format +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lwebp +Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ diff --git a/internal/libwebp-1.2.1/src/libwebp.rc b/internal/libwebp-1.2.1/src/libwebp.rc new file mode 100644 index 0000000..2deeaca --- /dev/null +++ b/internal/libwebp-1.2.1/src/libwebp.rc @@ -0,0 +1,41 @@ +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,2,1 + PRODUCTVERSION 1,0,2,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Google, Inc." + VALUE "FileDescription", "libwebp DLL" + VALUE "FileVersion", "1.2.1" + VALUE "InternalName", "libwebp.dll" + VALUE "LegalCopyright", "Copyright (C) 2021" + VALUE "OriginalFilename", "libwebp.dll" + VALUE "ProductName", "WebP Image Codec" + VALUE "ProductVersion", "1.2.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources diff --git a/internal/libwebp-1.2.1/src/libwebpdecoder.pc.in b/internal/libwebp-1.2.1/src/libwebpdecoder.pc.in new file mode 100644 index 0000000..3ef647a --- /dev/null +++ b/internal/libwebp-1.2.1/src/libwebpdecoder.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libwebpdecoder +Description: Library for the WebP graphics format (decode only) +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lwebpdecoder +Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ diff --git a/internal/libwebp-1.2.1/src/libwebpdecoder.rc b/internal/libwebp-1.2.1/src/libwebpdecoder.rc new file mode 100644 index 0000000..366f242 --- /dev/null +++ b/internal/libwebp-1.2.1/src/libwebpdecoder.rc @@ -0,0 +1,41 @@ +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,2,1 + PRODUCTVERSION 1,0,2,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Google, Inc." + VALUE "FileDescription", "libwebpdecoder DLL" + VALUE "FileVersion", "1.2.1" + VALUE "InternalName", "libwebpdecoder.dll" + VALUE "LegalCopyright", "Copyright (C) 2021" + VALUE "OriginalFilename", "libwebpdecoder.dll" + VALUE "ProductName", "WebP Image Decoder" + VALUE "ProductVersion", "1.2.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources diff --git a/internal/libwebp-1.2.1/src/mux/Makefile.am b/internal/libwebp-1.2.1/src/mux/Makefile.am new file mode 100644 index 0000000..252219f --- /dev/null +++ b/internal/libwebp-1.2.1/src/mux/Makefile.am @@ -0,0 +1,22 @@ +AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir) +lib_LTLIBRARIES = libwebpmux.la + +libwebpmux_la_SOURCES = +libwebpmux_la_SOURCES += anim_encode.c +libwebpmux_la_SOURCES += animi.h +libwebpmux_la_SOURCES += muxedit.c +libwebpmux_la_SOURCES += muxi.h +libwebpmux_la_SOURCES += muxinternal.c +libwebpmux_la_SOURCES += muxread.c + +libwebpmuxinclude_HEADERS = +libwebpmuxinclude_HEADERS += ../webp/mux.h +libwebpmuxinclude_HEADERS += ../webp/mux_types.h +libwebpmuxinclude_HEADERS += ../webp/types.h +noinst_HEADERS = +noinst_HEADERS += ../webp/format_constants.h + +libwebpmux_la_LIBADD = ../libwebp.la +libwebpmux_la_LDFLAGS = -no-undefined -version-info 3:7:0 -lm +libwebpmuxincludedir = $(includedir)/webp +pkgconfig_DATA = libwebpmux.pc diff --git a/internal/libwebp-1.2.1/src/mux/anim_encode.c b/internal/libwebp-1.2.1/src/mux/anim_encode.c new file mode 100644 index 0000000..7078d9a --- /dev/null +++ b/internal/libwebp-1.2.1/src/mux/anim_encode.c @@ -0,0 +1,1585 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// AnimEncoder implementation. +// + +#include +#include +#include // for pow() +#include +#include // for abs() + +#include "src/mux/animi.h" +#include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/encode.h" +#include "src/webp/format_constants.h" +#include "src/webp/mux.h" + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + +#define ERROR_STR_MAX_LENGTH 100 + +//------------------------------------------------------------------------------ +// Internal structs. + +// Stores frame rectangle dimensions. +typedef struct { + int x_offset_, y_offset_, width_, height_; +} FrameRectangle; + +// Used to store two candidates of encoded data for an animation frame. One of +// the two will be chosen later. +typedef struct { + WebPMuxFrameInfo sub_frame_; // Encoded frame rectangle. + WebPMuxFrameInfo key_frame_; // Encoded frame if it is a key-frame. + int is_key_frame_; // True if 'key_frame' has been chosen. +} EncodedFrame; + +struct WebPAnimEncoder { + const int canvas_width_; // Canvas width. + const int canvas_height_; // Canvas height. + const WebPAnimEncoderOptions options_; // Global encoding options. + + FrameRectangle prev_rect_; // Previous WebP frame rectangle. + WebPConfig last_config_; // Cached in case a re-encode is needed. + WebPConfig last_config_reversed_; // If 'last_config_' uses lossless, then + // this config uses lossy and vice versa; + // only valid if 'options_.allow_mixed' + // is true. + + WebPPicture* curr_canvas_; // Only pointer; we don't own memory. + + // Canvas buffers. + WebPPicture curr_canvas_copy_; // Possibly modified current canvas. + int curr_canvas_copy_modified_; // True if pixels in 'curr_canvas_copy_' + // differ from those in 'curr_canvas_'. + + WebPPicture prev_canvas_; // Previous canvas. + WebPPicture prev_canvas_disposed_; // Previous canvas disposed to background. + + // Encoded data. + EncodedFrame* encoded_frames_; // Array of encoded frames. + size_t size_; // Number of allocated frames. + size_t start_; // Frame start index. + size_t count_; // Number of valid frames. + size_t flush_count_; // If >0, 'flush_count' frames starting from + // 'start' are ready to be added to mux. + + // key-frame related. + int64_t best_delta_; // min(canvas size - frame size) over the frames. + // Can be negative in certain cases due to + // transparent pixels in a frame. + int keyframe_; // Index of selected key-frame relative to 'start_'. + int count_since_key_frame_; // Frames seen since the last key-frame. + + int first_timestamp_; // Timestamp of the first frame. + int prev_timestamp_; // Timestamp of the last added frame. + int prev_candidate_undecided_; // True if it's not yet decided if previous + // frame would be a sub-frame or a key-frame. + + // Misc. + int is_first_frame_; // True if first frame is yet to be added/being added. + int got_null_frame_; // True if WebPAnimEncoderAdd() has already been called + // with a NULL frame. + + size_t in_frame_count_; // Number of input frames processed so far. + size_t out_frame_count_; // Number of frames added to mux so far. This may be + // different from 'in_frame_count_' due to merging. + + WebPMux* mux_; // Muxer to assemble the WebP bitstream. + char error_str_[ERROR_STR_MAX_LENGTH]; // Error string. Empty if no error. +}; + +// ----------------------------------------------------------------------------- +// Life of WebPAnimEncoder object. + +#define DELTA_INFINITY (1ULL << 32) +#define KEYFRAME_NONE (-1) + +// Reset the counters in the WebPAnimEncoder. +static void ResetCounters(WebPAnimEncoder* const enc) { + enc->start_ = 0; + enc->count_ = 0; + enc->flush_count_ = 0; + enc->best_delta_ = DELTA_INFINITY; + enc->keyframe_ = KEYFRAME_NONE; +} + +static void DisableKeyframes(WebPAnimEncoderOptions* const enc_options) { + enc_options->kmax = INT_MAX; + enc_options->kmin = enc_options->kmax - 1; +} + +#define MAX_CACHED_FRAMES 30 + +static void SanitizeEncoderOptions(WebPAnimEncoderOptions* const enc_options) { + int print_warning = enc_options->verbose; + + if (enc_options->minimize_size) { + DisableKeyframes(enc_options); + } + + 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) { + enc_options->kmin = enc_options->kmax - 1; + if (print_warning) { + fprintf(stderr, "WARNING: Setting kmin = %d, so that kmin < kmax.\n", + enc_options->kmin); + } + } else { + const int kmin_limit = enc_options->kmax / 2 + 1; + if (enc_options->kmin < kmin_limit && kmin_limit < enc_options->kmax) { + // This ensures that enc.keyframe + kmin >= kmax is always true. So, we + // can flush all the frames in the 'count_since_key_frame == kmax' case. + enc_options->kmin = kmin_limit; + if (print_warning) { + fprintf(stderr, + "WARNING: Setting kmin = %d, so that kmin >= kmax / 2 + 1.\n", + enc_options->kmin); + } + } + } + // Limit the max number of frames that are allocated. + if (enc_options->kmax - enc_options->kmin > MAX_CACHED_FRAMES) { + enc_options->kmin = enc_options->kmax - MAX_CACHED_FRAMES; + if (print_warning) { + fprintf(stderr, + "WARNING: Setting kmin = %d, so that kmax - kmin <= %d.\n", + enc_options->kmin, MAX_CACHED_FRAMES); + } + } + assert(enc_options->kmin < enc_options->kmax); +} + +#undef MAX_CACHED_FRAMES + +static void DefaultEncoderOptions(WebPAnimEncoderOptions* const enc_options) { + enc_options->anim_params.loop_count = 0; + enc_options->anim_params.bgcolor = 0xffffffff; // White. + enc_options->minimize_size = 0; + DisableKeyframes(enc_options); + enc_options->allow_mixed = 0; + enc_options->verbose = 0; +} + +int WebPAnimEncoderOptionsInitInternal(WebPAnimEncoderOptions* enc_options, + int abi_version) { + if (enc_options == NULL || + WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_MUX_ABI_VERSION)) { + return 0; + } + DefaultEncoderOptions(enc_options); + return 1; +} + +// This starting value is more fit to WebPCleanupTransparentAreaLossless(). +#define TRANSPARENT_COLOR 0x00000000 + +static void ClearRectangle(WebPPicture* const picture, + int left, int top, int width, int height) { + int j; + for (j = top; j < top + height; ++j) { + uint32_t* const dst = picture->argb + j * picture->argb_stride; + int i; + for (i = left; i < left + width; ++i) { + dst[i] = TRANSPARENT_COLOR; + } + } +} + +static void WebPUtilClearPic(WebPPicture* const picture, + const FrameRectangle* const rect) { + if (rect != NULL) { + ClearRectangle(picture, rect->x_offset_, rect->y_offset_, + rect->width_, rect->height_); + } else { + ClearRectangle(picture, 0, 0, picture->width, picture->height); + } +} + +static void MarkNoError(WebPAnimEncoder* const enc) { + enc->error_str_[0] = '\0'; // Empty string. +} + +static void MarkError(WebPAnimEncoder* const enc, const char* str) { + if (snprintf(enc->error_str_, ERROR_STR_MAX_LENGTH, "%s.", str) < 0) { + assert(0); // FIX ME! + } +} + +static void MarkError2(WebPAnimEncoder* const enc, + const char* str, int error_code) { + if (snprintf(enc->error_str_, ERROR_STR_MAX_LENGTH, "%s: %d.", str, + error_code) < 0) { + assert(0); // FIX ME! + } +} + +WebPAnimEncoder* WebPAnimEncoderNewInternal( + int width, int height, const WebPAnimEncoderOptions* enc_options, + int abi_version) { + WebPAnimEncoder* enc; + + if (WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_MUX_ABI_VERSION)) { + return NULL; + } + if (width <= 0 || height <= 0 || + (width * (uint64_t)height) >= MAX_IMAGE_AREA) { + return NULL; + } + + enc = (WebPAnimEncoder*)WebPSafeCalloc(1, sizeof(*enc)); + if (enc == NULL) return NULL; + MarkNoError(enc); + + // Dimensions and options. + *(int*)&enc->canvas_width_ = width; + *(int*)&enc->canvas_height_ = height; + if (enc_options != NULL) { + *(WebPAnimEncoderOptions*)&enc->options_ = *enc_options; + SanitizeEncoderOptions((WebPAnimEncoderOptions*)&enc->options_); + } else { + DefaultEncoderOptions((WebPAnimEncoderOptions*)&enc->options_); + } + + // Canvas buffers. + if (!WebPPictureInit(&enc->curr_canvas_copy_) || + !WebPPictureInit(&enc->prev_canvas_) || + !WebPPictureInit(&enc->prev_canvas_disposed_)) { + goto Err; + } + enc->curr_canvas_copy_.width = width; + enc->curr_canvas_copy_.height = height; + enc->curr_canvas_copy_.use_argb = 1; + if (!WebPPictureAlloc(&enc->curr_canvas_copy_) || + !WebPPictureCopy(&enc->curr_canvas_copy_, &enc->prev_canvas_) || + !WebPPictureCopy(&enc->curr_canvas_copy_, &enc->prev_canvas_disposed_)) { + goto Err; + } + WebPUtilClearPic(&enc->prev_canvas_, NULL); + enc->curr_canvas_copy_modified_ = 1; + + // Encoded frames. + ResetCounters(enc); + // Note: one extra storage is for the previous frame. + enc->size_ = enc->options_.kmax - enc->options_.kmin + 1; + // We need space for at least 2 frames. But when kmin, kmax are both zero, + // enc->size_ will be 1. So we handle that special case below. + if (enc->size_ < 2) enc->size_ = 2; + enc->encoded_frames_ = + (EncodedFrame*)WebPSafeCalloc(enc->size_, sizeof(*enc->encoded_frames_)); + if (enc->encoded_frames_ == NULL) goto Err; + + enc->mux_ = WebPMuxNew(); + if (enc->mux_ == NULL) goto Err; + + enc->count_since_key_frame_ = 0; + enc->first_timestamp_ = 0; + enc->prev_timestamp_ = 0; + enc->prev_candidate_undecided_ = 0; + enc->is_first_frame_ = 1; + enc->got_null_frame_ = 0; + + return enc; // All OK. + + Err: + WebPAnimEncoderDelete(enc); + return NULL; +} + +// Release the data contained by 'encoded_frame'. +static void FrameRelease(EncodedFrame* const encoded_frame) { + if (encoded_frame != NULL) { + WebPDataClear(&encoded_frame->sub_frame_.bitstream); + WebPDataClear(&encoded_frame->key_frame_.bitstream); + memset(encoded_frame, 0, sizeof(*encoded_frame)); + } +} + +void WebPAnimEncoderDelete(WebPAnimEncoder* enc) { + if (enc != NULL) { + WebPPictureFree(&enc->curr_canvas_copy_); + WebPPictureFree(&enc->prev_canvas_); + WebPPictureFree(&enc->prev_canvas_disposed_); + if (enc->encoded_frames_ != NULL) { + size_t i; + for (i = 0; i < enc->size_; ++i) { + FrameRelease(&enc->encoded_frames_[i]); + } + WebPSafeFree(enc->encoded_frames_); + } + WebPMuxDelete(enc->mux_); + WebPSafeFree(enc); + } +} + +// ----------------------------------------------------------------------------- +// Frame addition. + +// Returns cached frame at the given 'position'. +static EncodedFrame* GetFrame(const WebPAnimEncoder* const enc, + size_t position) { + assert(enc->start_ + position < enc->size_); + return &enc->encoded_frames_[enc->start_ + position]; +} + +typedef int (*ComparePixelsFunc)(const uint32_t*, int, const uint32_t*, int, + int, int); + +// Returns true if 'length' number of pixels in 'src' and 'dst' are equal, +// assuming the given step sizes between pixels. +// 'max_allowed_diff' is unused and only there to allow function pointer use. +static WEBP_INLINE int ComparePixelsLossless(const uint32_t* src, int src_step, + const uint32_t* dst, int dst_step, + int length, int max_allowed_diff) { + (void)max_allowed_diff; + assert(length > 0); + while (length-- > 0) { + if (*src != *dst) { + return 0; + } + src += src_step; + dst += dst_step; + } + return 1; +} + +// Helper to check if each channel in 'src' and 'dst' is at most off by +// 'max_allowed_diff'. +static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst, + int max_allowed_diff) { + const int src_a = (src >> 24) & 0xff; + const int src_r = (src >> 16) & 0xff; + const int src_g = (src >> 8) & 0xff; + const int src_b = (src >> 0) & 0xff; + const int dst_a = (dst >> 24) & 0xff; + const int dst_r = (dst >> 16) & 0xff; + const int dst_g = (dst >> 8) & 0xff; + const int dst_b = (dst >> 0) & 0xff; + + 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 +// error bound, assuming the given step sizes between pixels. +static WEBP_INLINE int ComparePixelsLossy(const uint32_t* src, int src_step, + const uint32_t* dst, int dst_step, + int length, int max_allowed_diff) { + assert(length > 0); + while (length-- > 0) { + if (!PixelsAreSimilar(*src, *dst, max_allowed_diff)) { + return 0; + } + src += src_step; + dst += dst_step; + } + return 1; +} + +static int IsEmptyRect(const FrameRectangle* const rect) { + return (rect->width_ == 0) || (rect->height_ == 0); +} + +static int QualityToMaxDiff(float quality) { + const double val = pow(quality / 100., 0.5); + const double max_diff = 31 * (1 - val) + 1 * val; + return (int)(max_diff + 0.5); +} + +// Assumes that an initial valid guess of change rectangle 'rect' is passed. +static void MinimizeChangeRectangle(const WebPPicture* const src, + const WebPPicture* const dst, + FrameRectangle* const rect, + int is_lossless, float quality) { + int i, j; + const ComparePixelsFunc compare_pixels = + is_lossless ? ComparePixelsLossless : ComparePixelsLossy; + const int max_allowed_diff_lossy = QualityToMaxDiff(quality); + const int max_allowed_diff = is_lossless ? 0 : max_allowed_diff_lossy; + + // Assumption/correctness checks. + assert(src->width == dst->width && src->height == dst->height); + assert(rect->x_offset_ + rect->width_ <= dst->width); + assert(rect->y_offset_ + rect->height_ <= dst->height); + + // Left boundary. + for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) { + const uint32_t* const src_argb = + &src->argb[rect->y_offset_ * src->argb_stride + i]; + const uint32_t* const dst_argb = + &dst->argb[rect->y_offset_ * dst->argb_stride + i]; + if (compare_pixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, + rect->height_, max_allowed_diff)) { + --rect->width_; // Redundant column. + ++rect->x_offset_; + } else { + break; + } + } + if (rect->width_ == 0) goto NoChange; + + // Right boundary. + for (i = rect->x_offset_ + rect->width_ - 1; i >= rect->x_offset_; --i) { + const uint32_t* const src_argb = + &src->argb[rect->y_offset_ * src->argb_stride + i]; + const uint32_t* const dst_argb = + &dst->argb[rect->y_offset_ * dst->argb_stride + i]; + if (compare_pixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, + rect->height_, max_allowed_diff)) { + --rect->width_; // Redundant column. + } else { + break; + } + } + if (rect->width_ == 0) goto NoChange; + + // Top boundary. + for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) { + const uint32_t* const src_argb = + &src->argb[j * src->argb_stride + rect->x_offset_]; + const uint32_t* const dst_argb = + &dst->argb[j * dst->argb_stride + rect->x_offset_]; + if (compare_pixels(src_argb, 1, dst_argb, 1, rect->width_, + max_allowed_diff)) { + --rect->height_; // Redundant row. + ++rect->y_offset_; + } else { + break; + } + } + if (rect->height_ == 0) goto NoChange; + + // Bottom boundary. + for (j = rect->y_offset_ + rect->height_ - 1; j >= rect->y_offset_; --j) { + const uint32_t* const src_argb = + &src->argb[j * src->argb_stride + rect->x_offset_]; + const uint32_t* const dst_argb = + &dst->argb[j * dst->argb_stride + rect->x_offset_]; + if (compare_pixels(src_argb, 1, dst_argb, 1, rect->width_, + max_allowed_diff)) { + --rect->height_; // Redundant row. + } else { + break; + } + } + if (rect->height_ == 0) goto NoChange; + + if (IsEmptyRect(rect)) { + NoChange: + rect->x_offset_ = 0; + rect->y_offset_ = 0; + rect->width_ = 0; + rect->height_ = 0; + } +} + +// Snap rectangle to even offsets (and adjust dimensions if needed). +static WEBP_INLINE void SnapToEvenOffsets(FrameRectangle* const rect) { + rect->width_ += (rect->x_offset_ & 1); + rect->height_ += (rect->y_offset_ & 1); + rect->x_offset_ &= ~1; + rect->y_offset_ &= ~1; +} + +typedef struct { + int should_try_; // Should try this set of parameters. + int empty_rect_allowed_; // Frame with empty rectangle can be skipped. + FrameRectangle rect_ll_; // Frame rectangle for lossless compression. + WebPPicture sub_frame_ll_; // Sub-frame pic for lossless compression. + FrameRectangle rect_lossy_; // Frame rectangle for lossy compression. + // Could be smaller than rect_ll_ as pixels + // with small diffs can be ignored. + WebPPicture sub_frame_lossy_; // Sub-frame pic for lossless compression. +} SubFrameParams; + +static int SubFrameParamsInit(SubFrameParams* const params, + int should_try, int empty_rect_allowed) { + params->should_try_ = should_try; + params->empty_rect_allowed_ = empty_rect_allowed; + if (!WebPPictureInit(¶ms->sub_frame_ll_) || + !WebPPictureInit(¶ms->sub_frame_lossy_)) { + return 0; + } + return 1; +} + +static void SubFrameParamsFree(SubFrameParams* const params) { + WebPPictureFree(¶ms->sub_frame_ll_); + WebPPictureFree(¶ms->sub_frame_lossy_); +} + +// Given previous and current canvas, picks the optimal rectangle for the +// current frame based on 'is_lossless' and other parameters. Assumes that the +// initial guess 'rect' is valid. +static int GetSubRect(const WebPPicture* const prev_canvas, + const WebPPicture* const curr_canvas, int is_key_frame, + int is_first_frame, int empty_rect_allowed, + int is_lossless, float quality, + FrameRectangle* const rect, + WebPPicture* const sub_frame) { + if (!is_key_frame || is_first_frame) { // Optimize frame rectangle. + // Note: This behaves as expected for first frame, as 'prev_canvas' is + // initialized to a fully transparent canvas in the beginning. + MinimizeChangeRectangle(prev_canvas, curr_canvas, rect, + is_lossless, quality); + } + + if (IsEmptyRect(rect)) { + if (empty_rect_allowed) { // No need to get 'sub_frame'. + return 1; + } else { // Force a 1x1 rectangle. + rect->width_ = 1; + rect->height_ = 1; + assert(rect->x_offset_ == 0); + assert(rect->y_offset_ == 0); + } + } + + SnapToEvenOffsets(rect); + return WebPPictureView(curr_canvas, rect->x_offset_, rect->y_offset_, + rect->width_, rect->height_, sub_frame); +} + +// Picks optimal frame rectangle for both lossless and lossy compression. The +// initial guess for frame rectangles will be the full canvas. +static int GetSubRects(const WebPPicture* const prev_canvas, + const WebPPicture* const curr_canvas, int is_key_frame, + int is_first_frame, float quality, + SubFrameParams* const params) { + // Lossless frame rectangle. + params->rect_ll_.x_offset_ = 0; + params->rect_ll_.y_offset_ = 0; + params->rect_ll_.width_ = curr_canvas->width; + params->rect_ll_.height_ = curr_canvas->height; + if (!GetSubRect(prev_canvas, curr_canvas, is_key_frame, is_first_frame, + params->empty_rect_allowed_, 1, quality, + ¶ms->rect_ll_, ¶ms->sub_frame_ll_)) { + return 0; + } + // Lossy frame rectangle. + params->rect_lossy_ = params->rect_ll_; // seed with lossless rect. + return GetSubRect(prev_canvas, curr_canvas, is_key_frame, is_first_frame, + params->empty_rect_allowed_, 0, quality, + ¶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) { + FrameRectangle 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 FrameRectangle* const rect, + WebPPicture* const curr_canvas) { + assert(rect != NULL); + if (dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { + WebPUtilClearPic(curr_canvas, rect); + } +} + +static uint32_t RectArea(const FrameRectangle* const rect) { + return (uint32_t)rect->width_ * rect->height_; +} + +static int IsLosslessBlendingPossible(const WebPPicture* const src, + const WebPPicture* const dst, + const FrameRectangle* const rect) { + int i, j; + assert(src->width == dst->width && src->height == dst->height); + assert(rect->x_offset_ + rect->width_ <= dst->width); + assert(rect->y_offset_ + rect->height_ <= dst->height); + for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) { + for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) { + const uint32_t src_pixel = src->argb[j * src->argb_stride + i]; + const uint32_t dst_pixel = dst->argb[j * dst->argb_stride + i]; + const uint32_t dst_alpha = dst_pixel >> 24; + if (dst_alpha != 0xff && src_pixel != dst_pixel) { + // In this case, if we use blending, we can't attain the desired + // 'dst_pixel' value for this pixel. So, blending is not possible. + return 0; + } + } + } + return 1; +} + +static int IsLossyBlendingPossible(const WebPPicture* const src, + const WebPPicture* const dst, + const FrameRectangle* const rect, + float quality) { + const int max_allowed_diff_lossy = QualityToMaxDiff(quality); + int i, j; + assert(src->width == dst->width && src->height == dst->height); + assert(rect->x_offset_ + rect->width_ <= dst->width); + assert(rect->y_offset_ + rect->height_ <= dst->height); + for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) { + for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) { + const uint32_t src_pixel = src->argb[j * src->argb_stride + i]; + const uint32_t dst_pixel = dst->argb[j * dst->argb_stride + i]; + const uint32_t dst_alpha = dst_pixel >> 24; + if (dst_alpha != 0xff && + !PixelsAreSimilar(src_pixel, dst_pixel, max_allowed_diff_lossy)) { + // In this case, if we use blending, we can't attain the desired + // 'dst_pixel' value for this pixel. So, blending is not possible. + return 0; + } + } + } + return 1; +} + +// For pixels in 'rect', replace those pixels in 'dst' that are same as 'src' by +// transparent pixels. +// Returns true if at least one pixel gets modified. +static int IncreaseTransparency(const WebPPicture* const src, + const FrameRectangle* const rect, + WebPPicture* const dst) { + int i, j; + int modified = 0; + assert(src != NULL && dst != NULL && rect != NULL); + assert(src->width == dst->width && src->height == dst->height); + for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) { + const uint32_t* const psrc = src->argb + j * src->argb_stride; + uint32_t* const pdst = dst->argb + j * dst->argb_stride; + for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) { + if (psrc[i] == pdst[i] && pdst[i] != TRANSPARENT_COLOR) { + pdst[i] = TRANSPARENT_COLOR; + modified = 1; + } + } + } + return modified; +} + +#undef TRANSPARENT_COLOR + +// Replace similar blocks of pixels by a 'see-through' transparent block +// with uniform average color. +// Assumes lossy compression is being used. +// Returns true if at least one pixel gets modified. +static int FlattenSimilarBlocks(const WebPPicture* const src, + const FrameRectangle* const rect, + WebPPicture* const dst, float quality) { + const int max_allowed_diff_lossy = QualityToMaxDiff(quality); + int i, j; + int modified = 0; + const int block_size = 8; + const int y_start = (rect->y_offset_ + block_size) & ~(block_size - 1); + const int y_end = (rect->y_offset_ + rect->height_) & ~(block_size - 1); + const int x_start = (rect->x_offset_ + block_size) & ~(block_size - 1); + const int x_end = (rect->x_offset_ + rect->width_) & ~(block_size - 1); + assert(src != NULL && dst != NULL && rect != NULL); + assert(src->width == dst->width && src->height == dst->height); + assert((block_size & (block_size - 1)) == 0); // must be a power of 2 + // Iterate over each block and count similar pixels. + for (j = y_start; j < y_end; j += block_size) { + for (i = x_start; i < x_end; i += block_size) { + int cnt = 0; + int avg_r = 0, avg_g = 0, avg_b = 0; + int x, y; + const uint32_t* const psrc = src->argb + j * src->argb_stride + i; + uint32_t* const pdst = dst->argb + j * dst->argb_stride + i; + for (y = 0; y < block_size; ++y) { + for (x = 0; x < block_size; ++x) { + const uint32_t src_pixel = psrc[x + y * src->argb_stride]; + const int alpha = src_pixel >> 24; + if (alpha == 0xff && + PixelsAreSimilar(src_pixel, pdst[x + y * dst->argb_stride], + max_allowed_diff_lossy)) { + ++cnt; + avg_r += (src_pixel >> 16) & 0xff; + avg_g += (src_pixel >> 8) & 0xff; + avg_b += (src_pixel >> 0) & 0xff; + } + } + } + // If we have a fully similar block, we replace it with an + // average transparent block. This compresses better in lossy mode. + if (cnt == block_size * block_size) { + const uint32_t color = (0x00 << 24) | + ((avg_r / cnt) << 16) | + ((avg_g / cnt) << 8) | + ((avg_b / cnt) << 0); + for (y = 0; y < block_size; ++y) { + for (x = 0; x < block_size; ++x) { + pdst[x + y * dst->argb_stride] = color; + } + } + modified = 1; + } + } + } + return modified; +} + +static int EncodeFrame(const WebPConfig* const config, WebPPicture* const pic, + WebPMemoryWriter* const memory) { + pic->use_argb = 1; + pic->writer = WebPMemoryWrite; + pic->custom_ptr = memory; + if (!WebPEncode(config, pic)) { + return 0; + } + return 1; +} + +// Struct representing a candidate encoded frame including its metadata. +typedef struct { + WebPMemoryWriter mem_; + WebPMuxFrameInfo info_; + FrameRectangle rect_; + int evaluate_; // True if this candidate should be evaluated. +} Candidate; + +// Generates a candidate encoded frame given a picture and metadata. +static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame, + const FrameRectangle* const rect, + const WebPConfig* const encoder_config, + int use_blending, + Candidate* const candidate) { + WebPConfig config = *encoder_config; + WebPEncodingError error_code = VP8_ENC_OK; + assert(candidate != NULL); + memset(candidate, 0, sizeof(*candidate)); + + // Set frame rect and info. + candidate->rect_ = *rect; + candidate->info_.id = WEBP_CHUNK_ANMF; + candidate->info_.x_offset = rect->x_offset_; + candidate->info_.y_offset = rect->y_offset_; + candidate->info_.dispose_method = WEBP_MUX_DISPOSE_NONE; // Set later. + candidate->info_.blend_method = + use_blending ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND; + candidate->info_.duration = 0; // Set in next call to WebPAnimEncoderAdd(). + + // Encode picture. + WebPMemoryWriterInit(&candidate->mem_); + + if (!config.lossless && use_blending) { + // Disable filtering to avoid blockiness in reconstructed frames at the + // time of decoding. + config.autofilter = 0; + config.filter_strength = 0; + } + if (!EncodeFrame(&config, sub_frame, &candidate->mem_)) { + error_code = sub_frame->error_code; + goto Err; + } + + candidate->evaluate_ = 1; + return error_code; + + Err: + WebPMemoryWriterClear(&candidate->mem_); + return error_code; +} + +static void CopyCurrentCanvas(WebPAnimEncoder* const enc) { + if (enc->curr_canvas_copy_modified_) { + WebPCopyPixels(enc->curr_canvas_, &enc->curr_canvas_copy_); + enc->curr_canvas_copy_.progress_hook = enc->curr_canvas_->progress_hook; + enc->curr_canvas_copy_.user_data = enc->curr_canvas_->user_data; + enc->curr_canvas_copy_modified_ = 0; + } +} + +enum { + LL_DISP_NONE = 0, + LL_DISP_BG, + LOSSY_DISP_NONE, + LOSSY_DISP_BG, + CANDIDATE_COUNT +}; + +#define MIN_COLORS_LOSSY 31 // Don't try lossy below this threshold. +#define MAX_COLORS_LOSSLESS 194 // Don't try lossless above this threshold. + +// Generates candidates for a given dispose method given pre-filled sub-frame +// 'params'. +static WebPEncodingError GenerateCandidates( + WebPAnimEncoder* const enc, Candidate candidates[CANDIDATE_COUNT], + WebPMuxAnimDispose dispose_method, int is_lossless, int is_key_frame, + SubFrameParams* const params, + const WebPConfig* const config_ll, const WebPConfig* const config_lossy) { + WebPEncodingError error_code = VP8_ENC_OK; + const int is_dispose_none = (dispose_method == WEBP_MUX_DISPOSE_NONE); + Candidate* const candidate_ll = + is_dispose_none ? &candidates[LL_DISP_NONE] : &candidates[LL_DISP_BG]; + Candidate* const candidate_lossy = is_dispose_none + ? &candidates[LOSSY_DISP_NONE] + : &candidates[LOSSY_DISP_BG]; + WebPPicture* const curr_canvas = &enc->curr_canvas_copy_; + const WebPPicture* const prev_canvas = + is_dispose_none ? &enc->prev_canvas_ : &enc->prev_canvas_disposed_; + int use_blending_ll, use_blending_lossy; + int evaluate_ll, evaluate_lossy; + + CopyCurrentCanvas(enc); + use_blending_ll = + !is_key_frame && + IsLosslessBlendingPossible(prev_canvas, curr_canvas, ¶ms->rect_ll_); + use_blending_lossy = + !is_key_frame && + IsLossyBlendingPossible(prev_canvas, curr_canvas, ¶ms->rect_lossy_, + config_lossy->quality); + + // Pick candidates to be tried. + if (!enc->options_.allow_mixed) { + evaluate_ll = is_lossless; + evaluate_lossy = !is_lossless; + } else if (enc->options_.minimize_size) { + evaluate_ll = 1; + evaluate_lossy = 1; + } else { // Use a heuristic for trying lossless and/or lossy compression. + const int num_colors = WebPGetColorPalette(¶ms->sub_frame_ll_, NULL); + evaluate_ll = (num_colors < MAX_COLORS_LOSSLESS); + evaluate_lossy = (num_colors >= MIN_COLORS_LOSSY); + } + + // Generate candidates. + if (evaluate_ll) { + CopyCurrentCanvas(enc); + if (use_blending_ll) { + enc->curr_canvas_copy_modified_ = + IncreaseTransparency(prev_canvas, ¶ms->rect_ll_, curr_canvas); + } + error_code = EncodeCandidate(¶ms->sub_frame_ll_, ¶ms->rect_ll_, + config_ll, use_blending_ll, candidate_ll); + if (error_code != VP8_ENC_OK) return error_code; + } + if (evaluate_lossy) { + CopyCurrentCanvas(enc); + if (use_blending_lossy) { + enc->curr_canvas_copy_modified_ = + FlattenSimilarBlocks(prev_canvas, ¶ms->rect_lossy_, curr_canvas, + config_lossy->quality); + } + error_code = + EncodeCandidate(¶ms->sub_frame_lossy_, ¶ms->rect_lossy_, + config_lossy, use_blending_lossy, candidate_lossy); + if (error_code != VP8_ENC_OK) return error_code; + enc->curr_canvas_copy_modified_ = 1; + } + return error_code; +} + +#undef MIN_COLORS_LOSSY +#undef MAX_COLORS_LOSSLESS + +static void GetEncodedData(const WebPMemoryWriter* const memory, + WebPData* const encoded_data) { + encoded_data->bytes = memory->mem; + encoded_data->size = memory->size; +} + +// Sets dispose method of the previous frame to be 'dispose_method'. +static void SetPreviousDisposeMethod(WebPAnimEncoder* const enc, + WebPMuxAnimDispose dispose_method) { + const size_t position = enc->count_ - 2; + EncodedFrame* const prev_enc_frame = GetFrame(enc, position); + assert(enc->count_ >= 2); // As current and previous frames are in enc. + + if (enc->prev_candidate_undecided_) { + assert(dispose_method == WEBP_MUX_DISPOSE_NONE); + prev_enc_frame->sub_frame_.dispose_method = dispose_method; + prev_enc_frame->key_frame_.dispose_method = dispose_method; + } else { + WebPMuxFrameInfo* const prev_info = prev_enc_frame->is_key_frame_ + ? &prev_enc_frame->key_frame_ + : &prev_enc_frame->sub_frame_; + prev_info->dispose_method = dispose_method; + } +} + +static int IncreasePreviousDuration(WebPAnimEncoder* const enc, int duration) { + const size_t position = enc->count_ - 1; + EncodedFrame* const prev_enc_frame = GetFrame(enc, position); + int new_duration; + + assert(enc->count_ >= 1); + assert(!prev_enc_frame->is_key_frame_ || + prev_enc_frame->sub_frame_.duration == + prev_enc_frame->key_frame_.duration); + assert(prev_enc_frame->sub_frame_.duration == + (prev_enc_frame->sub_frame_.duration & (MAX_DURATION - 1))); + assert(duration == (duration & (MAX_DURATION - 1))); + + new_duration = prev_enc_frame->sub_frame_.duration + duration; + if (new_duration >= MAX_DURATION) { // Special case. + // Separate out previous frame from earlier merged frames to avoid overflow. + // We add a 1x1 transparent frame for the previous frame, with blending on. + const FrameRectangle rect = { 0, 0, 1, 1 }; + const uint8_t lossless_1x1_bytes[] = { + 0x52, 0x49, 0x46, 0x46, 0x14, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50, + 0x56, 0x50, 0x38, 0x4c, 0x08, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x10, 0x88, 0x88, 0x08 + }; + const WebPData lossless_1x1 = { + lossless_1x1_bytes, sizeof(lossless_1x1_bytes) + }; + const uint8_t lossy_1x1_bytes[] = { + 0x52, 0x49, 0x46, 0x46, 0x40, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50, + 0x56, 0x50, 0x38, 0x58, 0x0a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x4c, 0x50, 0x48, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x56, 0x50, 0x38, 0x20, 0x18, 0x00, 0x00, 0x00, + 0x30, 0x01, 0x00, 0x9d, 0x01, 0x2a, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x34, 0x25, 0xa4, 0x00, 0x03, 0x70, 0x00, 0xfe, 0xfb, 0xfd, 0x50, 0x00 + }; + const WebPData lossy_1x1 = { lossy_1x1_bytes, sizeof(lossy_1x1_bytes) }; + const int can_use_lossless = + (enc->last_config_.lossless || enc->options_.allow_mixed); + EncodedFrame* const curr_enc_frame = GetFrame(enc, enc->count_); + curr_enc_frame->is_key_frame_ = 0; + curr_enc_frame->sub_frame_.id = WEBP_CHUNK_ANMF; + curr_enc_frame->sub_frame_.x_offset = 0; + curr_enc_frame->sub_frame_.y_offset = 0; + curr_enc_frame->sub_frame_.dispose_method = WEBP_MUX_DISPOSE_NONE; + curr_enc_frame->sub_frame_.blend_method = WEBP_MUX_BLEND; + curr_enc_frame->sub_frame_.duration = duration; + if (!WebPDataCopy(can_use_lossless ? &lossless_1x1 : &lossy_1x1, + &curr_enc_frame->sub_frame_.bitstream)) { + return 0; + } + ++enc->count_; + ++enc->count_since_key_frame_; + enc->flush_count_ = enc->count_ - 1; + enc->prev_candidate_undecided_ = 0; + enc->prev_rect_ = rect; + } else { // Regular case. + // Increase duration of the previous frame by 'duration'. + prev_enc_frame->sub_frame_.duration = new_duration; + prev_enc_frame->key_frame_.duration = new_duration; + } + return 1; +} + +// Pick the candidate encoded frame with smallest size and release other +// candidates. +// TODO(later): Perhaps a rough SSIM/PSNR produced by the encoder should +// also be a criteria, in addition to sizes. +static void PickBestCandidate(WebPAnimEncoder* const enc, + Candidate* const candidates, int is_key_frame, + EncodedFrame* const encoded_frame) { + int i; + int best_idx = -1; + size_t best_size = ~0; + for (i = 0; i < CANDIDATE_COUNT; ++i) { + if (candidates[i].evaluate_) { + const size_t candidate_size = candidates[i].mem_.size; + if (candidate_size < best_size) { + best_idx = i; + best_size = candidate_size; + } + } + } + assert(best_idx != -1); + for (i = 0; i < CANDIDATE_COUNT; ++i) { + if (candidates[i].evaluate_) { + if (i == best_idx) { + WebPMuxFrameInfo* const dst = is_key_frame + ? &encoded_frame->key_frame_ + : &encoded_frame->sub_frame_; + *dst = candidates[i].info_; + GetEncodedData(&candidates[i].mem_, &dst->bitstream); + if (!is_key_frame) { + // Note: Previous dispose method only matters for non-keyframes. + // Also, we don't want to modify previous dispose method that was + // selected when a non key-frame was assumed. + const WebPMuxAnimDispose prev_dispose_method = + (best_idx == LL_DISP_NONE || best_idx == LOSSY_DISP_NONE) + ? WEBP_MUX_DISPOSE_NONE + : WEBP_MUX_DISPOSE_BACKGROUND; + SetPreviousDisposeMethod(enc, prev_dispose_method); + } + enc->prev_rect_ = candidates[i].rect_; // save for next frame. + } else { + WebPMemoryWriterClear(&candidates[i].mem_); + candidates[i].evaluate_ = 0; + } + } + } +} + +// Depending on the configuration, tries different compressions +// (lossy/lossless), dispose methods, blending methods etc to encode the current +// frame and outputs the best one in 'encoded_frame'. +// 'frame_skipped' will be set to true if this frame should actually be skipped. +static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, + const WebPConfig* const config, + int is_key_frame, + EncodedFrame* const encoded_frame, + int* const frame_skipped) { + int i; + WebPEncodingError error_code = VP8_ENC_OK; + const WebPPicture* const curr_canvas = &enc->curr_canvas_copy_; + const WebPPicture* const prev_canvas = &enc->prev_canvas_; + Candidate candidates[CANDIDATE_COUNT]; + const int is_lossless = config->lossless; + const int consider_lossless = is_lossless || enc->options_.allow_mixed; + const int consider_lossy = !is_lossless || enc->options_.allow_mixed; + const int is_first_frame = enc->is_first_frame_; + + // First frame cannot be skipped as there is no 'previous frame' to merge it + // to. So, empty rectangle is not allowed for the first frame. + const int empty_rect_allowed_none = !is_first_frame; + + // Even if there is exact pixel match between 'disposed previous canvas' and + // 'current canvas', we can't skip current frame, as there may not be exact + // pixel match between 'previous canvas' and 'current canvas'. So, we don't + // allow empty rectangle in this case. + const int empty_rect_allowed_bg = 0; + + // If current frame is a key-frame, dispose method of previous frame doesn't + // matter, so we don't try dispose to background. + // Also, if key-frame insertion is on, and previous frame could be picked as + // either a sub-frame or a key-frame, then we can't be sure about what frame + // rectangle would be disposed. In that case too, we don't try dispose to + // background. + const int dispose_bg_possible = + !is_key_frame && !enc->prev_candidate_undecided_; + + SubFrameParams dispose_none_params; + SubFrameParams dispose_bg_params; + + WebPConfig config_ll = *config; + WebPConfig config_lossy = *config; + config_ll.lossless = 1; + config_lossy.lossless = 0; + enc->last_config_ = *config; + enc->last_config_reversed_ = config->lossless ? config_lossy : config_ll; + *frame_skipped = 0; + + if (!SubFrameParamsInit(&dispose_none_params, 1, empty_rect_allowed_none) || + !SubFrameParamsInit(&dispose_bg_params, 0, empty_rect_allowed_bg)) { + return VP8_ENC_ERROR_INVALID_CONFIGURATION; + } + + memset(candidates, 0, sizeof(candidates)); + + // Change-rectangle assuming previous frame was DISPOSE_NONE. + if (!GetSubRects(prev_canvas, curr_canvas, is_key_frame, is_first_frame, + config_lossy.quality, &dispose_none_params)) { + error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION; + goto Err; + } + + if ((consider_lossless && IsEmptyRect(&dispose_none_params.rect_ll_)) || + (consider_lossy && IsEmptyRect(&dispose_none_params.rect_lossy_))) { + // Don't encode the frame at all. Instead, the duration of the previous + // frame will be increased later. + assert(empty_rect_allowed_none); + *frame_skipped = 1; + goto End; + } + + if (dispose_bg_possible) { + // Change-rectangle assuming previous frame was DISPOSE_BACKGROUND. + WebPPicture* const prev_canvas_disposed = &enc->prev_canvas_disposed_; + WebPCopyPixels(prev_canvas, prev_canvas_disposed); + DisposeFrameRectangle(WEBP_MUX_DISPOSE_BACKGROUND, &enc->prev_rect_, + prev_canvas_disposed); + + if (!GetSubRects(prev_canvas_disposed, curr_canvas, is_key_frame, + is_first_frame, config_lossy.quality, + &dispose_bg_params)) { + error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION; + goto Err; + } + assert(!IsEmptyRect(&dispose_bg_params.rect_ll_)); + assert(!IsEmptyRect(&dispose_bg_params.rect_lossy_)); + + if (enc->options_.minimize_size) { // Try both dispose methods. + dispose_bg_params.should_try_ = 1; + dispose_none_params.should_try_ = 1; + } else if ((is_lossless && + RectArea(&dispose_bg_params.rect_ll_) < + RectArea(&dispose_none_params.rect_ll_)) || + (!is_lossless && + RectArea(&dispose_bg_params.rect_lossy_) < + RectArea(&dispose_none_params.rect_lossy_))) { + dispose_bg_params.should_try_ = 1; // Pick DISPOSE_BACKGROUND. + dispose_none_params.should_try_ = 0; + } + } + + if (dispose_none_params.should_try_) { + error_code = GenerateCandidates( + enc, candidates, WEBP_MUX_DISPOSE_NONE, is_lossless, is_key_frame, + &dispose_none_params, &config_ll, &config_lossy); + if (error_code != VP8_ENC_OK) goto Err; + } + + if (dispose_bg_params.should_try_) { + assert(!enc->is_first_frame_); + assert(dispose_bg_possible); + error_code = GenerateCandidates( + enc, candidates, WEBP_MUX_DISPOSE_BACKGROUND, is_lossless, is_key_frame, + &dispose_bg_params, &config_ll, &config_lossy); + if (error_code != VP8_ENC_OK) goto Err; + } + + PickBestCandidate(enc, candidates, is_key_frame, encoded_frame); + + goto End; + + Err: + for (i = 0; i < CANDIDATE_COUNT; ++i) { + if (candidates[i].evaluate_) { + WebPMemoryWriterClear(&candidates[i].mem_); + } + } + + End: + SubFrameParamsFree(&dispose_none_params); + SubFrameParamsFree(&dispose_bg_params); + return error_code; +} + +// Calculate the penalty incurred if we encode given frame as a key frame +// instead of a sub-frame. +static int64_t KeyFramePenalty(const EncodedFrame* const encoded_frame) { + return ((int64_t)encoded_frame->key_frame_.bitstream.size - + encoded_frame->sub_frame_.bitstream.size); +} + +static int CacheFrame(WebPAnimEncoder* const enc, + const WebPConfig* const config) { + int ok = 0; + int frame_skipped = 0; + WebPEncodingError error_code = VP8_ENC_OK; + const size_t position = enc->count_; + EncodedFrame* const encoded_frame = GetFrame(enc, position); + + ++enc->count_; + + if (enc->is_first_frame_) { // Add this as a key-frame. + error_code = SetFrame(enc, config, 1, encoded_frame, &frame_skipped); + if (error_code != VP8_ENC_OK) goto End; + assert(frame_skipped == 0); // First frame can't be skipped, even if empty. + assert(position == 0 && enc->count_ == 1); + encoded_frame->is_key_frame_ = 1; + enc->flush_count_ = 0; + enc->count_since_key_frame_ = 0; + enc->prev_candidate_undecided_ = 0; + } else { + ++enc->count_since_key_frame_; + if (enc->count_since_key_frame_ <= enc->options_.kmin) { + // Add this as a frame rectangle. + error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped); + if (error_code != VP8_ENC_OK) goto End; + if (frame_skipped) goto Skip; + encoded_frame->is_key_frame_ = 0; + enc->flush_count_ = enc->count_ - 1; + enc->prev_candidate_undecided_ = 0; + } else { + int64_t curr_delta; + FrameRectangle prev_rect_key, prev_rect_sub; + + // Add this as a frame rectangle to enc. + error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped); + if (error_code != VP8_ENC_OK) goto End; + if (frame_skipped) goto Skip; + prev_rect_sub = enc->prev_rect_; + + + // Add this as a key-frame to enc, too. + error_code = SetFrame(enc, config, 1, encoded_frame, &frame_skipped); + if (error_code != VP8_ENC_OK) goto End; + assert(frame_skipped == 0); // Key-frame cannot be an empty rectangle. + prev_rect_key = enc->prev_rect_; + + // Analyze size difference of the two variants. + curr_delta = KeyFramePenalty(encoded_frame); + if (curr_delta <= enc->best_delta_) { // Pick this as the key-frame. + if (enc->keyframe_ != KEYFRAME_NONE) { + EncodedFrame* const old_keyframe = GetFrame(enc, enc->keyframe_); + assert(old_keyframe->is_key_frame_); + old_keyframe->is_key_frame_ = 0; + } + encoded_frame->is_key_frame_ = 1; + enc->prev_candidate_undecided_ = 1; + enc->keyframe_ = (int)position; + enc->best_delta_ = curr_delta; + enc->flush_count_ = enc->count_ - 1; // We can flush previous frames. + } else { + encoded_frame->is_key_frame_ = 0; + enc->prev_candidate_undecided_ = 0; + } + // Note: We need '>=' below because when kmin and kmax are both zero, + // count_since_key_frame will always be > kmax. + if (enc->count_since_key_frame_ >= enc->options_.kmax) { + enc->flush_count_ = enc->count_ - 1; + enc->count_since_key_frame_ = 0; + enc->keyframe_ = KEYFRAME_NONE; + enc->best_delta_ = DELTA_INFINITY; + } + if (!enc->prev_candidate_undecided_) { + enc->prev_rect_ = + encoded_frame->is_key_frame_ ? prev_rect_key : prev_rect_sub; + } + } + } + + // Update previous to previous and previous canvases for next call. + WebPCopyPixels(enc->curr_canvas_, &enc->prev_canvas_); + enc->is_first_frame_ = 0; + + Skip: + ok = 1; + ++enc->in_frame_count_; + + End: + if (!ok || frame_skipped) { + FrameRelease(encoded_frame); + // We reset some counters, as the frame addition failed/was skipped. + --enc->count_; + if (!enc->is_first_frame_) --enc->count_since_key_frame_; + if (!ok) { + MarkError2(enc, "ERROR adding frame. WebPEncodingError", error_code); + } + } + enc->curr_canvas_->error_code = error_code; // report error_code + assert(ok || error_code != VP8_ENC_OK); + return ok; +} + +static int FlushFrames(WebPAnimEncoder* const enc) { + while (enc->flush_count_ > 0) { + WebPMuxError err; + EncodedFrame* const curr = GetFrame(enc, 0); + const WebPMuxFrameInfo* const info = + curr->is_key_frame_ ? &curr->key_frame_ : &curr->sub_frame_; + assert(enc->mux_ != NULL); + err = WebPMuxPushFrame(enc->mux_, info, 1); + if (err != WEBP_MUX_OK) { + MarkError2(enc, "ERROR adding frame. WebPMuxError", err); + return 0; + } + if (enc->options_.verbose) { + fprintf(stderr, "INFO: Added frame. offset:%d,%d dispose:%d blend:%d\n", + info->x_offset, info->y_offset, info->dispose_method, + info->blend_method); + } + ++enc->out_frame_count_; + FrameRelease(curr); + ++enc->start_; + --enc->flush_count_; + --enc->count_; + if (enc->keyframe_ != KEYFRAME_NONE) --enc->keyframe_; + } + + if (enc->count_ == 1 && enc->start_ != 0) { + // Move enc->start to index 0. + const int enc_start_tmp = (int)enc->start_; + EncodedFrame temp = enc->encoded_frames_[0]; + enc->encoded_frames_[0] = enc->encoded_frames_[enc_start_tmp]; + enc->encoded_frames_[enc_start_tmp] = temp; + FrameRelease(&enc->encoded_frames_[enc_start_tmp]); + enc->start_ = 0; + } + return 1; +} + +#undef DELTA_INFINITY +#undef KEYFRAME_NONE + +int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp, + const WebPConfig* encoder_config) { + WebPConfig config; + int ok; + + if (enc == NULL) { + return 0; + } + MarkNoError(enc); + + if (!enc->is_first_frame_) { + // Make sure timestamps are non-decreasing (integer wrap-around is OK). + const uint32_t prev_frame_duration = + (uint32_t)timestamp - enc->prev_timestamp_; + if (prev_frame_duration >= MAX_DURATION) { + if (frame != NULL) { + frame->error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION; + } + MarkError(enc, "ERROR adding frame: timestamps must be non-decreasing"); + return 0; + } + if (!IncreasePreviousDuration(enc, (int)prev_frame_duration)) { + return 0; + } + // IncreasePreviousDuration() may add a frame to avoid exceeding + // MAX_DURATION which could cause CacheFrame() to over read encoded_frames_ + // before the next flush. + if (enc->count_ == enc->size_ && !FlushFrames(enc)) { + return 0; + } + } else { + enc->first_timestamp_ = timestamp; + } + + if (frame == NULL) { // Special: last call. + enc->got_null_frame_ = 1; + enc->prev_timestamp_ = timestamp; + return 1; + } + + if (frame->width != enc->canvas_width_ || + frame->height != enc->canvas_height_) { + frame->error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION; + MarkError(enc, "ERROR adding frame: Invalid frame dimensions"); + return 0; + } + + if (!frame->use_argb) { // Convert frame from YUV(A) to ARGB. + if (enc->options_.verbose) { + fprintf(stderr, "WARNING: Converting frame from YUV(A) to ARGB format; " + "this incurs a small loss.\n"); + } + if (!WebPPictureYUVAToARGB(frame)) { + MarkError(enc, "ERROR converting frame from YUV(A) to ARGB"); + return 0; + } + } + + if (encoder_config != NULL) { + if (!WebPValidateConfig(encoder_config)) { + MarkError(enc, "ERROR adding frame: Invalid WebPConfig"); + return 0; + } + config = *encoder_config; + } else { + WebPConfigInit(&config); + config.lossless = 1; + } + assert(enc->curr_canvas_ == NULL); + enc->curr_canvas_ = frame; // Store reference. + assert(enc->curr_canvas_copy_modified_ == 1); + CopyCurrentCanvas(enc); + + ok = CacheFrame(enc, &config) && FlushFrames(enc); + + enc->curr_canvas_ = NULL; + enc->curr_canvas_copy_modified_ = 1; + if (ok) { + enc->prev_timestamp_ = timestamp; + } + return ok; +} + +// ----------------------------------------------------------------------------- +// Bitstream assembly. + +static int DecodeFrameOntoCanvas(const WebPMuxFrameInfo* const frame, + WebPPicture* const canvas) { + const WebPData* const image = &frame->bitstream; + WebPPicture sub_image; + WebPDecoderConfig config; + WebPInitDecoderConfig(&config); + WebPUtilClearPic(canvas, NULL); + if (WebPGetFeatures(image->bytes, image->size, &config.input) != + VP8_STATUS_OK) { + return 0; + } + if (!WebPPictureView(canvas, frame->x_offset, frame->y_offset, + config.input.width, config.input.height, &sub_image)) { + return 0; + } + config.output.is_external_memory = 1; + config.output.colorspace = MODE_BGRA; + config.output.u.RGBA.rgba = (uint8_t*)sub_image.argb; + config.output.u.RGBA.stride = sub_image.argb_stride * 4; + config.output.u.RGBA.size = config.output.u.RGBA.stride * sub_image.height; + + if (WebPDecode(image->bytes, image->size, &config) != VP8_STATUS_OK) { + return 0; + } + return 1; +} + +static int FrameToFullCanvas(WebPAnimEncoder* const enc, + const WebPMuxFrameInfo* const frame, + WebPData* const full_image) { + WebPPicture* const canvas_buf = &enc->curr_canvas_copy_; + WebPMemoryWriter mem1, mem2; + WebPMemoryWriterInit(&mem1); + WebPMemoryWriterInit(&mem2); + + if (!DecodeFrameOntoCanvas(frame, canvas_buf)) goto Err; + if (!EncodeFrame(&enc->last_config_, canvas_buf, &mem1)) goto Err; + GetEncodedData(&mem1, full_image); + + if (enc->options_.allow_mixed) { + if (!EncodeFrame(&enc->last_config_reversed_, canvas_buf, &mem2)) goto Err; + if (mem2.size < mem1.size) { + GetEncodedData(&mem2, full_image); + WebPMemoryWriterClear(&mem1); + } else { + WebPMemoryWriterClear(&mem2); + } + } + return 1; + + Err: + WebPMemoryWriterClear(&mem1); + WebPMemoryWriterClear(&mem2); + return 0; +} + +// Convert a single-frame animation to a non-animated image if appropriate. +// TODO(urvang): Can we pick one of the two heuristically (based on frame +// rectangle and/or presence of alpha)? +static WebPMuxError OptimizeSingleFrame(WebPAnimEncoder* const enc, + WebPData* const webp_data) { + WebPMuxError err = WEBP_MUX_OK; + int canvas_width, canvas_height; + WebPMuxFrameInfo frame; + WebPData full_image; + WebPData webp_data2; + WebPMux* const mux = WebPMuxCreate(webp_data, 0); + if (mux == NULL) return WEBP_MUX_BAD_DATA; + assert(enc->out_frame_count_ == 1); + WebPDataInit(&frame.bitstream); + WebPDataInit(&full_image); + WebPDataInit(&webp_data2); + + err = WebPMuxGetFrame(mux, 1, &frame); + if (err != WEBP_MUX_OK) goto End; + if (frame.id != WEBP_CHUNK_ANMF) goto End; // Non-animation: nothing to do. + err = WebPMuxGetCanvasSize(mux, &canvas_width, &canvas_height); + if (err != WEBP_MUX_OK) goto End; + if (!FrameToFullCanvas(enc, &frame, &full_image)) { + err = WEBP_MUX_BAD_DATA; + goto End; + } + err = WebPMuxSetImage(mux, &full_image, 1); + if (err != WEBP_MUX_OK) goto End; + err = WebPMuxAssemble(mux, &webp_data2); + if (err != WEBP_MUX_OK) goto End; + + if (webp_data2.size < webp_data->size) { // Pick 'webp_data2' if smaller. + WebPDataClear(webp_data); + *webp_data = webp_data2; + WebPDataInit(&webp_data2); + } + + End: + WebPDataClear(&frame.bitstream); + WebPDataClear(&full_image); + WebPMuxDelete(mux); + WebPDataClear(&webp_data2); + return err; +} + +int WebPAnimEncoderAssemble(WebPAnimEncoder* enc, WebPData* webp_data) { + WebPMux* mux; + WebPMuxError err; + + if (enc == NULL) { + return 0; + } + MarkNoError(enc); + + if (webp_data == NULL) { + MarkError(enc, "ERROR assembling: NULL input"); + return 0; + } + + if (enc->in_frame_count_ == 0) { + MarkError(enc, "ERROR: No frames to assemble"); + return 0; + } + + if (!enc->got_null_frame_ && enc->in_frame_count_ > 1 && enc->count_ > 0) { + // set duration of the last frame to be avg of durations of previous frames. + const double delta_time = + (uint32_t)enc->prev_timestamp_ - enc->first_timestamp_; + const int average_duration = (int)(delta_time / (enc->in_frame_count_ - 1)); + if (!IncreasePreviousDuration(enc, average_duration)) { + return 0; + } + } + + // Flush any remaining frames. + enc->flush_count_ = enc->count_; + if (!FlushFrames(enc)) { + return 0; + } + + // Set definitive canvas size. + mux = enc->mux_; + err = WebPMuxSetCanvasSize(mux, enc->canvas_width_, enc->canvas_height_); + if (err != WEBP_MUX_OK) goto Err; + + err = WebPMuxSetAnimationParams(mux, &enc->options_.anim_params); + if (err != WEBP_MUX_OK) goto Err; + + // Assemble into a WebP bitstream. + err = WebPMuxAssemble(mux, webp_data); + if (err != WEBP_MUX_OK) goto Err; + + if (enc->out_frame_count_ == 1) { + err = OptimizeSingleFrame(enc, webp_data); + if (err != WEBP_MUX_OK) goto Err; + } + return 1; + + Err: + MarkError2(enc, "ERROR assembling WebP", err); + return 0; +} + +const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc) { + if (enc == NULL) return NULL; + return enc->error_str_; +} + +// ----------------------------------------------------------------------------- diff --git a/internal/libwebp-1.2.1/src/mux/animi.h b/internal/libwebp-1.2.1/src/mux/animi.h new file mode 100644 index 0000000..34c45ba --- /dev/null +++ b/internal/libwebp-1.2.1/src/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 "src/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/internal/libwebp-1.2.1/src/mux/libwebpmux.pc.in b/internal/libwebp-1.2.1/src/mux/libwebpmux.pc.in new file mode 100644 index 0000000..a96fac7 --- /dev/null +++ b/internal/libwebp-1.2.1/src/mux/libwebpmux.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libwebpmux +Description: Library for manipulating the WebP graphics format container +Version: @PACKAGE_VERSION@ +Requires: libwebp >= 0.2.0 +Cflags: -I${includedir} +Libs: -L${libdir} -lwebpmux +Libs.private: -lm diff --git a/internal/libwebp-1.2.1/src/mux/libwebpmux.rc b/internal/libwebp-1.2.1/src/mux/libwebpmux.rc new file mode 100644 index 0000000..9f294d1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/mux/libwebpmux.rc @@ -0,0 +1,41 @@ +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,2,1 + PRODUCTVERSION 1,0,2,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Google, Inc." + VALUE "FileDescription", "libwebpmux DLL" + VALUE "FileVersion", "1.2.1" + VALUE "InternalName", "libwebpmux.dll" + VALUE "LegalCopyright", "Copyright (C) 2021" + VALUE "OriginalFilename", "libwebpmux.dll" + VALUE "ProductName", "WebP Image Muxer" + VALUE "ProductVersion", "1.2.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources diff --git a/internal/libwebp-1.2.1/src/mux/muxedit.c b/internal/libwebp-1.2.1/src/mux/muxedit.c new file mode 100644 index 0000000..02c3ede --- /dev/null +++ b/internal/libwebp-1.2.1/src/mux/muxedit.c @@ -0,0 +1,655 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Set and delete APIs for mux. +// +// Authors: Urvang (urvang@google.com) +// Vikas (vikasa@google.com) + +#include +#include "src/mux/muxi.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ +// Life of a mux object. + +static void MuxInit(WebPMux* const mux) { + assert(mux != NULL); + memset(mux, 0, sizeof(*mux)); + mux->canvas_width_ = 0; // just to be explicit + mux->canvas_height_ = 0; +} + +WebPMux* WebPNewInternal(int version) { + if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) { + return NULL; + } else { + WebPMux* const mux = (WebPMux*)WebPSafeMalloc(1ULL, sizeof(WebPMux)); + if (mux != NULL) MuxInit(mux); + return mux; + } +} + +// Delete all images in 'wpi_list'. +static void DeleteAllImages(WebPMuxImage** const wpi_list) { + while (*wpi_list != NULL) { + *wpi_list = MuxImageDelete(*wpi_list); + } +} + +static void MuxRelease(WebPMux* const mux) { + assert(mux != NULL); + DeleteAllImages(&mux->images_); + ChunkListDelete(&mux->vp8x_); + ChunkListDelete(&mux->iccp_); + ChunkListDelete(&mux->anim_); + ChunkListDelete(&mux->exif_); + ChunkListDelete(&mux->xmp_); + ChunkListDelete(&mux->unknown_); +} + +void WebPMuxDelete(WebPMux* mux) { + if (mux != NULL) { + MuxRelease(mux); + WebPSafeFree(mux); + } +} + +//------------------------------------------------------------------------------ +// Helper method(s). + +// Handy MACRO, makes MuxSet() very symmetric to MuxGet(). +#define SWITCH_ID_LIST(INDEX, LIST) \ + if (idx == (INDEX)) { \ + err = ChunkAssignData(&chunk, data, copy_data, tag); \ + if (err == WEBP_MUX_OK) { \ + err = ChunkSetHead(&chunk, (LIST)); \ + } \ + return err; \ + } + +static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, + const WebPData* const data, int copy_data) { + WebPChunk chunk; + WebPMuxError err = WEBP_MUX_NOT_FOUND; + const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag); + assert(mux != NULL); + assert(!IsWPI(kChunks[idx].id)); + + ChunkInit(&chunk); + SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x_); + SWITCH_ID_LIST(IDX_ICCP, &mux->iccp_); + SWITCH_ID_LIST(IDX_ANIM, &mux->anim_); + SWITCH_ID_LIST(IDX_EXIF, &mux->exif_); + SWITCH_ID_LIST(IDX_XMP, &mux->xmp_); + SWITCH_ID_LIST(IDX_UNKNOWN, &mux->unknown_); + return err; +} +#undef SWITCH_ID_LIST + +// 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_bytes = (uint8_t*)WebPSafeMalloc(1ULL, frame_size); + if (frame_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; + + PutLE24(frame_bytes + 0, info->x_offset / 2); + PutLE24(frame_bytes + 3, info->y_offset / 2); + + 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->bytes = frame_bytes; + frame->size = frame_size; + return WEBP_MUX_OK; +} + +// Outputs image data given a bitstream. The bitstream can either be a +// single-image WebP file or raw VP8/VP8L data. +// Also outputs 'is_lossless' to be true if the given bitstream is lossless. +static WebPMuxError GetImageData(const WebPData* const bitstream, + WebPData* const image, WebPData* const alpha, + int* const is_lossless) { + WebPDataInit(alpha); // Default: no alpha. + if (bitstream->size < TAG_SIZE || + memcmp(bitstream->bytes, "RIFF", TAG_SIZE)) { + // It is NOT webp file data. Return input data as is. + *image = *bitstream; + } else { + // It is webp file data. Extract image data from it. + const WebPMuxImage* wpi; + WebPMux* const mux = WebPMuxCreate(bitstream, 0); + if (mux == NULL) return WEBP_MUX_BAD_DATA; + wpi = mux->images_; + assert(wpi != NULL && wpi->img_ != NULL); + *image = wpi->img_->data_; + if (wpi->alpha_ != NULL) { + *alpha = wpi->alpha_->data_; + } + WebPMuxDelete(mux); + } + *is_lossless = VP8LCheckSignature(image->bytes, image->size); + return WEBP_MUX_OK; +} + +static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) { + WebPMuxError err = WEBP_MUX_NOT_FOUND; + assert(chunk_list); + while (*chunk_list) { + WebPChunk* const chunk = *chunk_list; + if (chunk->tag_ == tag) { + *chunk_list = ChunkDelete(chunk); + err = WEBP_MUX_OK; + } else { + chunk_list = &chunk->next_; + } + } + return err; +} + +static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux, uint32_t tag) { + const WebPChunkId id = ChunkGetIdFromTag(tag); + assert(mux != NULL); + if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT; + return DeleteChunks(MuxGetChunkListFromId(mux, id), tag); +} + +//------------------------------------------------------------------------------ +// Set API(s). + +WebPMuxError WebPMuxSetChunk(WebPMux* mux, const char fourcc[4], + const WebPData* chunk_data, int copy_data) { + uint32_t tag; + WebPMuxError err; + if (mux == NULL || fourcc == NULL || chunk_data == NULL || + chunk_data->bytes == NULL || chunk_data->size > MAX_CHUNK_PAYLOAD) { + return WEBP_MUX_INVALID_ARGUMENT; + } + tag = ChunkGetTagFromFourCC(fourcc); + + // Delete existing chunk(s) with the same 'fourcc'. + err = MuxDeleteAllNamedData(mux, tag); + if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; + + // Add the given chunk. + return MuxSet(mux, tag, chunk_data, copy_data); +} + +// Creates a chunk from given 'data' and sets it as 1st chunk in 'chunk_list'. +static WebPMuxError AddDataToChunkList( + const WebPData* const data, int copy_data, uint32_t tag, + WebPChunk** chunk_list) { + WebPChunk chunk; + WebPMuxError err; + ChunkInit(&chunk); + err = ChunkAssignData(&chunk, data, copy_data, tag); + if (err != WEBP_MUX_OK) goto Err; + err = ChunkSetHead(&chunk, chunk_list); + if (err != WEBP_MUX_OK) goto Err; + return WEBP_MUX_OK; + Err: + ChunkRelease(&chunk); + return err; +} + +// Extracts image & alpha data from the given bitstream and then sets wpi.alpha_ +// and wpi.img_ appropriately. +static WebPMuxError SetAlphaAndImageChunks( + const WebPData* const bitstream, int copy_data, WebPMuxImage* const wpi) { + int is_lossless = 0; + WebPData image, alpha; + WebPMuxError err = GetImageData(bitstream, &image, &alpha, &is_lossless); + const int image_tag = + is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag; + if (err != WEBP_MUX_OK) return err; + if (alpha.bytes != NULL) { + err = AddDataToChunkList(&alpha, copy_data, kChunks[IDX_ALPHA].tag, + &wpi->alpha_); + if (err != WEBP_MUX_OK) return err; + } + err = AddDataToChunkList(&image, copy_data, image_tag, &wpi->img_); + if (err != WEBP_MUX_OK) return err; + return MuxImageFinalize(wpi) ? WEBP_MUX_OK : WEBP_MUX_INVALID_ARGUMENT; +} + +WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream, + int copy_data) { + WebPMuxImage wpi; + WebPMuxError err; + + if (mux == NULL || bitstream == NULL || bitstream->bytes == NULL || + bitstream->size > MAX_CHUNK_PAYLOAD) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + if (mux->images_ != NULL) { + // Only one 'simple image' can be added in mux. So, remove present images. + DeleteAllImages(&mux->images_); + } + + MuxImageInit(&wpi); + err = SetAlphaAndImageChunks(bitstream, copy_data, &wpi); + if (err != WEBP_MUX_OK) goto Err; + + // Add this WebPMuxImage to mux. + err = MuxImagePush(&wpi, &mux->images_); + if (err != WEBP_MUX_OK) goto Err; + + // All is well. + return WEBP_MUX_OK; + + Err: // Something bad happened. + MuxImageRelease(&wpi); + return err; +} + +WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info, + int copy_data) { + WebPMuxImage wpi; + WebPMuxError err; + + if (mux == NULL || info == NULL) return WEBP_MUX_INVALID_ARGUMENT; + + if (info->id != WEBP_CHUNK_ANMF) return WEBP_MUX_INVALID_ARGUMENT; + + if (info->bitstream.bytes == NULL || + info->bitstream.size > MAX_CHUNK_PAYLOAD) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + if (mux->images_ != NULL) { + const WebPMuxImage* const image = mux->images_; + const uint32_t image_id = (image->header_ != NULL) ? + ChunkGetIdFromTag(image->header_->tag_) : WEBP_CHUNK_IMAGE; + if (image_id != info->id) { + return WEBP_MUX_INVALID_ARGUMENT; // Conflicting frame types. + } + } + + MuxImageInit(&wpi); + err = SetAlphaAndImageChunks(&info->bitstream, copy_data, &wpi); + if (err != WEBP_MUX_OK) goto Err; + assert(wpi.img_ != NULL); // As SetAlphaAndImageChunks() was successful. + + { + 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 (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) || + tmp.dispose_method != (tmp.dispose_method & 1)) { + err = WEBP_MUX_INVALID_ARGUMENT; + goto Err; + } + err = CreateFrameData(wpi.width_, wpi.height_, &tmp, &frame); + if (err != WEBP_MUX_OK) goto Err; + // 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; + } + + // Add this WebPMuxImage to mux. + err = MuxImagePush(&wpi, &mux->images_); + if (err != WEBP_MUX_OK) goto Err; + + // All is well. + return WEBP_MUX_OK; + + Err: // Something bad happened. + MuxImageRelease(&wpi); + return err; +} + +WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux, + const WebPMuxAnimParams* params) { + WebPMuxError err; + uint8_t data[ANIM_CHUNK_SIZE]; + const WebPData anim = { data, ANIM_CHUNK_SIZE }; + + if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT; + if (params->loop_count < 0 || params->loop_count >= MAX_LOOP_COUNT) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + // Delete any existing ANIM chunk(s). + err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag); + if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; + + // Set the animation parameters. + PutLE32(data, params->bgcolor); + PutLE16(data + 4, params->loop_count); + return MuxSet(mux, kChunks[IDX_ANIM].tag, &anim, 1); +} + +WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux, + int width, int height) { + WebPMuxError err; + if (mux == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + if (width < 0 || height < 0 || + width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) { + return WEBP_MUX_INVALID_ARGUMENT; + } + if (width * (uint64_t)height >= MAX_IMAGE_AREA) { + return WEBP_MUX_INVALID_ARGUMENT; + } + if ((width * height) == 0 && (width | height) != 0) { + // one of width / height is zero, but not both -> invalid! + return WEBP_MUX_INVALID_ARGUMENT; + } + // If we already assembled a VP8X chunk, invalidate it. + err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag); + if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; + + mux->canvas_width_ = width; + mux->canvas_height_ = height; + return WEBP_MUX_OK; +} + +//------------------------------------------------------------------------------ +// Delete API(s). + +WebPMuxError WebPMuxDeleteChunk(WebPMux* mux, const char fourcc[4]) { + if (mux == NULL || fourcc == NULL) return WEBP_MUX_INVALID_ARGUMENT; + return MuxDeleteAllNamedData(mux, ChunkGetTagFromFourCC(fourcc)); +} + +WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) { + if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; + return MuxImageDeleteNth(&mux->images_, nth); +} + +//------------------------------------------------------------------------------ +// Assembly of the WebP RIFF file. + +static WebPMuxError GetFrameInfo( + const WebPChunk* const frame_chunk, + int* const x_offset, int* const y_offset, int* const duration) { + 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); + *duration = GetLE24(data->bytes + 12); + return WEBP_MUX_OK; +} + +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_chunk = wpi->header_; + WebPMuxError err; + assert(wpi != NULL); + assert(frame_chunk != NULL); + + // 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. + if (width != NULL) *width = wpi->width_; + if (height != NULL) *height = wpi->height_; + return WEBP_MUX_OK; +} + +// Returns the tightest dimension for the canvas considering the image list. +static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux, + int* const width, int* const height) { + WebPMuxImage* wpi = NULL; + assert(mux != NULL); + assert(width != NULL && height != NULL); + + wpi = mux->images_; + assert(wpi != NULL); + assert(wpi->img_ != NULL); + + if (wpi->next_ != NULL) { + 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. + 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, + &duration, &w, &h); + const int max_x_pos = x_offset + w; + const int max_y_pos = y_offset + h; + if (err != WEBP_MUX_OK) return err; + assert(x_offset < MAX_POSITION_OFFSET); + assert(y_offset < MAX_POSITION_OFFSET); + + if (max_x_pos > max_x) max_x = max_x_pos; + if (max_y_pos > max_y) max_y = max_y_pos; + } + *width = max_x; + *height = max_y; + } else { + // For a single image, canvas dimensions are same as image dimensions. + *width = wpi->width_; + *height = wpi->height_; + } + return WEBP_MUX_OK; +} + +// VP8X format: +// Total Size : 10, +// Flags : 4 bytes, +// Width : 3 bytes, +// Height : 3 bytes. +static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { + WebPMuxError err = WEBP_MUX_OK; + uint32_t flags = 0; + int width = 0; + int height = 0; + uint8_t data[VP8X_CHUNK_SIZE]; + const WebPData vp8x = { data, VP8X_CHUNK_SIZE }; + const WebPMuxImage* images = NULL; + + assert(mux != NULL); + images = mux->images_; // First image. + if (images == NULL || images->img_ == NULL || + images->img_->data_.bytes == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + // If VP8X chunk(s) is(are) already present, remove them (and later add new + // VP8X chunk with updated flags). + err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag); + if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; + + // Set flags. + if (mux->iccp_ != NULL && mux->iccp_->data_.bytes != NULL) { + flags |= ICCP_FLAG; + } + if (mux->exif_ != NULL && mux->exif_->data_.bytes != NULL) { + flags |= EXIF_FLAG; + } + if (mux->xmp_ != NULL && mux->xmp_->data_.bytes != NULL) { + flags |= XMP_FLAG; + } + if (images->header_ != NULL) { + if (images->header_->tag_ == kChunks[IDX_ANMF].tag) { + // This is an image with animation. + flags |= ANIMATION_FLAG; + } + } + if (MuxImageCount(images, WEBP_CHUNK_ALPHA) > 0) { + flags |= ALPHA_FLAG; // Some images have an alpha channel. + } + + err = GetAdjustedCanvasSize(mux, &width, &height); + if (err != WEBP_MUX_OK) return err; + + if (width <= 0 || height <= 0) { + return WEBP_MUX_INVALID_ARGUMENT; + } + if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + if (mux->canvas_width_ != 0 || mux->canvas_height_ != 0) { + if (width > mux->canvas_width_ || height > mux->canvas_height_) { + return WEBP_MUX_INVALID_ARGUMENT; + } + width = mux->canvas_width_; + height = mux->canvas_height_; + } + + if (flags == 0 && mux->unknown_ == NULL) { + // For simple file format, VP8X chunk should not be added. + return WEBP_MUX_OK; + } + + if (MuxHasAlpha(images)) { + // This means some frames explicitly/implicitly contain alpha. + // Note: This 'flags' update must NOT be done for a lossless image + // without a VP8X chunk! + flags |= ALPHA_FLAG; + } + + PutLE32(data + 0, flags); // VP8X chunk flags. + PutLE24(data + 4, width - 1); // canvas width. + PutLE24(data + 7, height - 1); // canvas height. + + return MuxSet(mux, kChunks[IDX_VP8X].tag, &vp8x, 1); +} + +// Cleans up 'mux' by removing any unnecessary chunks. +static WebPMuxError MuxCleanup(WebPMux* const mux) { + int num_frames; + int num_anim_chunks; + + // 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; + 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->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; + } + } + // Remove ANIM chunk if this is a non-animated image. + err = WebPMuxNumChunks(mux, kChunks[IDX_ANIM].id, &num_anim_chunks); + if (err != WEBP_MUX_OK) return err; + if (num_anim_chunks >= 1 && num_frames == 0) { + err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag); + if (err != WEBP_MUX_OK) return err; + } + return WEBP_MUX_OK; +} + +// Total size of a list of images. +static size_t ImageListDiskSize(const WebPMuxImage* wpi_list) { + size_t size = 0; + while (wpi_list != NULL) { + size += MuxImageDiskSize(wpi_list); + wpi_list = wpi_list->next_; + } + return size; +} + +// Write out the given list of images into 'dst'. +static uint8_t* ImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) { + while (wpi_list != NULL) { + dst = MuxImageEmit(wpi_list, dst); + wpi_list = wpi_list->next_; + } + return dst; +} + +WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) { + size_t size = 0; + uint8_t* data = NULL; + uint8_t* dst = NULL; + WebPMuxError err; + + if (assembled_data == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + // Clean up returned data, in case something goes wrong. + memset(assembled_data, 0, sizeof(*assembled_data)); + + if (mux == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + // Finalize mux. + err = MuxCleanup(mux); + if (err != WEBP_MUX_OK) return err; + err = CreateVP8XChunk(mux); + if (err != WEBP_MUX_OK) return err; + + // Allocate data. + size = ChunkListDiskSize(mux->vp8x_) + ChunkListDiskSize(mux->iccp_) + + ChunkListDiskSize(mux->anim_) + ImageListDiskSize(mux->images_) + + ChunkListDiskSize(mux->exif_) + ChunkListDiskSize(mux->xmp_) + + ChunkListDiskSize(mux->unknown_) + RIFF_HEADER_SIZE; + + data = (uint8_t*)WebPSafeMalloc(1ULL, size); + if (data == NULL) return WEBP_MUX_MEMORY_ERROR; + + // Emit header & chunks. + dst = MuxEmitRiffHeader(data, size); + dst = ChunkListEmit(mux->vp8x_, dst); + dst = ChunkListEmit(mux->iccp_, dst); + dst = ChunkListEmit(mux->anim_, dst); + dst = ImageListEmit(mux->images_, dst); + dst = ChunkListEmit(mux->exif_, dst); + dst = ChunkListEmit(mux->xmp_, dst); + dst = ChunkListEmit(mux->unknown_, dst); + assert(dst == data + size); + + // Validate mux. + err = MuxValidate(mux); + if (err != WEBP_MUX_OK) { + WebPSafeFree(data); + data = NULL; + size = 0; + } + + // Finalize data. + assembled_data->bytes = data; + assembled_data->size = size; + + return err; +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/src/mux/muxi.h b/internal/libwebp-1.2.1/src/mux/muxi.h new file mode 100644 index 0000000..330da66 --- /dev/null +++ b/internal/libwebp-1.2.1/src/mux/muxi.h @@ -0,0 +1,234 @@ +// 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 for mux library. +// +// Author: Urvang (urvang@google.com) + +#ifndef WEBP_MUX_MUXI_H_ +#define WEBP_MUX_MUXI_H_ + +#include +#include +#include "src/dec/vp8i_dec.h" +#include "src/dec/vp8li_dec.h" +#include "src/webp/mux.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Defines and constants. + +#define MUX_MAJ_VERSION 1 +#define MUX_MIN_VERSION 2 +#define MUX_REV_VERSION 1 + +// Chunk object. +typedef struct WebPChunk WebPChunk; +struct WebPChunk { + uint32_t tag_; + int owner_; // True if *data_ memory is owned internally. + // VP8X, ANIM, and other internally created chunks + // like ANMF are always owned. + WebPData data_; + WebPChunk* next_; +}; + +// 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. + WebPChunk* alpha_; // Corresponds to WEBP_CHUNK_ALPHA. + WebPChunk* img_; // Corresponds to WEBP_CHUNK_IMAGE. + WebPChunk* unknown_; // Corresponds to WEBP_CHUNK_UNKNOWN. + int width_; + int height_; + int has_alpha_; // Through ALPH chunk or as part of VP8L. + int is_partial_; // True if only some of the chunks are filled. + WebPMuxImage* next_; +}; + +// Main mux object. Stores data chunks. +struct WebPMux { + WebPMuxImage* images_; + WebPChunk* iccp_; + WebPChunk* exif_; + WebPChunk* xmp_; + WebPChunk* anim_; + WebPChunk* vp8x_; + + WebPChunk* unknown_; + int canvas_width_; + int canvas_height_; +}; + +// CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only. +// Note: the reason for having two enums ('WebPChunkId' and 'CHUNK_INDEX') is to +// allow two different chunks to have the same id (e.g. WebPChunkId +// 'WEBP_CHUNK_IMAGE' can correspond to CHUNK_INDEX 'IDX_VP8' or 'IDX_VP8L'). +typedef enum { + IDX_VP8X = 0, + IDX_ICCP, + IDX_ANIM, + IDX_ANMF, + IDX_ALPHA, + IDX_VP8, + IDX_VP8L, + IDX_EXIF, + IDX_XMP, + IDX_UNKNOWN, + + IDX_NIL, + IDX_LAST_CHUNK +} CHUNK_INDEX; + +#define NIL_TAG 0x00000000u // To signal void chunk. + +typedef struct { + uint32_t tag; + WebPChunkId id; + uint32_t size; +} ChunkInfo; + +extern const ChunkInfo kChunks[IDX_LAST_CHUNK]; + +//------------------------------------------------------------------------------ +// Chunk object management. + +// Initialize. +void ChunkInit(WebPChunk* const chunk); + +// Get chunk index from chunk tag. Returns IDX_UNKNOWN if not found. +CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag); + +// Get chunk id from chunk tag. Returns WEBP_CHUNK_UNKNOWN if not found. +WebPChunkId ChunkGetIdFromTag(uint32_t tag); + +// Convert a fourcc string to a tag. +uint32_t ChunkGetTagFromFourCC(const char fourcc[4]); + +// Get chunk index from fourcc. Returns IDX_UNKNOWN if given fourcc is unknown. +CHUNK_INDEX ChunkGetIndexFromFourCC(const char fourcc[4]); + +// Search for nth chunk with given 'tag' in the chunk list. +// nth = 0 means "last of the list". +WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag); + +// Fill the chunk with the given data. +WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data, + int copy_data, uint32_t tag); + +// Sets 'chunk' as the only element in 'chunk_list' if it is empty. +// On success ownership is transferred from 'chunk' to the 'chunk_list'. +WebPMuxError ChunkSetHead(WebPChunk* const chunk, WebPChunk** const chunk_list); +// Sets 'chunk' at last position in the 'chunk_list'. +// On success ownership is transferred from 'chunk' to the 'chunk_list'. +// *chunk_list also points towards the last valid element of the initial +// *chunk_list. +WebPMuxError ChunkAppend(WebPChunk* const chunk, WebPChunk*** const chunk_list); + +// Releases chunk and returns chunk->next_. +WebPChunk* ChunkRelease(WebPChunk* const chunk); + +// Deletes given chunk & returns chunk->next_. +WebPChunk* ChunkDelete(WebPChunk* const chunk); + +// Deletes all chunks in the given chunk list. +void ChunkListDelete(WebPChunk** const chunk_list); + +// Returns size of the chunk including chunk header and padding byte (if any). +static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) { + assert(chunk_size <= MAX_CHUNK_PAYLOAD); + return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U); +} + +// Size of a chunk including header and padding. +static WEBP_INLINE size_t ChunkDiskSize(const WebPChunk* chunk) { + const size_t data_size = chunk->data_.size; + return SizeWithPadding(data_size); +} + +// Total size of a list of chunks. +size_t ChunkListDiskSize(const WebPChunk* chunk_list); + +// Write out the given list of chunks into 'dst'. +uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst); + +//------------------------------------------------------------------------------ +// MuxImage object management. + +// Initialize. +void MuxImageInit(WebPMuxImage* const wpi); + +// Releases image 'wpi' and returns wpi->next. +WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi); + +// Delete image 'wpi' and return the next image in the list or NULL. +// 'wpi' can be NULL. +WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi); + +// Count number of images matching the given tag id in the 'wpi_list'. +// If id == WEBP_CHUNK_NIL, all images will be matched. +int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id); + +// Update width/height/has_alpha info from chunks within wpi. +// Also remove ALPH chunk if not needed. +int MuxImageFinalize(WebPMuxImage* const wpi); + +// Check if given ID corresponds to an image related chunk. +static WEBP_INLINE int IsWPI(WebPChunkId id) { + switch (id) { + case WEBP_CHUNK_ANMF: + case WEBP_CHUNK_ALPHA: + case WEBP_CHUNK_IMAGE: return 1; + default: return 0; + } +} + +// Pushes 'wpi' at the end of 'wpi_list'. +WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list); + +// Delete nth image in the image list. +WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth); + +// Get nth image in the image list. +WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth, + WebPMuxImage** wpi); + +// Total size of the given image. +size_t MuxImageDiskSize(const WebPMuxImage* const wpi); + +// Write out the given image into 'dst'. +uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst); + +//------------------------------------------------------------------------------ +// Helper methods for mux. + +// Checks if the given image list contains at least one image with alpha. +int MuxHasAlpha(const WebPMuxImage* images); + +// Write out RIFF header into 'data', given total data size 'size'. +uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size); + +// Returns the list where chunk with given ID is to be inserted in mux. +WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id); + +// Validates the given mux object. +WebPMuxError MuxValidate(const WebPMux* const mux); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_MUX_MUXI_H_ diff --git a/internal/libwebp-1.2.1/src/mux/muxinternal.c b/internal/libwebp-1.2.1/src/mux/muxinternal.c new file mode 100644 index 0000000..b9ee671 --- /dev/null +++ b/internal/libwebp-1.2.1/src/mux/muxinternal.c @@ -0,0 +1,548 @@ +// 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 objects and utils for mux. +// +// Authors: Urvang (urvang@google.com) +// Vikas (vikasa@google.com) + +#include +#include "src/mux/muxi.h" +#include "src/utils/utils.h" + +#define UNDEFINED_CHUNK_SIZE ((uint32_t)(-1)) + +const ChunkInfo kChunks[] = { + { MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE }, + { 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('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 }, + { MKFOURCC('E', 'X', 'I', 'F'), WEBP_CHUNK_EXIF, UNDEFINED_CHUNK_SIZE }, + { MKFOURCC('X', 'M', 'P', ' '), WEBP_CHUNK_XMP, UNDEFINED_CHUNK_SIZE }, + { NIL_TAG, WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE }, + + { NIL_TAG, WEBP_CHUNK_NIL, UNDEFINED_CHUNK_SIZE } +}; + +//------------------------------------------------------------------------------ + +int WebPGetMuxVersion(void) { + return (MUX_MAJ_VERSION << 16) | (MUX_MIN_VERSION << 8) | MUX_REV_VERSION; +} + +//------------------------------------------------------------------------------ +// Life of a chunk object. + +void ChunkInit(WebPChunk* const chunk) { + assert(chunk); + memset(chunk, 0, sizeof(*chunk)); + chunk->tag_ = NIL_TAG; +} + +WebPChunk* ChunkRelease(WebPChunk* const chunk) { + WebPChunk* next; + if (chunk == NULL) return NULL; + if (chunk->owner_) { + WebPDataClear(&chunk->data_); + } + next = chunk->next_; + ChunkInit(chunk); + return next; +} + +//------------------------------------------------------------------------------ +// Chunk misc methods. + +CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag) { + int i; + for (i = 0; kChunks[i].tag != NIL_TAG; ++i) { + if (tag == kChunks[i].tag) return (CHUNK_INDEX)i; + } + return IDX_UNKNOWN; +} + +WebPChunkId ChunkGetIdFromTag(uint32_t tag) { + int i; + for (i = 0; kChunks[i].tag != NIL_TAG; ++i) { + if (tag == kChunks[i].tag) return kChunks[i].id; + } + return WEBP_CHUNK_UNKNOWN; +} + +uint32_t ChunkGetTagFromFourCC(const char fourcc[4]) { + return MKFOURCC(fourcc[0], fourcc[1], fourcc[2], fourcc[3]); +} + +CHUNK_INDEX ChunkGetIndexFromFourCC(const char fourcc[4]) { + const uint32_t tag = ChunkGetTagFromFourCC(fourcc); + return ChunkGetIndexFromTag(tag); +} + +//------------------------------------------------------------------------------ +// Chunk search methods. + +// Returns next chunk in the chunk list with the given tag. +static WebPChunk* ChunkSearchNextInList(WebPChunk* chunk, uint32_t tag) { + while (chunk != NULL && chunk->tag_ != tag) { + chunk = chunk->next_; + } + return chunk; +} + +WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) { + uint32_t iter = nth; + first = ChunkSearchNextInList(first, tag); + if (first == NULL) return NULL; + + while (--iter != 0) { + WebPChunk* next_chunk = ChunkSearchNextInList(first->next_, tag); + if (next_chunk == NULL) break; + first = next_chunk; + } + return ((nth > 0) && (iter > 0)) ? NULL : first; +} + +//------------------------------------------------------------------------------ +// Chunk writer methods. + +WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data, + int copy_data, uint32_t tag) { + // For internally allocated chunks, always copy data & make it owner of data. + if (tag == kChunks[IDX_VP8X].tag || tag == kChunks[IDX_ANIM].tag) { + copy_data = 1; + } + + ChunkRelease(chunk); + + if (data != NULL) { + if (copy_data) { // Copy data. + if (!WebPDataCopy(data, &chunk->data_)) return WEBP_MUX_MEMORY_ERROR; + chunk->owner_ = 1; // Chunk is owner of data. + } else { // Don't copy data. + chunk->data_ = *data; + } + } + chunk->tag_ = tag; + return WEBP_MUX_OK; +} + +WebPMuxError ChunkSetHead(WebPChunk* const chunk, + WebPChunk** const chunk_list) { + WebPChunk* new_chunk; + + assert(chunk_list != NULL); + if (*chunk_list != NULL) { + return WEBP_MUX_NOT_FOUND; + } + + new_chunk = (WebPChunk*)WebPSafeMalloc(1ULL, sizeof(*new_chunk)); + if (new_chunk == NULL) return WEBP_MUX_MEMORY_ERROR; + *new_chunk = *chunk; + chunk->owner_ = 0; + new_chunk->next_ = NULL; + *chunk_list = new_chunk; + return WEBP_MUX_OK; +} + +WebPMuxError ChunkAppend(WebPChunk* const chunk, + WebPChunk*** const chunk_list) { + assert(chunk_list != NULL && *chunk_list != NULL); + + if (**chunk_list == NULL) { + ChunkSetHead(chunk, *chunk_list); + } else { + WebPChunk* last_chunk = **chunk_list; + while (last_chunk->next_ != NULL) last_chunk = last_chunk->next_; + ChunkSetHead(chunk, &last_chunk->next_); + *chunk_list = &last_chunk->next_; + } + return WEBP_MUX_OK; +} + +//------------------------------------------------------------------------------ +// Chunk deletion method(s). + +WebPChunk* ChunkDelete(WebPChunk* const chunk) { + WebPChunk* const next = ChunkRelease(chunk); + WebPSafeFree(chunk); + return next; +} + +void ChunkListDelete(WebPChunk** const chunk_list) { + while (*chunk_list != NULL) { + *chunk_list = ChunkDelete(*chunk_list); + } +} + +//------------------------------------------------------------------------------ +// Chunk serialization methods. + +static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) { + const size_t chunk_size = chunk->data_.size; + assert(chunk); + assert(chunk->tag_ != NIL_TAG); + PutLE32(dst + 0, chunk->tag_); + PutLE32(dst + TAG_SIZE, (uint32_t)chunk_size); + assert(chunk_size == (uint32_t)chunk_size); + memcpy(dst + CHUNK_HEADER_SIZE, chunk->data_.bytes, chunk_size); + if (chunk_size & 1) + dst[CHUNK_HEADER_SIZE + chunk_size] = 0; // Add padding. + return dst + ChunkDiskSize(chunk); +} + +uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) { + while (chunk_list != NULL) { + dst = ChunkEmit(chunk_list, dst); + chunk_list = chunk_list->next_; + } + return dst; +} + +size_t ChunkListDiskSize(const WebPChunk* chunk_list) { + size_t size = 0; + while (chunk_list != NULL) { + size += ChunkDiskSize(chunk_list); + chunk_list = chunk_list->next_; + } + return size; +} + +//------------------------------------------------------------------------------ +// Life of a MuxImage object. + +void MuxImageInit(WebPMuxImage* const wpi) { + assert(wpi); + memset(wpi, 0, sizeof(*wpi)); +} + +WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) { + WebPMuxImage* next; + if (wpi == NULL) return NULL; + // There should be at most one chunk of header_, alpha_, img_ but we call + // ChunkListDelete to be safe + ChunkListDelete(&wpi->header_); + ChunkListDelete(&wpi->alpha_); + ChunkListDelete(&wpi->img_); + ChunkListDelete(&wpi->unknown_); + + next = wpi->next_; + MuxImageInit(wpi); + return next; +} + +//------------------------------------------------------------------------------ +// MuxImage search methods. + +// Get a reference to appropriate chunk list within an image given chunk tag. +static WebPChunk** GetChunkListFromId(const WebPMuxImage* const wpi, + WebPChunkId id) { + assert(wpi != NULL); + switch (id) { + 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; + } +} + +int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) { + int count = 0; + const WebPMuxImage* current; + for (current = wpi_list; current != NULL; current = current->next_) { + if (id == WEBP_CHUNK_NIL) { + ++count; // Special case: count all images. + } else { + const WebPChunk* const wpi_chunk = *GetChunkListFromId(current, id); + if (wpi_chunk != NULL) { + const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_); + if (wpi_chunk_id == id) ++count; // Count images with a matching 'id'. + } + } + } + return count; +} + +// Outputs a pointer to 'prev_wpi->next_', +// where 'prev_wpi' is the pointer to the image at position (nth - 1). +// Returns true if nth image was found. +static int SearchImageToGetOrDelete(WebPMuxImage** wpi_list, uint32_t nth, + WebPMuxImage*** const location) { + uint32_t count = 0; + assert(wpi_list); + *location = wpi_list; + + if (nth == 0) { + nth = MuxImageCount(*wpi_list, WEBP_CHUNK_NIL); + if (nth == 0) return 0; // Not found. + } + + while (*wpi_list != NULL) { + WebPMuxImage* const cur_wpi = *wpi_list; + ++count; + if (count == nth) return 1; // Found. + wpi_list = &cur_wpi->next_; + *location = wpi_list; + } + return 0; // Not found. +} + +//------------------------------------------------------------------------------ +// MuxImage writer methods. + +WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list) { + WebPMuxImage* new_wpi; + + while (*wpi_list != NULL) { + WebPMuxImage* const cur_wpi = *wpi_list; + if (cur_wpi->next_ == NULL) break; + wpi_list = &cur_wpi->next_; + } + + new_wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*new_wpi)); + if (new_wpi == NULL) return WEBP_MUX_MEMORY_ERROR; + *new_wpi = *wpi; + new_wpi->next_ = NULL; + + if (*wpi_list != NULL) { + (*wpi_list)->next_ = new_wpi; + } else { + *wpi_list = new_wpi; + } + return WEBP_MUX_OK; +} + +//------------------------------------------------------------------------------ +// MuxImage deletion methods. + +WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi) { + // Delete the components of wpi. If wpi is NULL this is a noop. + WebPMuxImage* const next = MuxImageRelease(wpi); + WebPSafeFree(wpi); + return next; +} + +WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth) { + assert(wpi_list); + if (!SearchImageToGetOrDelete(wpi_list, nth, &wpi_list)) { + return WEBP_MUX_NOT_FOUND; + } + *wpi_list = MuxImageDelete(*wpi_list); + return WEBP_MUX_OK; +} + +//------------------------------------------------------------------------------ +// MuxImage reader methods. + +WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth, + WebPMuxImage** wpi) { + assert(wpi_list); + assert(wpi); + if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth, + (WebPMuxImage***)&wpi_list)) { + return WEBP_MUX_NOT_FOUND; + } + *wpi = (WebPMuxImage*)*wpi_list; + return WEBP_MUX_OK; +} + +//------------------------------------------------------------------------------ +// MuxImage serialization methods. + +// Size of an image. +size_t MuxImageDiskSize(const WebPMuxImage* const wpi) { + size_t size = 0; + if (wpi->header_ != NULL) size += ChunkDiskSize(wpi->header_); + if (wpi->alpha_ != NULL) size += ChunkDiskSize(wpi->alpha_); + if (wpi->img_ != NULL) size += ChunkDiskSize(wpi->img_); + if (wpi->unknown_ != NULL) size += ChunkListDiskSize(wpi->unknown_); + return size; +} + +// 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); + PutLE32(dst + 0, header->tag_); + PutLE32(dst + TAG_SIZE, (uint32_t)offset_to_next); + assert(header_size == (uint32_t)header_size); + memcpy(dst + CHUNK_HEADER_SIZE, header->data_.bytes, header_size); + if (header_size & 1) { + dst[CHUNK_HEADER_SIZE + header_size] = 0; // Add padding. + } + return dst + ChunkDiskSize(header); +} + +uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) { + // Ordering of chunks to be emitted is strictly as follows: + // 1. ANMF chunk (if present). + // 2. ALPH chunk (if present). + // 3. VP8/VP8L chunk. + assert(wpi); + if (wpi->header_ != NULL) { + dst = ChunkEmitSpecial(wpi->header_, MuxImageDiskSize(wpi), dst); + } + if (wpi->alpha_ != NULL) dst = ChunkEmit(wpi->alpha_, dst); + if (wpi->img_ != NULL) dst = ChunkEmit(wpi->img_, dst); + if (wpi->unknown_ != NULL) dst = ChunkListEmit(wpi->unknown_, dst); + return dst; +} + +//------------------------------------------------------------------------------ +// Helper methods for mux. + +int MuxHasAlpha(const WebPMuxImage* images) { + while (images != NULL) { + if (images->has_alpha_) return 1; + images = images->next_; + } + return 0; +} + +uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size) { + PutLE32(data + 0, MKFOURCC('R', 'I', 'F', 'F')); + PutLE32(data + TAG_SIZE, (uint32_t)size - CHUNK_HEADER_SIZE); + assert(size == (uint32_t)size); + PutLE32(data + TAG_SIZE + CHUNK_SIZE_BYTES, MKFOURCC('W', 'E', 'B', 'P')); + return data + RIFF_HEADER_SIZE; +} + +WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id) { + assert(mux != NULL); + switch (id) { + case WEBP_CHUNK_VP8X: return (WebPChunk**)&mux->vp8x_; + case WEBP_CHUNK_ICCP: return (WebPChunk**)&mux->iccp_; + case WEBP_CHUNK_ANIM: return (WebPChunk**)&mux->anim_; + case WEBP_CHUNK_EXIF: return (WebPChunk**)&mux->exif_; + case WEBP_CHUNK_XMP: return (WebPChunk**)&mux->xmp_; + default: return (WebPChunk**)&mux->unknown_; + } +} + +static int IsNotCompatible(int feature, int num_items) { + return (feature != 0) != (num_items > 0); +} + +#define NO_FLAG ((WebPFeatureFlags)0) + +// Test basic constraints: +// retrieval, maximum number of chunks by index (use -1 to skip) +// and feature incompatibility (use NO_FLAG to skip). +// On success returns WEBP_MUX_OK and stores the chunk count in *num. +static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx, + WebPFeatureFlags feature, + uint32_t vp8x_flags, + int max, int* num) { + const WebPMuxError err = + WebPMuxNumChunks(mux, kChunks[idx].id, num); + if (err != WEBP_MUX_OK) return err; + if (max > -1 && *num > max) return WEBP_MUX_INVALID_ARGUMENT; + if (feature != NO_FLAG && IsNotCompatible(vp8x_flags & feature, *num)) { + return WEBP_MUX_INVALID_ARGUMENT; + } + return WEBP_MUX_OK; +} + +WebPMuxError MuxValidate(const WebPMux* const mux) { + int num_iccp; + int num_exif; + int num_xmp; + int num_anim; + int num_frames; + int num_vp8x; + int num_images; + int num_alpha; + uint32_t flags; + WebPMuxError err; + + // Verify mux is not NULL. + if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; + + // Verify mux has at least one image. + if (mux->images_ == NULL) return WEBP_MUX_INVALID_ARGUMENT; + + err = WebPMuxGetFeatures(mux, &flags); + if (err != WEBP_MUX_OK) return err; + + // At most one color profile chunk. + err = ValidateChunk(mux, IDX_ICCP, ICCP_FLAG, flags, 1, &num_iccp); + if (err != WEBP_MUX_OK) return err; + + // At most one EXIF metadata. + err = ValidateChunk(mux, IDX_EXIF, EXIF_FLAG, flags, 1, &num_exif); + if (err != WEBP_MUX_OK) return err; + + // At most one XMP metadata. + err = ValidateChunk(mux, IDX_XMP, XMP_FLAG, flags, 1, &num_xmp); + if (err != WEBP_MUX_OK) return err; + + // Animation: ANIMATION_FLAG, ANIM chunk and ANMF chunk(s) are consistent. + // At most one ANIM chunk. + err = ValidateChunk(mux, IDX_ANIM, NO_FLAG, flags, 1, &num_anim); + if (err != WEBP_MUX_OK) return err; + err = ValidateChunk(mux, IDX_ANMF, NO_FLAG, flags, -1, &num_frames); + if (err != WEBP_MUX_OK) return err; + + { + const int has_animation = !!(flags & ANIMATION_FLAG); + if (has_animation && (num_anim == 0 || num_frames == 0)) { + return WEBP_MUX_INVALID_ARGUMENT; + } + if (!has_animation && (num_anim == 1 || num_frames > 0)) { + return WEBP_MUX_INVALID_ARGUMENT; + } + if (!has_animation) { + const WebPMuxImage* images = mux->images_; + // There can be only one image. + if (images == NULL || images->next_ != NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + // Size must match. + if (mux->canvas_width_ > 0) { + if (images->width_ != mux->canvas_width_ || + images->height_ != mux->canvas_height_) { + return WEBP_MUX_INVALID_ARGUMENT; + } + } + } + } + + // 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); + if (err != WEBP_MUX_OK) return err; + err = ValidateChunk(mux, IDX_VP8, NO_FLAG, flags, -1, &num_images); + if (err != WEBP_MUX_OK) return err; + if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT; + + // ALPHA_FLAG & alpha chunk(s) are consistent. + // Note: ALPHA_FLAG can be set when there is actually no Alpha data present. + if (MuxHasAlpha(mux->images_)) { + if (num_vp8x > 0) { + // VP8X chunk is present, so it should contain ALPHA_FLAG. + if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT; + } else { + // VP8X chunk is not present, so ALPH chunks should NOT be present either. + err = WebPMuxNumChunks(mux, WEBP_CHUNK_ALPHA, &num_alpha); + if (err != WEBP_MUX_OK) return err; + if (num_alpha > 0) return WEBP_MUX_INVALID_ARGUMENT; + } + } + + return WEBP_MUX_OK; +} + +#undef NO_FLAG + +//------------------------------------------------------------------------------ + diff --git a/internal/libwebp-1.2.1/src/mux/muxread.c b/internal/libwebp-1.2.1/src/mux/muxread.c new file mode 100644 index 0000000..8005039 --- /dev/null +++ b/internal/libwebp-1.2.1/src/mux/muxread.c @@ -0,0 +1,554 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Read APIs for mux. +// +// Authors: Urvang (urvang@google.com) +// Vikas (vikasa@google.com) + +#include +#include "src/mux/muxi.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ +// Helper method(s). + +// Handy MACRO. +#define SWITCH_ID_LIST(INDEX, LIST) \ + if (idx == (INDEX)) { \ + const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \ + kChunks[(INDEX)].tag); \ + if (chunk) { \ + *data = chunk->data_; \ + return WEBP_MUX_OK; \ + } else { \ + return WEBP_MUX_NOT_FOUND; \ + } \ + } + +static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx, + uint32_t nth, WebPData* const data) { + assert(mux != NULL); + assert(!IsWPI(kChunks[idx].id)); + WebPDataInit(data); + + SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_); + SWITCH_ID_LIST(IDX_ICCP, mux->iccp_); + SWITCH_ID_LIST(IDX_ANIM, mux->anim_); + SWITCH_ID_LIST(IDX_EXIF, mux->exif_); + SWITCH_ID_LIST(IDX_XMP, mux->xmp_); + assert(idx != IDX_UNKNOWN); + return WEBP_MUX_NOT_FOUND; +} +#undef SWITCH_ID_LIST + +// Fill the chunk with the given data (includes chunk header bytes), after some +// verifications. +static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk, + const uint8_t* data, size_t data_size, + size_t riff_size, int copy_data) { + uint32_t chunk_size; + WebPData chunk_data; + + // Correctness checks. + if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA; + chunk_size = GetLE32(data + TAG_SIZE); + if (chunk_size > MAX_CHUNK_PAYLOAD) return WEBP_MUX_BAD_DATA; + + { + const size_t chunk_disk_size = SizeWithPadding(chunk_size); + if (chunk_disk_size > riff_size) return WEBP_MUX_BAD_DATA; + if (chunk_disk_size > data_size) return WEBP_MUX_NOT_ENOUGH_DATA; + } + + // Data assignment. + chunk_data.bytes = data + CHUNK_HEADER_SIZE; + chunk_data.size = chunk_size; + return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0)); +} + +int MuxImageFinalize(WebPMuxImage* const wpi) { + const WebPChunk* const img = wpi->img_; + const WebPData* const image = &img->data_; + const int is_lossless = (img->tag_ == kChunks[IDX_VP8L].tag); + int w, h; + int vp8l_has_alpha = 0; + const int ok = is_lossless ? + VP8LGetInfo(image->bytes, image->size, &w, &h, &vp8l_has_alpha) : + VP8GetInfo(image->bytes, image->size, image->size, &w, &h); + assert(img != NULL); + if (ok) { + // Ignore ALPH chunk accompanying VP8L. + if (is_lossless && (wpi->alpha_ != NULL)) { + ChunkDelete(wpi->alpha_); + wpi->alpha_ = NULL; + } + wpi->width_ = w; + wpi->height_ = h; + wpi->has_alpha_ = vp8l_has_alpha || (wpi->alpha_ != NULL); + } + return ok; +} + +static int MuxImageParse(const WebPChunk* const chunk, int copy_data, + WebPMuxImage* const wpi) { + const uint8_t* bytes = chunk->data_.bytes; + size_t size = chunk->data_.size; + const uint8_t* const last = (bytes == NULL) ? NULL : bytes + size; + WebPChunk subchunk; + size_t subchunk_size; + WebPChunk** unknown_chunk_list = &wpi->unknown_; + ChunkInit(&subchunk); + + assert(chunk->tag_ == kChunks[IDX_ANMF].tag); + assert(!wpi->is_partial_); + + // ANMF. + { + const size_t hdr_size = ANMF_CHUNK_SIZE; + const WebPData temp = { bytes, 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_); + } + ChunkSetHead(&subchunk, &wpi->header_); + wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks. + + // Rest of the chunks. + subchunk_size = ChunkDiskSize(&subchunk) - CHUNK_HEADER_SIZE; + bytes += subchunk_size; + size -= subchunk_size; + + while (bytes != last) { + ChunkInit(&subchunk); + if (ChunkVerifyAndAssign(&subchunk, bytes, size, size, + copy_data) != WEBP_MUX_OK) { + goto Fail; + } + switch (ChunkGetIdFromTag(subchunk.tag_)) { + case WEBP_CHUNK_ALPHA: + if (wpi->alpha_ != NULL) goto Fail; // Consecutive ALPH chunks. + if (ChunkSetHead(&subchunk, &wpi->alpha_) != WEBP_MUX_OK) goto Fail; + wpi->is_partial_ = 1; // Waiting for a VP8 chunk. + break; + case WEBP_CHUNK_IMAGE: + if (wpi->img_ != NULL) goto Fail; // Only 1 image chunk allowed. + if (ChunkSetHead(&subchunk, &wpi->img_) != WEBP_MUX_OK) goto Fail; + if (!MuxImageFinalize(wpi)) goto Fail; + wpi->is_partial_ = 0; // wpi is completely filled. + break; + case WEBP_CHUNK_UNKNOWN: + if (wpi->is_partial_) { + goto Fail; // Encountered an unknown chunk + // before some image chunks. + } + if (ChunkAppend(&subchunk, &unknown_chunk_list) != WEBP_MUX_OK) { + goto Fail; + } + break; + default: + goto Fail; + } + subchunk_size = ChunkDiskSize(&subchunk); + bytes += subchunk_size; + size -= subchunk_size; + } + if (wpi->is_partial_) goto Fail; + return 1; + + Fail: + ChunkRelease(&subchunk); + return 0; +} + +//------------------------------------------------------------------------------ +// Create a mux object from WebP-RIFF data. + +WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, + int version) { + size_t riff_size; + uint32_t tag; + const uint8_t* end; + WebPMux* mux = NULL; + WebPMuxImage* wpi = NULL; + const uint8_t* data; + size_t size; + WebPChunk chunk; + // Stores the end of the chunk lists so that it is faster to append data to + // their ends. + WebPChunk** chunk_list_ends[WEBP_CHUNK_NIL + 1] = { NULL }; + ChunkInit(&chunk); + + if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) { + return NULL; // version mismatch + } + if (bitstream == NULL) return NULL; + + data = bitstream->bytes; + size = bitstream->size; + + if (data == NULL) return NULL; + if (size < RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE) return NULL; + if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') || + GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) { + return NULL; + } + + mux = WebPMuxNew(); + if (mux == NULL) return NULL; + + tag = GetLE32(data + RIFF_HEADER_SIZE); + if (tag != kChunks[IDX_VP8].tag && + tag != kChunks[IDX_VP8L].tag && + tag != kChunks[IDX_VP8X].tag) { + goto Err; // First chunk should be VP8, VP8L or VP8X. + } + + riff_size = GetLE32(data + TAG_SIZE); + if (riff_size > MAX_CHUNK_PAYLOAD) goto Err; + + // Note this padding is historical and differs from demux.c which does not + // pad the file size. + riff_size = SizeWithPadding(riff_size); + if (riff_size < CHUNK_HEADER_SIZE) goto Err; + if (riff_size > size) goto Err; + // There's no point in reading past the end of the RIFF chunk. + if (size > riff_size + CHUNK_HEADER_SIZE) { + size = riff_size + CHUNK_HEADER_SIZE; + } + + end = data + size; + data += RIFF_HEADER_SIZE; + size -= RIFF_HEADER_SIZE; + + wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*wpi)); + if (wpi == NULL) goto Err; + MuxImageInit(wpi); + + // Loop over chunks. + while (data != end) { + size_t data_size; + WebPChunkId id; + if (ChunkVerifyAndAssign(&chunk, data, size, riff_size, + copy_data) != WEBP_MUX_OK) { + goto Err; + } + data_size = ChunkDiskSize(&chunk); + id = ChunkGetIdFromTag(chunk.tag_); + switch (id) { + case WEBP_CHUNK_ALPHA: + if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks. + if (ChunkSetHead(&chunk, &wpi->alpha_) != WEBP_MUX_OK) goto Err; + wpi->is_partial_ = 1; // Waiting for a VP8 chunk. + break; + case WEBP_CHUNK_IMAGE: + if (ChunkSetHead(&chunk, &wpi->img_) != WEBP_MUX_OK) goto Err; + if (!MuxImageFinalize(wpi)) goto Err; + wpi->is_partial_ = 0; // wpi is completely filled. + PushImage: + // Add this to mux->images_ list. + if (MuxImagePush(wpi, &mux->images_) != WEBP_MUX_OK) goto Err; + MuxImageInit(wpi); // Reset for reading next image. + break; + case WEBP_CHUNK_ANMF: + if (wpi->is_partial_) goto Err; // Previous wpi is still incomplete. + if (!MuxImageParse(&chunk, copy_data, wpi)) goto Err; + ChunkRelease(&chunk); + goto PushImage; + default: // A non-image chunk. + if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before + // getting all chunks of an image. + if (chunk_list_ends[id] == NULL) { + chunk_list_ends[id] = + MuxGetChunkListFromId(mux, id); // List to add this chunk. + } + if (ChunkAppend(&chunk, &chunk_list_ends[id]) != WEBP_MUX_OK) goto Err; + if (id == WEBP_CHUNK_VP8X) { // grab global specs + if (data_size < CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE) goto Err; + mux->canvas_width_ = GetLE24(data + 12) + 1; + mux->canvas_height_ = GetLE24(data + 15) + 1; + } + break; + } + data += data_size; + size -= data_size; + ChunkInit(&chunk); + } + + // Incomplete image. + if (wpi->is_partial_) goto Err; + + // Validate mux if complete. + if (MuxValidate(mux) != WEBP_MUX_OK) goto Err; + + MuxImageDelete(wpi); + return mux; // All OK; + + Err: // Something bad happened. + ChunkRelease(&chunk); + MuxImageDelete(wpi); + WebPMuxDelete(mux); + return NULL; +} + +//------------------------------------------------------------------------------ +// Get API(s). + +// Validates that the given mux has a single image. +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); + + if (num_images == 0) { + // No images in mux. + return WEBP_MUX_NOT_FOUND; + } else if (num_images == 1 && num_frames == 0) { + // Valid case (single image). + return WEBP_MUX_OK; + } else { + // Frame case OR an invalid mux. + return WEBP_MUX_INVALID_ARGUMENT; + } +} + +// Get the canvas width, height and flags after validating that VP8X/VP8/VP8L +// chunk and canvas size are valid. +static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux, + int* width, int* height, uint32_t* flags) { + int w, h; + uint32_t f = 0; + WebPData data; + assert(mux != NULL); + + // Check if VP8X chunk is present. + if (MuxGet(mux, IDX_VP8X, 1, &data) == WEBP_MUX_OK) { + if (data.size < VP8X_CHUNK_SIZE) return WEBP_MUX_BAD_DATA; + f = GetLE32(data.bytes + 0); + w = GetLE24(data.bytes + 4) + 1; + h = GetLE24(data.bytes + 7) + 1; + } else { + const WebPMuxImage* const wpi = mux->images_; + // Grab user-forced canvas size as default. + w = mux->canvas_width_; + h = mux->canvas_height_; + if (w == 0 && h == 0 && ValidateForSingleImage(mux) == WEBP_MUX_OK) { + // single image and not forced canvas size => use dimension of first frame + assert(wpi != NULL); + w = wpi->width_; + h = wpi->height_; + } + if (wpi != NULL) { + if (wpi->has_alpha_) f |= ALPHA_FLAG; + } + } + if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA; + + if (width != NULL) *width = w; + if (height != NULL) *height = h; + if (flags != NULL) *flags = f; + return WEBP_MUX_OK; +} + +WebPMuxError WebPMuxGetCanvasSize(const WebPMux* mux, int* width, int* height) { + if (mux == NULL || width == NULL || height == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + return MuxGetCanvasInfo(mux, width, height, NULL); +} + +WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) { + if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT; + return MuxGetCanvasInfo(mux, NULL, NULL, flags); +} + +static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width, + int height, uint32_t flags) { + const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; + assert(width >= 1 && height >= 1); + assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE); + assert(width * (uint64_t)height < MAX_IMAGE_AREA); + PutLE32(dst, MKFOURCC('V', 'P', '8', 'X')); + PutLE32(dst + TAG_SIZE, VP8X_CHUNK_SIZE); + PutLE32(dst + CHUNK_HEADER_SIZE, flags); + PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1); + PutLE24(dst + CHUNK_HEADER_SIZE + 7, height - 1); + return dst + vp8x_size; +} + +// Assemble a single image WebP bitstream from 'wpi'. +static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi, + WebPData* const bitstream) { + uint8_t* dst; + + // Allocate data. + 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 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); + if (data == NULL) return WEBP_MUX_MEMORY_ERROR; + + // There should be at most one alpha_ chunk and exactly one img_ chunk. + assert(wpi->alpha_ == NULL || wpi->alpha_->next_ == NULL); + assert(wpi->img_ != NULL && wpi->img_->next_ == NULL); + + // Main RIFF header. + dst = MuxEmitRiffHeader(data, size); + + if (need_vp8x) { + dst = EmitVP8XChunk(dst, wpi->width_, wpi->height_, ALPHA_FLAG); // VP8X. + dst = ChunkListEmit(wpi->alpha_, dst); // ALPH. + } + + // Bitstream. + dst = ChunkListEmit(wpi->img_, dst); + assert(dst == data + size); + + // Output. + bitstream->bytes = data; + bitstream->size = size; + return WEBP_MUX_OK; +} + +WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4], + WebPData* chunk_data) { + CHUNK_INDEX idx; + if (mux == NULL || fourcc == NULL || chunk_data == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + idx = ChunkGetIndexFromFourCC(fourcc); + if (IsWPI(kChunks[idx].id)) { // An image chunk. + return WEBP_MUX_INVALID_ARGUMENT; + } else if (idx != IDX_UNKNOWN) { // A known chunk type. + return MuxGet(mux, idx, 1, chunk_data); + } else { // An unknown chunk type. + const WebPChunk* const chunk = + ChunkSearchList(mux->unknown_, 1, ChunkGetTagFromFourCC(fourcc)); + if (chunk == NULL) return WEBP_MUX_NOT_FOUND; + *chunk_data = chunk->data_; + return WEBP_MUX_OK; + } +} + +static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi, + WebPMuxFrameInfo* const info) { + // Set some defaults for unrelated fields. + info->x_offset = 0; + info->y_offset = 0; + info->duration = 1; + info->dispose_method = WEBP_MUX_DISPOSE_NONE; + info->blend_method = WEBP_MUX_BLEND; + // Extract data for related fields. + info->id = ChunkGetIdFromTag(wpi->img_->tag_); + return SynthesizeBitstream(wpi, &info->bitstream); +} + +static WebPMuxError MuxGetFrameInternal(const WebPMuxImage* const wpi, + WebPMuxFrameInfo* const frame) { + const int is_frame = (wpi->header_->tag_ == kChunks[IDX_ANMF].tag); + const WebPData* frame_data; + if (!is_frame) return WEBP_MUX_INVALID_ARGUMENT; + assert(wpi->header_ != NULL); // Already checked by WebPMuxGetFrame(). + // 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_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; + } + frame->id = ChunkGetIdFromTag(wpi->header_->tag_); + return SynthesizeBitstream(wpi, &frame->bitstream); +} + +WebPMuxError WebPMuxGetFrame( + const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame) { + WebPMuxError err; + WebPMuxImage* wpi; + + if (mux == NULL || frame == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + // Get the nth WebPMuxImage. + err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, &wpi); + if (err != WEBP_MUX_OK) return err; + + // Get frame info. + if (wpi->header_ == NULL) { + return MuxGetImageInternal(wpi, frame); + } else { + return MuxGetFrameInternal(wpi, frame); + } +} + +WebPMuxError WebPMuxGetAnimationParams(const WebPMux* mux, + WebPMuxAnimParams* params) { + WebPData anim; + WebPMuxError err; + + if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT; + + err = MuxGet(mux, IDX_ANIM, 1, &anim); + if (err != WEBP_MUX_OK) return err; + if (anim.size < kChunks[WEBP_CHUNK_ANIM].size) return WEBP_MUX_BAD_DATA; + params->bgcolor = GetLE32(anim.bytes); + params->loop_count = GetLE16(anim.bytes + 4); + + return WEBP_MUX_OK; +} + +// Get chunk index from chunk id. Returns IDX_NIL if not found. +static CHUNK_INDEX ChunkGetIndexFromId(WebPChunkId id) { + int i; + for (i = 0; kChunks[i].id != WEBP_CHUNK_NIL; ++i) { + if (id == kChunks[i].id) return (CHUNK_INDEX)i; + } + return IDX_NIL; +} + +// Count number of chunks matching 'tag' in the 'chunk_list'. +// If tag == NIL_TAG, any tag will be matched. +static int CountChunks(const WebPChunk* const chunk_list, uint32_t tag) { + int count = 0; + const WebPChunk* current; + for (current = chunk_list; current != NULL; current = current->next_) { + if (tag == NIL_TAG || current->tag_ == tag) { + count++; // Count chunks whose tags match. + } + } + return count; +} + +WebPMuxError WebPMuxNumChunks(const WebPMux* mux, + WebPChunkId id, int* num_elements) { + if (mux == NULL || num_elements == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + if (IsWPI(id)) { + *num_elements = MuxImageCount(mux->images_, id); + } else { + WebPChunk* const* chunk_list = MuxGetChunkListFromId(mux, id); + const CHUNK_INDEX idx = ChunkGetIndexFromId(id); + *num_elements = CountChunks(*chunk_list, kChunks[idx].tag); + } + + return WEBP_MUX_OK; +} + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/src/utils/Makefile.am b/internal/libwebp-1.2.1/src/utils/Makefile.am new file mode 100644 index 0000000..fbb0fe7 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/Makefile.am @@ -0,0 +1,51 @@ +AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir) +noinst_LTLIBRARIES = libwebputils.la + +if BUILD_LIBWEBPDECODER + noinst_LTLIBRARIES += libwebputilsdecode.la +endif + +common_HEADERS = ../webp/types.h +commondir = $(includedir)/webp + +noinst_HEADERS = +noinst_HEADERS += ../dsp/dsp.h +noinst_HEADERS += ../webp/decode.h +noinst_HEADERS += ../webp/encode.h +noinst_HEADERS += ../webp/format_constants.h + +COMMON_SOURCES = +COMMON_SOURCES += bit_reader_utils.c +COMMON_SOURCES += bit_reader_utils.h +COMMON_SOURCES += bit_reader_inl_utils.h +COMMON_SOURCES += color_cache_utils.c +COMMON_SOURCES += color_cache_utils.h +COMMON_SOURCES += endian_inl_utils.h +COMMON_SOURCES += filters_utils.c +COMMON_SOURCES += filters_utils.h +COMMON_SOURCES += huffman_utils.c +COMMON_SOURCES += huffman_utils.h +COMMON_SOURCES += quant_levels_dec_utils.c +COMMON_SOURCES += quant_levels_dec_utils.h +COMMON_SOURCES += rescaler_utils.c +COMMON_SOURCES += rescaler_utils.h +COMMON_SOURCES += random_utils.c +COMMON_SOURCES += random_utils.h +COMMON_SOURCES += thread_utils.c +COMMON_SOURCES += thread_utils.h +COMMON_SOURCES += utils.c +COMMON_SOURCES += utils.h + +ENC_SOURCES = +ENC_SOURCES += bit_writer_utils.c +ENC_SOURCES += bit_writer_utils.h +ENC_SOURCES += huffman_encode_utils.c +ENC_SOURCES += huffman_encode_utils.h +ENC_SOURCES += quant_levels_utils.c +ENC_SOURCES += quant_levels_utils.h + +libwebputils_la_SOURCES = $(COMMON_SOURCES) $(ENC_SOURCES) + +if BUILD_LIBWEBPDECODER + libwebputilsdecode_la_SOURCES = $(COMMON_SOURCES) +endif diff --git a/internal/libwebp-1.2.1/src/utils/bit_reader_inl_utils.h b/internal/libwebp-1.2.1/src/utils/bit_reader_inl_utils.h new file mode 100644 index 0000000..404b9a6 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/bit_reader_inl_utils.h @@ -0,0 +1,196 @@ +// 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_UTILS_H_ +#define WEBP_UTILS_BIT_READER_INL_UTILS_H_ + +#ifdef HAVE_CONFIG_H +#include "src/webp/config.h" +#endif + +#include // for memcpy + +#include "src/dsp/dsp.h" +#include "src/utils/bit_reader_utils.h" +#include "src/utils/endian_inl_utils.h" +#include "src/utils/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* WEBP_RESTRICT 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* WEBP_RESTRICT const br, + int prob, const char label[]) { + // 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; + BT_TRACK(br); + 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* WEBP_RESTRICT const br, int v, + const char label[]) { + 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; + BT_TRACK(br); + return (v ^ mask) - mask; + } +} + +static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* WEBP_RESTRICT const br, + int prob, const char label[]) { + // 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; + BT_TRACK(br); + return bit; + } +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_UTILS_BIT_READER_INL_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/bit_reader_utils.c b/internal/libwebp-1.2.1/src/utils/bit_reader_utils.c new file mode 100644 index 0000000..857cd60 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/bit_reader_utils.c @@ -0,0 +1,298 @@ +// 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 "src/webp/config.h" +#endif + +#include "src/utils/bit_reader_inl_utils.h" +#include "src/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); + VP8LoadNewBytes(br); +} + +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, const char label[]) { + uint32_t v = 0; + while (bits-- > 0) { + v |= VP8GetBit(br, 0x80, label) << bits; + } + return v; +} + +int32_t VP8GetSignedValue(VP8BitReader* const br, int bits, + const char label[]) { + const int value = VP8GetValue(br, bits, label); + return VP8Get(br, label) ? -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; + } +} + +//------------------------------------------------------------------------------ +// Bit-tracing tool + +#if (BITTRACE > 0) + +#include // for atexit() +#include +#include + +#define MAX_NUM_LABELS 32 +static struct { + const char* label; + int size; + int count; +} kLabels[MAX_NUM_LABELS]; + +static int last_label = 0; +static int last_pos = 0; +static const uint8_t* buf_start = NULL; +static int init_done = 0; + +static void PrintBitTraces(void) { + int i; + int scale = 1; + int total = 0; + const char* units = "bits"; +#if (BITTRACE == 2) + scale = 8; + units = "bytes"; +#endif + for (i = 0; i < last_label; ++i) total += kLabels[i].size; + if (total < 1) total = 1; // avoid rounding errors + printf("=== Bit traces ===\n"); + for (i = 0; i < last_label; ++i) { + const int skip = 16 - (int)strlen(kLabels[i].label); + const int value = (kLabels[i].size + scale - 1) / scale; + assert(skip > 0); + printf("%s \%*s: %6d %s \t[%5.2f%%] [count: %7d]\n", + kLabels[i].label, skip, "", value, units, + 100.f * kLabels[i].size / total, + kLabels[i].count); + } + total = (total + scale - 1) / scale; + printf("Total: %d %s\n", total, units); +} + +void BitTrace(const struct VP8BitReader* const br, const char label[]) { + int i, pos; + if (!init_done) { + memset(kLabels, 0, sizeof(kLabels)); + atexit(PrintBitTraces); + buf_start = br->buf_; + init_done = 1; + } + pos = (int)(br->buf_ - buf_start) * 8 - br->bits_; + // if there's a too large jump, we've changed partition -> reset counter + if (abs(pos - last_pos) > 32) { + buf_start = br->buf_; + pos = 0; + last_pos = 0; + } + if (br->range_ >= 0x7f) pos += kVP8Log2Range[br->range_ - 0x7f]; + for (i = 0; i < last_label; ++i) { + if (!strcmp(label, kLabels[i].label)) break; + } + if (i == MAX_NUM_LABELS) abort(); // overflow! + kLabels[i].label = label; + kLabels[i].size += pos - last_pos; + kLabels[i].count += 1; + if (i == last_label) ++last_label; + last_pos = pos; +} + +#endif // BITTRACE > 0 + +//------------------------------------------------------------------------------ diff --git a/internal/libwebp-1.2.1/src/utils/bit_reader_utils.h b/internal/libwebp-1.2.1/src/utils/bit_reader_utils.h new file mode 100644 index 0000000..e64156e --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/bit_reader_utils.h @@ -0,0 +1,194 @@ +// 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_UTILS_H_ +#define WEBP_UTILS_BIT_READER_UTILS_H_ + +#include +#ifdef _MSC_VER +#include // _byteswap_ulong +#endif +#include "src/webp/types.h" + +// Warning! This macro triggers quite some MACRO wizardry around func signature! +#if !defined(BITTRACE) +#define BITTRACE 0 // 0 = off, 1 = print bits, 2 = print bytes +#endif + +#if (BITTRACE > 0) +struct VP8BitReader; +extern void BitTrace(const struct VP8BitReader* const br, const char label[]); +#define BT_TRACK(br) BitTrace(br, label) +#define VP8Get(BR, L) VP8GetValue(BR, 1, L) +#else +#define BT_TRACK(br) +// We'll REMOVE the 'const char label[]' from all signatures and calls (!!): +#define VP8GetValue(BR, N, L) VP8GetValue(BR, N) +#define VP8Get(BR, L) VP8GetValue(BR, 1, L) +#define VP8GetSignedValue(BR, N, L) VP8GetSignedValue(BR, N) +#define VP8GetBit(BR, P, L) VP8GetBit(BR, P) +#define VP8GetBitAlt(BR, P, L) VP8GetBitAlt(BR, P) +#define VP8GetSigned(BR, V, L) VP8GetSigned(BR, V) +#endif + +#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. + +#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 + +//------------------------------------------------------------------------------ +// 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, const char label[]); + +// return the next value with sign-extension. +int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits, + const char label[]); + +// 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. +// This function does *not* set br->eos_, since it's speed-critical. +// Use with extreme care! +static WEBP_INLINE void VP8LSetBitPos(VP8LBitReader* const br, int val) { + br->bit_pos_ = val; +} + +// 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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/bit_writer_utils.c b/internal/libwebp-1.2.1/src/utils/bit_writer_utils.c new file mode 100644 index 0000000..2f40850 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/bit_writer_utils.c @@ -0,0 +1,347 @@ +// 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 "src/utils/bit_writer_utils.h" +#include "src/utils/endian_inl_utils.h" +#include "src/utils/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 & 0xff; + 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); +} + +int VP8LBitWriterClone(const VP8LBitWriter* const src, + VP8LBitWriter* const dst) { + const size_t current_size = src->cur_ - src->buf_; + assert(src->cur_ >= src->buf_ && src->cur_ <= src->end_); + if (!VP8LBitWriterResize(dst, current_size)) return 0; + memcpy(dst->buf_, src->buf_, current_size); + dst->bits_ = src->bits_; + dst->used_ = src->used_; + dst->error_ = src->error_; + dst->cur_ = dst->buf_ + current_size; + return 1; +} + +void VP8LBitWriterWipeOut(VP8LBitWriter* const bw) { + if (bw != NULL) { + WebPSafeFree(bw->buf_); + memset(bw, 0, sizeof(*bw)); + } +} + +void VP8LBitWriterReset(const VP8LBitWriter* const bw_init, + VP8LBitWriter* const bw) { + bw->bits_ = bw_init->bits_; + bw->used_ = bw_init->used_; + bw->cur_ = bw->buf_ + (bw_init->cur_ - bw_init->buf_); + assert(bw->cur_ <= bw->end_); + bw->error_ = bw_init->error_; +} + +void VP8LBitWriterSwap(VP8LBitWriter* const src, VP8LBitWriter* const dst) { + const VP8LBitWriter tmp = *src; + *src = *dst; + *dst = tmp; +} + +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 (!CheckSizeOverflow(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 (!CheckSizeOverflow(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/internal/libwebp-1.2.1/src/utils/bit_writer_utils.h b/internal/libwebp-1.2.1/src/utils/bit_writer_utils.h new file mode 100644 index 0000000..b9d5102 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/bit_writer_utils.h @@ -0,0 +1,154 @@ +// 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_UTILS_H_ +#define WEBP_UTILS_BIT_WRITER_UTILS_H_ + +#include "src/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(const 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); +// Returns false in case of memory allocation error. +int VP8LBitWriterClone(const VP8LBitWriter* const src, + VP8LBitWriter* const dst); +// 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); +// Resets the cursor of the BitWriter bw to when it was like in bw_init. +void VP8LBitWriterReset(const VP8LBitWriter* const bw_init, + VP8LBitWriter* const bw); +// Swaps the memory held by two BitWriters. +void VP8LBitWriterSwap(VP8LBitWriter* const src, VP8LBitWriter* const dst); + +// 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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/color_cache_utils.c b/internal/libwebp-1.2.1/src/utils/color_cache_utils.c new file mode 100644 index 0000000..7b5222b --- /dev/null +++ b/internal/libwebp-1.2.1/src/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 "src/utils/color_cache_utils.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ +// VP8LColorCache. + +int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits) { + const int hash_size = 1 << hash_bits; + assert(color_cache != NULL); + assert(hash_bits > 0); + color_cache->colors_ = (uint32_t*)WebPSafeCalloc( + (uint64_t)hash_size, sizeof(*color_cache->colors_)); + if (color_cache->colors_ == NULL) return 0; + color_cache->hash_shift_ = 32 - hash_bits; + color_cache->hash_bits_ = hash_bits; + return 1; +} + +void VP8LColorCacheClear(VP8LColorCache* const color_cache) { + if (color_cache != NULL) { + WebPSafeFree(color_cache->colors_); + color_cache->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/internal/libwebp-1.2.1/src/utils/color_cache_utils.h b/internal/libwebp-1.2.1/src/utils/color_cache_utils.h new file mode 100644 index 0000000..b45d47c --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/color_cache_utils.h @@ -0,0 +1,89 @@ +// 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_UTILS_H_ +#define WEBP_UTILS_COLOR_CACHE_UTILS_H_ + +#include + +#include "src/dsp/dsp.h" +#include "src/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 = 0x1e35a7bdu; + +static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE +int VP8LHashPix(uint32_t argb, int shift) { + return (int)((argb * kHashMul) >> 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 = VP8LHashPix(argb, cc->hash_shift_); + cc->colors_[key] = argb; +} + +static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc, + uint32_t argb) { + return VP8LHashPix(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 = VP8LHashPix(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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/endian_inl_utils.h b/internal/libwebp-1.2.1/src/utils/endian_inl_utils.h new file mode 100644 index 0000000..3630a29 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/endian_inl_utils.h @@ -0,0 +1,93 @@ +// 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_UTILS_H_ +#define WEBP_UTILS_ENDIAN_INL_UTILS_H_ + +#ifdef HAVE_CONFIG_H +#include "src/webp/config.h" +#endif + +#include "src/dsp/dsp.h" +#include "src/webp/types.h" + +#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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/filters_utils.c b/internal/libwebp-1.2.1/src/utils/filters_utils.c new file mode 100644 index 0000000..bbc2c34 --- /dev/null +++ b/internal/libwebp-1.2.1/src/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 "src/utils/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/internal/libwebp-1.2.1/src/utils/filters_utils.h b/internal/libwebp-1.2.1/src/utils/filters_utils.h new file mode 100644 index 0000000..61da66e --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_UTILS_H_ +#define WEBP_UTILS_FILTERS_UTILS_H_ + +#include "src/webp/types.h" +#include "src/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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/huffman_encode_utils.c b/internal/libwebp-1.2.1/src/utils/huffman_encode_utils.c new file mode 100644 index 0000000..fd7a47d --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/huffman_encode_utils.c @@ -0,0 +1,416 @@ +// 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 "src/utils/huffman_encode_utils.h" +#include "src/utils/utils.h" +#include "src/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/internal/libwebp-1.2.1/src/utils/huffman_encode_utils.h b/internal/libwebp-1.2.1/src/utils/huffman_encode_utils.h new file mode 100644 index 0000000..3f7f1d8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_UTILS_H_ +#define WEBP_UTILS_HUFFMAN_ENCODE_UTILS_H_ + +#include "src/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 huff_code); + +#ifdef __cplusplus +} +#endif + +#endif // WEBP_UTILS_HUFFMAN_ENCODE_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/huffman_utils.c b/internal/libwebp-1.2.1/src/utils/huffman_utils.c new file mode 100644 index 0000000..0cba0fb --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/huffman_utils.c @@ -0,0 +1,235 @@ +// 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 "src/utils/huffman_utils.h" +#include "src/utils/utils.h" +#include "src/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 && sorted != NULL) || + (root_table == NULL && sorted == 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) { + if (sorted != NULL) { + sorted[offset[symbol_code_length]++] = symbol; + } else { + offset[symbol_code_length]++; + } + } + } + + // Special case code with only one value. + if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) { + if (sorted != NULL) { + 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; + } + if (root_table == NULL) continue; + 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; + } + if (root_table == NULL) continue; + 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 (root_table == NULL) { + total_size = BuildHuffmanTable(NULL, root_bits, + code_lengths, code_lengths_size, NULL); + } else 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/internal/libwebp-1.2.1/src/utils/huffman_utils.h b/internal/libwebp-1.2.1/src/utils/huffman_utils.h new file mode 100644 index 0000000..13b7ad1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/huffman_utils.h @@ -0,0 +1,90 @@ +// 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_UTILS_H_ +#define WEBP_UTILS_HUFFMAN_UTILS_H_ + +#include +#include "src/webp/format_constants.h" +#include "src/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). +// If root_table is NULL, it returns 0 if a lookup cannot be built, something +// > 0 otherwise (but not the table size). +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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/quant_levels_dec_utils.c b/internal/libwebp-1.2.1/src/utils/quant_levels_dec_utils.c new file mode 100644 index 0000000..f65b6cd --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/quant_levels_dec_utils.c @@ -0,0 +1,291 @@ +// 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 "src/utils/quant_levels_dec_utils.h" + +#include // for memset + +#include "src/utils/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_8b_MASK (int)(~0U << (8 + DFIX)) +static WEBP_INLINE uint8_t clip_8b(int v) { + return (!(v & CLIP_8b_MASK)) ? (uint8_t)(v >> DFIX) : (v < 0) ? 0u : 255u; +} +#undef CLIP_8b_MASK + +// 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) { + int radius = 4 * strength / 100; + + if (strength < 0 || strength > 100) return 0; + if (data == NULL || width <= 0 || height <= 0) return 0; // bad params + + // limit the filter size to not exceed the image dimensions + if (2 * radius + 1 > width) radius = (width - 1) >> 1; + if (2 * radius + 1 > height) radius = (height - 1) >> 1; + + 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/internal/libwebp-1.2.1/src/utils/quant_levels_dec_utils.h b/internal/libwebp-1.2.1/src/utils/quant_levels_dec_utils.h new file mode 100644 index 0000000..327f19f --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_UTILS_H_ +#define WEBP_UTILS_QUANT_LEVELS_DEC_UTILS_H_ + +#include "src/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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/quant_levels_utils.c b/internal/libwebp-1.2.1/src/utils/quant_levels_utils.c new file mode 100644 index 0000000..d65ad3c --- /dev/null +++ b/internal/libwebp-1.2.1/src/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 "src/utils/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/internal/libwebp-1.2.1/src/utils/quant_levels_utils.h b/internal/libwebp-1.2.1/src/utils/quant_levels_utils.h new file mode 100644 index 0000000..9ee3ea0 --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_UTILS_H_ +#define WEBP_UTILS_QUANT_LEVELS_UTILS_H_ + +#include + +#include "src/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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/random_utils.c b/internal/libwebp-1.2.1/src/utils/random_utils.c new file mode 100644 index 0000000..7edb3fe --- /dev/null +++ b/internal/libwebp-1.2.1/src/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 "src/utils/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/internal/libwebp-1.2.1/src/utils/random_utils.h b/internal/libwebp-1.2.1/src/utils/random_utils.h new file mode 100644 index 0000000..a5006f8 --- /dev/null +++ b/internal/libwebp-1.2.1/src/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_UTILS_H_ +#define WEBP_UTILS_RANDOM_UTILS_H_ + +#include +#include "src/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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/rescaler_utils.c b/internal/libwebp-1.2.1/src/utils/rescaler_utils.c new file mode 100644 index 0000000..a0581a1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/rescaler_utils.c @@ -0,0 +1,160 @@ +// 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 +#include "src/dsp/dsp.h" +#include "src/utils/rescaler_utils.h" +#include "src/utils/utils.h" + +//------------------------------------------------------------------------------ + +int 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) { + const int x_add = src_width, x_sub = dst_width; + const int y_add = src_height, y_sub = dst_height; + const uint64_t total_size = 2ull * dst_width * num_channels * sizeof(*work); + if (!CheckSizeOverflow(total_size)) return 0; + + rescaler->x_expand = (src_width < dst_width); + rescaler->y_expand = (src_height < dst_height); + rescaler->src_width = src_width; + rescaler->src_height = src_height; + rescaler->dst_width = dst_width; + rescaler->dst_height = dst_height; + rescaler->src_y = 0; + rescaler->dst_y = 0; + rescaler->dst = dst; + rescaler->dst_stride = dst_stride; + rescaler->num_channels = num_channels; + + // for 'x_expand', we use bilinear interpolation + rescaler->x_add = rescaler->x_expand ? (x_sub - 1) : x_add; + rescaler->x_sub = rescaler->x_expand ? (x_add - 1) : x_sub; + if (!rescaler->x_expand) { // fx_scale is not used otherwise + rescaler->fx_scale = WEBP_RESCALER_FRAC(1, rescaler->x_sub); + } + // vertical scaling parameters + rescaler->y_add = rescaler->y_expand ? y_add - 1 : y_add; + rescaler->y_sub = rescaler->y_expand ? y_sub - 1 : y_sub; + rescaler->y_accum = rescaler->y_expand ? rescaler->y_sub : rescaler->y_add; + if (!rescaler->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 <= rescaler->y_add + // and rescaler->x_add >= 1; + const uint64_t num = (uint64_t)dst_height * WEBP_RESCALER_ONE; + const uint64_t den = (uint64_t)rescaler->x_add * rescaler->y_add; + const uint64_t ratio = num / den; + 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 == + // rescaler->y_add (which == src_height), and rescaler->x_add == 1. + // => We special-case fxy_scale = 0, in WebPRescalerExportRow(). + rescaler->fxy_scale = 0; + } else { + rescaler->fxy_scale = (uint32_t)ratio; + } + rescaler->fy_scale = WEBP_RESCALER_FRAC(1, rescaler->y_sub); + } else { + rescaler->fy_scale = WEBP_RESCALER_FRAC(1, rescaler->x_add); + // rescaler->fxy_scale is unused here. + } + rescaler->irow = work; + rescaler->frow = work + num_channels * dst_width; + memset(work, 0, (size_t)total_size); + + WebPRescalerDspInit(); + return 1; +} + +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; + const int max_size = INT_MAX / 2; + + // if width is unspecified, scale original proportionally to height ratio. + if (width == 0 && src_height > 0) { + width = + (int)(((uint64_t)src_width * height + src_height - 1) / src_height); + } + // if height is unspecified, scale original proportionally to width ratio. + if (height == 0 && src_width > 0) { + height = + (int)(((uint64_t)src_height * width + src_width - 1) / src_width); + } + // Check if the overall dimensions still make sense. + if (width <= 0 || height <= 0 || width > max_size || height > max_size) { + return 0; + } + + *scaled_width = width; + *scaled_height = height; + return 1; + } +} + +//------------------------------------------------------------------------------ +// all-in-one calls + +int WebPRescaleNeededLines(const WebPRescaler* const rescaler, + int max_num_lines) { + const int num_lines = + (rescaler->y_accum + rescaler->y_sub - 1) / rescaler->y_sub; + return (num_lines > max_num_lines) ? max_num_lines : num_lines; +} + +int WebPRescalerImport(WebPRescaler* const rescaler, int num_lines, + const uint8_t* src, int src_stride) { + int total_imported = 0; + while (total_imported < num_lines && + !WebPRescalerHasPendingOutput(rescaler)) { + if (rescaler->y_expand) { + rescaler_t* const tmp = rescaler->irow; + rescaler->irow = rescaler->frow; + rescaler->frow = tmp; + } + WebPRescalerImportRow(rescaler, src); + if (!rescaler->y_expand) { // Accumulate the contribution of the new row. + int x; + for (x = 0; x < rescaler->num_channels * rescaler->dst_width; ++x) { + rescaler->irow[x] += rescaler->frow[x]; + } + } + ++rescaler->src_y; + src += src_stride; + ++total_imported; + rescaler->y_accum -= rescaler->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/internal/libwebp-1.2.1/src/utils/rescaler_utils.h b/internal/libwebp-1.2.1/src/utils/rescaler_utils.h new file mode 100644 index 0000000..ef201ef --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/rescaler_utils.h @@ -0,0 +1,102 @@ +// 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_UTILS_H_ +#define WEBP_UTILS_RESCALER_UTILS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "src/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. +// Returns false in case of error. +int 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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/thread_utils.c b/internal/libwebp-1.2.1/src/utils/thread_utils.c new file mode 100644 index 0000000..4e470e1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/thread_utils.c @@ -0,0 +1,369 @@ +// 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 "src/utils/thread_utils.h" +#include "src/utils/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 + +typedef struct { + pthread_mutex_t mutex_; + pthread_cond_t condition_; + pthread_t thread_; +} WebPWorkerImpl; + +#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 THREADFN ThreadLoop(void* ptr) { + WebPWorker* const worker = (WebPWorker*)ptr; + WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_; + int done = 0; + while (!done) { + pthread_mutex_lock(&impl->mutex_); + while (worker->status_ == OK) { // wait in idling mode + pthread_cond_wait(&impl->condition_, &impl->mutex_); + } + if (worker->status_ == WORK) { + WebPGetWorkerInterface()->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()) + // Note the associated mutex does not need to be held when signaling the + // condition. Unlocking the mutex first may improve performance in some + // implementations, avoiding the case where the waiting thread can't + // reacquire the mutex when woken. + pthread_mutex_unlock(&impl->mutex_); + pthread_cond_signal(&impl->condition_); + } + 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. + WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_; + if (impl == NULL) return; + + pthread_mutex_lock(&impl->mutex_); + if (worker->status_ >= OK) { + // wait for the worker to finish + while (worker->status_ != OK) { + pthread_cond_wait(&impl->condition_, &impl->mutex_); + } + // assign new status and release the working thread if needed + if (new_status != OK) { + worker->status_ = new_status; + // Note the associated mutex does not need to be held when signaling the + // condition. Unlocking the mutex first may improve performance in some + // implementations, avoiding the case where the waiting thread can't + // reacquire the mutex when woken. + pthread_mutex_unlock(&impl->mutex_); + pthread_cond_signal(&impl->condition_); + return; + } + } + pthread_mutex_unlock(&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 + WebPWorkerImpl* const impl = + (WebPWorkerImpl*)WebPSafeCalloc(1, sizeof(WebPWorkerImpl)); + worker->impl_ = (void*)impl; + if (worker->impl_ == NULL) { + return 0; + } + if (pthread_mutex_init(&impl->mutex_, NULL)) { + goto Error; + } + if (pthread_cond_init(&impl->condition_, NULL)) { + pthread_mutex_destroy(&impl->mutex_); + goto Error; + } + pthread_mutex_lock(&impl->mutex_); + ok = !pthread_create(&impl->thread_, NULL, ThreadLoop, worker); + if (ok) worker->status_ = OK; + pthread_mutex_unlock(&impl->mutex_); + if (!ok) { + pthread_mutex_destroy(&impl->mutex_); + pthread_cond_destroy(&impl->condition_); + Error: + WebPSafeFree(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) { + WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_; + ChangeState(worker, NOT_OK); + pthread_join(impl->thread_, NULL); + pthread_mutex_destroy(&impl->mutex_); + pthread_cond_destroy(&impl->condition_); + WebPSafeFree(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/internal/libwebp-1.2.1/src/utils/thread_utils.h b/internal/libwebp-1.2.1/src/utils/thread_utils.h new file mode 100644 index 0000000..29ad49f --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/thread_utils.h @@ -0,0 +1,90 @@ +// 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_UTILS_H_ +#define WEBP_UTILS_THREAD_UTILS_H_ + +#ifdef HAVE_CONFIG_H +#include "src/webp/config.h" +#endif + +#include "src/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*); + +// Synchronization object used to launch job in the worker thread +typedef struct { + void* impl_; // platform-dependent implementation worker details + 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_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/utils/utils.c b/internal/libwebp-1.2.1/src/utils/utils.c new file mode 100644 index 0000000..9e464c1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/utils.c @@ -0,0 +1,338 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Misc. common utility functions +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include // for memcpy() +#include "src/webp/decode.h" +#include "src/webp/encode.h" +#include "src/webp/format_constants.h" // for MAX_PALETTE_SIZE +#include "src/utils/color_cache_utils.h" +#include "src/utils/utils.h" + +// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of +// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow, +// and not multi-thread safe!). +// An interesting alternative is valgrind's 'massif' tool: +// 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=WebPSafeMalloc --alloc-fn=WebPSafeCalloc + ms_print massif.out +*/ +// In addition: +// * if PRINT_MEM_TRAFFIC is defined, all the details of the malloc/free cycles +// are printed. +// * if MALLOC_FAIL_AT is defined, the global environment variable +// $MALLOC_FAIL_AT is used to simulate a memory error when calloc or malloc +// is called for the nth time. Example usage: +// export MALLOC_FAIL_AT=50 && ./examples/cwebp input.png +// * if MALLOC_LIMIT is defined, the global environment variable $MALLOC_LIMIT +// sets the maximum amount of memory (in bytes) made available to libwebp. +// This can be used to emulate environment with very limited memory. +// Example: export MALLOC_LIMIT=64000000 && ./examples/dwebp picture.webp + +// #define PRINT_MEM_INFO +// #define PRINT_MEM_TRAFFIC +// #define MALLOC_FAIL_AT +// #define MALLOC_LIMIT + +//------------------------------------------------------------------------------ +// Checked memory allocation + +#if defined(PRINT_MEM_INFO) + +#include + +static int num_malloc_calls = 0; +static int num_calloc_calls = 0; +static int num_free_calls = 0; +static int countdown_to_fail = 0; // 0 = off + +typedef struct MemBlock MemBlock; +struct MemBlock { + void* ptr_; + size_t size_; + MemBlock* next_; +}; + +static MemBlock* all_blocks = NULL; +static size_t total_mem = 0; +static size_t total_mem_allocated = 0; +static size_t high_water_mark = 0; +static size_t mem_limit = 0; + +static int exit_registered = 0; + +static void PrintMemInfo(void) { + fprintf(stderr, "\nMEMORY INFO:\n"); + fprintf(stderr, "num calls to: malloc = %4d\n", num_malloc_calls); + fprintf(stderr, " calloc = %4d\n", num_calloc_calls); + fprintf(stderr, " free = %4d\n", num_free_calls); + fprintf(stderr, "total_mem: %u\n", (uint32_t)total_mem); + fprintf(stderr, "total_mem allocated: %u\n", (uint32_t)total_mem_allocated); + fprintf(stderr, "high-water mark: %u\n", (uint32_t)high_water_mark); + while (all_blocks != NULL) { + MemBlock* b = all_blocks; + all_blocks = b->next_; + free(b); + } +} + +static void Increment(int* const v) { + if (!exit_registered) { +#if defined(MALLOC_FAIL_AT) + { + const char* const malloc_fail_at_str = getenv("MALLOC_FAIL_AT"); + if (malloc_fail_at_str != NULL) { + countdown_to_fail = atoi(malloc_fail_at_str); + } + } +#endif +#if defined(MALLOC_LIMIT) + { + const char* const malloc_limit_str = getenv("MALLOC_LIMIT"); +#if MALLOC_LIMIT > 1 + mem_limit = (size_t)MALLOC_LIMIT; +#endif + if (malloc_limit_str != NULL) { + mem_limit = atoi(malloc_limit_str); + } + } +#endif + (void)countdown_to_fail; + (void)mem_limit; + atexit(PrintMemInfo); + exit_registered = 1; + } + ++*v; +} + +static void AddMem(void* ptr, size_t size) { + if (ptr != NULL) { + MemBlock* const b = (MemBlock*)malloc(sizeof(*b)); + if (b == NULL) abort(); + b->next_ = all_blocks; + all_blocks = b; + b->ptr_ = ptr; + b->size_ = size; + total_mem += size; + total_mem_allocated += size; +#if defined(PRINT_MEM_TRAFFIC) +#if defined(MALLOC_FAIL_AT) + fprintf(stderr, "fail-count: %5d [mem=%u]\n", + num_malloc_calls + num_calloc_calls, (uint32_t)total_mem); +#else + fprintf(stderr, "Mem: %u (+%u)\n", (uint32_t)total_mem, (uint32_t)size); +#endif +#endif + if (total_mem > high_water_mark) high_water_mark = total_mem; + } +} + +static void SubMem(void* ptr) { + if (ptr != NULL) { + MemBlock** b = &all_blocks; + // Inefficient search, but that's just for debugging. + while (*b != NULL && (*b)->ptr_ != ptr) b = &(*b)->next_; + if (*b == NULL) { + fprintf(stderr, "Invalid pointer free! (%p)\n", ptr); + abort(); + } + { + MemBlock* const block = *b; + *b = block->next_; + total_mem -= block->size_; +#if defined(PRINT_MEM_TRAFFIC) + fprintf(stderr, "Mem: %u (-%u)\n", + (uint32_t)total_mem, (uint32_t)block->size_); +#endif + free(block); + } + } +} + +#else +#define Increment(v) do {} while (0) +#define AddMem(p, s) do {} while (0) +#define SubMem(p) do {} while (0) +#endif + +// Returns 0 in case of overflow of nmemb * size. +static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) { + const uint64_t total_size = nmemb * size; + if (nmemb == 0) return 1; + if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0; + if (!CheckSizeOverflow(total_size)) return 0; +#if defined(PRINT_MEM_INFO) && defined(MALLOC_FAIL_AT) + if (countdown_to_fail > 0 && --countdown_to_fail == 0) { + return 0; // fake fail! + } +#endif +#if defined(PRINT_MEM_INFO) && defined(MALLOC_LIMIT) + if (mem_limit > 0) { + const uint64_t new_total_mem = (uint64_t)total_mem + total_size; + if (!CheckSizeOverflow(new_total_mem) || + new_total_mem > mem_limit) { + return 0; // fake fail! + } + } +#endif + + return 1; +} + +void* WebPSafeMalloc(uint64_t nmemb, size_t size) { + void* ptr; + Increment(&num_malloc_calls); + if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL; + assert(nmemb * size > 0); + ptr = malloc((size_t)(nmemb * size)); + AddMem(ptr, (size_t)(nmemb * size)); + return ptr; +} + +void* WebPSafeCalloc(uint64_t nmemb, size_t size) { + void* ptr; + Increment(&num_calloc_calls); + if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL; + assert(nmemb * size > 0); + ptr = calloc((size_t)nmemb, size); + AddMem(ptr, (size_t)(nmemb * size)); + return ptr; +} + +void WebPSafeFree(void* const ptr) { + if (ptr != NULL) { + Increment(&num_free_calls); + SubMem(ptr); + } + free(ptr); +} + +// Public API functions. + +void* WebPMalloc(size_t size) { + return WebPSafeMalloc(1, size); +} + +void WebPFree(void* ptr) { + WebPSafeFree(ptr); +} + +//------------------------------------------------------------------------------ + +void WebPCopyPlane(const uint8_t* src, int src_stride, + uint8_t* dst, int dst_stride, int width, int height) { + assert(src != NULL && dst != NULL); + assert(abs(src_stride) >= width && abs(dst_stride) >= width); + while (height-- > 0) { + memcpy(dst, src, width); + src += src_stride; + dst += dst_stride; + } +} + +void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) { + assert(src != NULL && dst != NULL); + assert(src->width == dst->width && src->height == dst->height); + assert(src->use_argb && dst->use_argb); + WebPCopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb, + 4 * dst->argb_stride, 4 * src->width, src->height); +} + +//------------------------------------------------------------------------------ + +#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) { + int i; + int x, y; + int num_colors = 0; + uint8_t in_use[COLOR_HASH_SIZE] = { 0 }; + uint32_t colors[COLOR_HASH_SIZE]; + const uint32_t* argb = pic->argb; + const int width = pic->width; + const int height = pic->height; + uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] + assert(pic != NULL); + assert(pic->use_argb); + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + int key; + if (argb[x] == last_pix) { + continue; + } + last_pix = argb[x]; + key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT); + while (1) { + if (!in_use[key]) { + colors[key] = last_pix; + in_use[key] = 1; + ++num_colors; + if (num_colors > MAX_PALETTE_SIZE) { + return MAX_PALETTE_SIZE + 1; // Exact count not needed. + } + break; + } else if (colors[key] == last_pix) { + break; // The color is already there. + } else { + // Some other color sits here, so do linear conflict resolution. + ++key; + key &= (COLOR_HASH_SIZE - 1); // Key mask. + } + } + } + argb += pic->argb_stride; + } + + if (palette != NULL) { // Fill the colors into palette. + num_colors = 0; + for (i = 0; i < COLOR_HASH_SIZE; ++i) { + if (in_use[i]) { + palette[num_colors] = colors[i]; + ++num_colors; + } + } + } + return num_colors; +} + +#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/internal/libwebp-1.2.1/src/utils/utils.h b/internal/libwebp-1.2.1/src/utils/utils.h new file mode 100644 index 0000000..ef04f10 --- /dev/null +++ b/internal/libwebp-1.2.1/src/utils/utils.h @@ -0,0 +1,200 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Misc. common utility functions +// +// Authors: Skal (pascal.massimino@gmail.com) +// Urvang (urvang@google.com) + +#ifndef WEBP_UTILS_UTILS_H_ +#define WEBP_UTILS_UTILS_H_ + +#ifdef HAVE_CONFIG_H +#include "src/webp/config.h" +#endif + +#include +#include + +#include "src/dsp/dsp.h" +#include "src/webp/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ +// Memory allocation + +// This is the maximum memory amount that libwebp will ever try to allocate. +#ifndef WEBP_MAX_ALLOCABLE_MEMORY +#if SIZE_MAX > (1ULL << 34) +#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 34) +#else +// For 32-bit targets keep this below INT_MAX to avoid valgrind warnings. +#define WEBP_MAX_ALLOCABLE_MEMORY ((1ULL << 31) - (1 << 16)) +#endif +#endif // WEBP_MAX_ALLOCABLE_MEMORY + +static WEBP_INLINE int CheckSizeOverflow(uint64_t size) { + return size == (size_t)size; +} + +// size-checking safe malloc/calloc: verify that the requested size is not too +// large, or return NULL. You don't need to call these for constructs like +// malloc(sizeof(foo)), but only if there's picture-dependent size involved +// somewhere (like: malloc(num_pixels * sizeof(*something))). That's why this +// safe malloc() borrows the signature from calloc(), pointing at the dangerous +// underlying multiply involved. +WEBP_EXTERN void* WebPSafeMalloc(uint64_t nmemb, size_t size); +// Note that WebPSafeCalloc() expects the second argument type to be 'size_t' +// in order to favor the "calloc(num_foo, sizeof(foo))" pattern. +WEBP_EXTERN void* WebPSafeCalloc(uint64_t nmemb, size_t size); + +// Companion deallocation function to the above allocations. +WEBP_EXTERN void WebPSafeFree(void* const ptr); + +//------------------------------------------------------------------------------ +// Alignment + +#define WEBP_ALIGN_CST 31 +#define WEBP_ALIGN(PTR) (((uintptr_t)(PTR) + WEBP_ALIGN_CST) & ~WEBP_ALIGN_CST) + +#include +// memcpy() is the safe way of moving potentially unaligned 32b memory. +static WEBP_INLINE uint32_t WebPMemToUint32(const uint8_t* const ptr) { + uint32_t A; + memcpy(&A, ptr, sizeof(A)); + return A; +} +static WEBP_INLINE void WebPUint32ToMem(uint8_t* const ptr, uint32_t val) { + memcpy(ptr, &val, sizeof(val)); +} + +//------------------------------------------------------------------------------ +// Reading/writing data. + +// Read 16, 24 or 32 bits stored in little-endian order. +static WEBP_INLINE int GetLE16(const uint8_t* const data) { + return (int)(data[0] << 0) | (data[1] << 8); +} + +static WEBP_INLINE int GetLE24(const uint8_t* const data) { + return GetLE16(data) | (data[2] << 16); +} + +static WEBP_INLINE uint32_t GetLE32(const uint8_t* const data) { + return GetLE16(data) | ((uint32_t)GetLE16(data + 2) << 16); +} + +// Store 16, 24 or 32 bits in little-endian order. +static WEBP_INLINE void PutLE16(uint8_t* const data, int val) { + assert(val < (1 << 16)); + data[0] = (val >> 0) & 0xff; + data[1] = (val >> 8) & 0xff; +} + +static WEBP_INLINE void PutLE24(uint8_t* const data, int val) { + assert(val < (1 << 24)); + PutLE16(data, val & 0xffff); + data[2] = (val >> 16) & 0xff; +} + +static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) { + PutLE16(data, (int)(val & 0xffff)); + PutLE16(data + 2, (int)(val >> 16)); +} + +// use GNU builtins where available. +#if defined(__GNUC__) && \ + ((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4) +// Returns (int)floor(log2(n)). n must be > 0. +static WEBP_INLINE int BitsLog2Floor(uint32_t n) { + return 31 ^ __builtin_clz(n); +} +// counts the number of trailing zero +static WEBP_INLINE int BitsCtz(uint32_t n) { return __builtin_ctz(n); } +#elif defined(_MSC_VER) && _MSC_VER > 1310 && \ + (defined(_M_X64) || defined(_M_IX86)) +#include +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) + +static WEBP_INLINE int BitsLog2Floor(uint32_t n) { + unsigned long first_set_bit; // NOLINT (runtime/int) + _BitScanReverse(&first_set_bit, n); + return first_set_bit; +} +static WEBP_INLINE int BitsCtz(uint32_t n) { + unsigned long first_set_bit; // NOLINT (runtime/int) + _BitScanForward(&first_set_bit, n); + return first_set_bit; +} +#else // default: use the (slow) C-version. +#define WEBP_HAVE_SLOW_CLZ_CTZ // signal that the Clz/Ctz function are slow +// 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_value = 0; + while (n >= 256) { + log_value += 8; + n >>= 8; + } + return log_value + WebPLogTable8bit[n]; +} + +static WEBP_INLINE int BitsLog2Floor(uint32_t n) { return WebPLog2FloorC(n); } + +static WEBP_INLINE int BitsCtz(uint32_t n) { + int i; + for (i = 0; i < 32; ++i, n >>= 1) { + if (n & 1) return i; + } + return 32; +} + +#endif + +//------------------------------------------------------------------------------ +// Pixel copying. + +struct WebPPicture; + +// Copy width x height pixels from 'src' to 'dst' honoring the strides. +WEBP_EXTERN void WebPCopyPlane(const uint8_t* src, int src_stride, + uint8_t* dst, int dst_stride, + int width, int height); + +// Copy ARGB pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are +// assumed to be already allocated and using ARGB data. +WEBP_EXTERN void WebPCopyPixels(const struct WebPPicture* const src, + struct WebPPicture* const dst); + +//------------------------------------------------------------------------------ +// Unique colors. + +// Returns count of unique colors in 'pic', assuming pic->use_argb is true. +// 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_PALETTE_SIZE, also outputs the actual unique colors into 'palette'. +// Note: 'palette' is assumed to be an array already allocated with at least +// MAX_PALETTE_SIZE elements. +WEBP_EXTERN int WebPGetColorPalette(const struct WebPPicture* const pic, + uint32_t* const palette); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_UTILS_UTILS_H_ diff --git a/internal/libwebp-1.2.1/src/webp/decode.h b/internal/libwebp-1.2.1/src/webp/decode.h new file mode 100644 index 0000000..44fcd64 --- /dev/null +++ b/internal/libwebp-1.2.1/src/webp/decode.h @@ -0,0 +1,503 @@ +// 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) + +#ifndef WEBP_WEBP_DECODE_H_ +#define WEBP_WEBP_DECODE_H_ + +#include "./types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WEBP_DECODER_ABI_VERSION 0x0209 // MAJOR(8b) + MINOR(8b) + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum VP8StatusCode VP8StatusCode; +// typedef enum WEBP_CSP_MODE WEBP_CSP_MODE; +typedef struct WebPRGBABuffer WebPRGBABuffer; +typedef struct WebPYUVABuffer WebPYUVABuffer; +typedef struct WebPDecBuffer WebPDecBuffer; +typedef struct WebPIDecoder WebPIDecoder; +typedef struct WebPBitstreamFeatures WebPBitstreamFeatures; +typedef struct WebPDecoderOptions WebPDecoderOptions; +typedef struct WebPDecoderConfig WebPDecoderConfig; + +// Return the decoder's version number, packed in hexadecimal using 8bits for +// each of major/minor/revision. E.g: v2.5.7 is 0x020507. +WEBP_EXTERN int WebPGetDecoderVersion(void); + +// Retrieve basic header information: width, height. +// This function will also validate the header, returning true on success, +// false otherwise. '*width' and '*height' are only valid on successful return. +// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant. +// 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. +WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Decodes WebP images pointed to by 'data' and returns RGBA samples, along +// with the dimensions in *width and *height. The ordering of samples in +// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent). +// The returned pointer should be deleted calling WebPFree(). +// Returns NULL in case of error. +WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data. +WEBP_EXTERN uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data. +WEBP_EXTERN uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data. +// If the bitstream contains transparency, it is ignored. +WEBP_EXTERN uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, + int* width, int* height); + +// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data. +WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, + int* width, int* height); + + +// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer +// returned is the Y samples buffer. Upon return, *u and *v will point to +// the U and V chroma data. These U and V buffers need NOT be passed to +// WebPFree(), unlike the returned Y luma one. The dimension of the U and V +// planes are both (*width + 1) / 2 and (*height + 1)/ 2. +// Upon return, the Y buffer has a stride returned as '*stride', while U and V +// have a common stride returned as '*uv_stride'. +// Return NULL in case of error. +// (*) Also named Y'CbCr. See: http://en.wikipedia.org/wiki/YCbCr +WEBP_EXTERN 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); + +// These five functions are variants of the above ones, that decode the image +// directly into a pre-allocated buffer 'output_buffer'. The maximum storage +// available in this buffer is indicated by 'output_buffer_size'. If this +// storage is not sufficient (or an error occurred), NULL is returned. +// Otherwise, output_buffer is returned, for convenience. +// The parameter 'output_stride' specifies the distance (in bytes) +// between scanlines. Hence, output_buffer_size is expected to be at least +// output_stride x picture-height. +WEBP_EXTERN uint8_t* WebPDecodeRGBAInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); +WEBP_EXTERN uint8_t* WebPDecodeARGBInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); +WEBP_EXTERN uint8_t* WebPDecodeBGRAInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); + +// RGB and BGR variants. Here too the transparency information, if present, +// will be dropped and ignored. +WEBP_EXTERN uint8_t* WebPDecodeRGBInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); +WEBP_EXTERN uint8_t* WebPDecodeBGRInto( + const uint8_t* data, size_t data_size, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); + +// WebPDecodeYUVInto() is a variant of WebPDecodeYUV() that operates directly +// into pre-allocated luma/chroma plane buffers. This function requires the +// strides to be passed: one for the luma plane and one for each of the +// chroma ones. The size of each plane buffer is passed as 'luma_size', +// 'u_size' and 'v_size' respectively. +// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred +// during decoding (or because some buffers were found to be too small). +WEBP_EXTERN 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); + +//------------------------------------------------------------------------------ +// Output colorspaces and buffer + +// Colorspaces +// Note: the naming describes the byte-ordering of packed samples in memory. +// For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,... +// Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels. +// RGBA-4444 and RGB-565 colorspaces are represented by following byte-order: +// RGBA-4444: [r3 r2 r1 r0 g3 g2 g1 g0], [b3 b2 b1 b0 a3 a2 a1 a0], ... +// RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ... +// In the case WEBP_SWAP_16BITS_CSP is defined, the bytes are swapped for +// these two modes: +// RGBA-4444: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ... +// RGB-565: [g2 g1 g0 b4 b3 b2 b1 b0], [r4 r3 r2 r1 r0 g5 g4 g3], ... + +typedef enum WEBP_CSP_MODE { + MODE_RGB = 0, MODE_RGBA = 1, + MODE_BGR = 2, MODE_BGRA = 3, + MODE_ARGB = 4, MODE_RGBA_4444 = 5, + MODE_RGB_565 = 6, + // RGB-premultiplied transparent modes (alpha value is preserved) + MODE_rgbA = 7, + MODE_bgrA = 8, + MODE_Argb = 9, + MODE_rgbA_4444 = 10, + // YUV modes must come after RGB ones. + MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0 + MODE_LAST = 13 +} WEBP_CSP_MODE; + +// Some useful macros: +static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) { + return (mode == MODE_rgbA || mode == MODE_bgrA || mode == MODE_Argb || + mode == MODE_rgbA_4444); +} + +static WEBP_INLINE int WebPIsAlphaMode(WEBP_CSP_MODE mode) { + return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB || + mode == MODE_RGBA_4444 || mode == MODE_YUVA || + WebPIsPremultipliedMode(mode)); +} + +static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) { + return (mode < MODE_YUV); +} + +//------------------------------------------------------------------------------ +// WebPDecBuffer: Generic structure for describing the output sample buffer. + +struct WebPRGBABuffer { // view as RGBA + uint8_t* rgba; // pointer to RGBA samples + int stride; // stride in bytes from one scanline to the next. + size_t size; // total size of the *rgba buffer. +}; + +struct WebPYUVABuffer { // view as YUVA + uint8_t* y, *u, *v, *a; // pointer to luma, chroma U/V, alpha samples + int y_stride; // luma stride + int u_stride, v_stride; // chroma strides + int a_stride; // alpha stride + size_t y_size; // luma plane size + size_t u_size, v_size; // chroma planes size + size_t a_size; // alpha-plane size +}; + +// Output buffer +struct WebPDecBuffer { + WEBP_CSP_MODE colorspace; // Colorspace. + int width, height; // Dimensions. + int is_external_memory; // If non-zero, 'internal_memory' pointer is not + // used. If value is '2' or more, the external + // memory is considered 'slow' and multiple + // read/write will be avoided. + union { + WebPRGBABuffer RGBA; + WebPYUVABuffer YUVA; + } u; // Nameless union of buffer parameters. + uint32_t pad[4]; // padding for later use + + uint8_t* private_memory; // Internally allocated memory (only when + // is_external_memory is 0). Should not be used + // externally, but accessed via the buffer union. +}; + +// Internal, version-checked, entry point +WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int); + +// Initialize the structure as empty. Must be called before any other use. +// Returns false in case of version mismatch +static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) { + return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION); +} + +// Free any memory associated with the buffer. Must always be called last. +// Note: doesn't free the 'buffer' structure itself. +WEBP_EXTERN void WebPFreeDecBuffer(WebPDecBuffer* buffer); + +//------------------------------------------------------------------------------ +// Enumeration of the status codes + +typedef enum VP8StatusCode { + VP8_STATUS_OK = 0, + VP8_STATUS_OUT_OF_MEMORY, + VP8_STATUS_INVALID_PARAM, + VP8_STATUS_BITSTREAM_ERROR, + VP8_STATUS_UNSUPPORTED_FEATURE, + VP8_STATUS_SUSPENDED, + VP8_STATUS_USER_ABORT, + VP8_STATUS_NOT_ENOUGH_DATA +} VP8StatusCode; + +//------------------------------------------------------------------------------ +// Incremental decoding +// +// This API allows streamlined decoding of partial data. +// Picture can be incrementally decoded as data become available thanks to the +// WebPIDecoder object. This object can be left in a SUSPENDED state if the +// picture is only partially decoded, pending additional input. +// Code example: +// +// WebPInitDecBuffer(&output_buffer); +// output_buffer.colorspace = mode; +// ... +// WebPIDecoder* idec = WebPINewDecoder(&output_buffer); +// while (additional_data_is_available) { +// // ... (get additional data in some new_data[] buffer) +// status = WebPIAppend(idec, new_data, new_data_size); +// if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) { +// break; // an error occurred. +// } +// +// // The above call decodes the current available buffer. +// // Part of the image can now be refreshed by calling +// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc. +// } +// WebPIDelete(idec); + +// Creates a new incremental decoder with the supplied buffer parameter. +// This output_buffer can be passed NULL, in which case a default output buffer +// is used (with MODE_RGB). Otherwise, an internal reference to 'output_buffer' +// is kept, which means that the lifespan of 'output_buffer' must be larger than +// that of the returned WebPIDecoder object. +// The supplied 'output_buffer' content MUST NOT be changed between calls to +// WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is +// not set to 0. In such a case, it is allowed to modify the pointers, size and +// stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain +// within valid bounds. +// All other fields of WebPDecBuffer MUST remain constant between calls. +// Returns NULL if the allocation failed. +WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer); + +// This function allocates and initializes an incremental-decoder object, which +// will output the RGB/A samples specified by 'csp' into a preallocated +// buffer 'output_buffer'. The size of this buffer is at least +// 'output_buffer_size' and the stride (distance in bytes between two scanlines) +// is specified by 'output_stride'. +// Additionally, output_buffer can be passed NULL in which case the output +// buffer will be allocated automatically when the decoding starts. The +// colorspace 'csp' is taken into account for allocating this buffer. All other +// parameters are ignored. +// Returns NULL if the allocation failed, or if some parameters are invalid. +WEBP_EXTERN WebPIDecoder* WebPINewRGB( + WEBP_CSP_MODE csp, + uint8_t* output_buffer, size_t output_buffer_size, int output_stride); + +// This function allocates and initializes an incremental-decoder object, which +// will output the raw luma/chroma samples into a preallocated planes if +// supplied. The luma plane is specified by its pointer 'luma', its size +// 'luma_size' and its stride 'luma_stride'. Similarly, the chroma-u plane +// is specified by the 'u', 'u_size' and 'u_stride' parameters, and the chroma-v +// plane by 'v' and 'v_size'. And same for the alpha-plane. The 'a' pointer +// can be pass NULL in case one is not interested in the transparency plane. +// Conversely, 'luma' can be passed NULL if no preallocated planes are supplied. +// In this case, the output buffer will be automatically allocated (using +// MODE_YUVA) when decoding starts. All parameters are then ignored. +// Returns NULL if the allocation failed or if a parameter is invalid. +WEBP_EXTERN 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); + +// Deprecated version of the above, without the alpha plane. +// Kept for backward compatibility. +WEBP_EXTERN 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); + +// Deletes the WebPIDecoder object and associated memory. Must always be called +// if WebPINewDecoder, WebPINewRGB or WebPINewYUV succeeded. +WEBP_EXTERN void WebPIDelete(WebPIDecoder* idec); + +// Copies and decodes the next available data. Returns VP8_STATUS_OK when +// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more +// data is expected. Returns error in other cases. +WEBP_EXTERN VP8StatusCode WebPIAppend( + WebPIDecoder* idec, const uint8_t* data, size_t data_size); + +// A variant of the above function to be used when data buffer contains +// partial data from the beginning. In this case data buffer is not copied +// to the internal memory. +// Note that the value of the 'data' pointer can change between calls to +// WebPIUpdate, for instance when the data buffer is resized to fit larger data. +WEBP_EXTERN VP8StatusCode WebPIUpdate( + WebPIDecoder* idec, const uint8_t* data, size_t data_size); + +// Returns the RGB/A image decoded so far. Returns NULL if output params +// are not initialized yet. The RGB/A output type corresponds to the colorspace +// specified during call to WebPINewDecoder() or WebPINewRGB(). +// *last_y is the index of last decoded row in raster scan order. Some pointers +// (*last_y, *width etc.) can be NULL if corresponding information is not +// needed. The values in these pointers are only valid on successful (non-NULL) +// return. +WEBP_EXTERN uint8_t* WebPIDecGetRGB( + const WebPIDecoder* idec, int* last_y, + int* width, int* height, int* stride); + +// Same as above function to get a YUVA image. Returns pointer to the luma +// plane or NULL in case of error. If there is no alpha information +// the alpha pointer '*a' will be returned NULL. +WEBP_EXTERN 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); + +// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the +// alpha information (if present). Kept for backward compatibility. +static WEBP_INLINE uint8_t* WebPIDecGetYUV( + const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v, + int* width, int* height, int* stride, int* uv_stride) { + return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height, + stride, uv_stride, NULL); +} + +// Generic call to retrieve information about the displayable area. +// If non NULL, the left/right/width/height pointers are filled with the visible +// rectangular area so far. +// Returns NULL in case the incremental decoder object is in an invalid state. +// Otherwise returns the pointer to the internal representation. This structure +// is read-only, tied to WebPIDecoder's lifespan and should not be modified. +WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea( + const WebPIDecoder* idec, int* left, int* top, int* width, int* height); + +//------------------------------------------------------------------------------ +// Advanced decoding parametrization +// +// Code sample for using the advanced decoding API +/* + // A) Init a configuration object + WebPDecoderConfig config; + CHECK(WebPInitDecoderConfig(&config)); + + // B) optional: retrieve the bitstream's features. + CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK); + + // C) Adjust 'config', if needed + config.no_fancy_upsampling = 1; + config.output.colorspace = MODE_BGRA; + // etc. + + // Note that you can also make config.output point to an externally + // supplied memory buffer, provided it's big enough to store the decoded + // picture. Otherwise, config.output will just be used to allocate memory + // and store the decoded picture. + + // D) Decode! + CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK); + + // E) Decoded image is now in config.output (and config.output.u.RGBA) + + // F) Reclaim memory allocated in config's object. It's safe to call + // this function even if the memory is external and wasn't allocated + // by WebPDecode(). + WebPFreeDecBuffer(&config.output); +*/ + +// Features gathered from the bitstream +struct WebPBitstreamFeatures { + int width; // Width in pixels, as read from the bitstream. + int height; // Height in pixels, as read from the bitstream. + int has_alpha; // True if the bitstream contains an alpha channel. + int has_animation; // True if the bitstream is an animation. + int format; // 0 = undefined (/mixed), 1 = lossy, 2 = lossless + + uint32_t pad[5]; // padding for later use +}; + +// Internal, version-checked, entry point +WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal( + const uint8_t*, size_t, WebPBitstreamFeatures*, int); + +// Retrieve features from the bitstream. The *features structure is filled +// with information gathered from the bitstream. +// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns +// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the +// features from headers. Returns error in other cases. +// 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 WEBP_INLINE VP8StatusCode WebPGetFeatures( + const uint8_t* data, size_t data_size, + WebPBitstreamFeatures* features) { + return WebPGetFeaturesInternal(data, data_size, features, + WEBP_DECODER_ABI_VERSION); +} + +// Decoding options +struct WebPDecoderOptions { + int bypass_filtering; // if true, skip the in-loop filtering + int no_fancy_upsampling; // if true, use faster pointwise upsampler + int use_cropping; // if true, cropping is applied _first_ + int crop_left, crop_top; // top-left position for cropping. + // Will be snapped to even values. + int crop_width, crop_height; // dimension of the cropping area + int use_scaling; // if true, scaling is applied _afterward_ + int scaled_width, scaled_height; // final resolution + int use_threads; // if true, use multi-threaded decoding + int dithering_strength; // dithering strength (0=Off, 100=full) + int flip; // if true, flip output vertically + int alpha_dithering_strength; // alpha dithering strength in [0..100] + + uint32_t pad[5]; // padding for later use +}; + +// Main object storing the configuration for advanced decoding. +struct WebPDecoderConfig { + WebPBitstreamFeatures input; // Immutable bitstream features (optional) + WebPDecBuffer output; // Output buffer (can point to external mem) + WebPDecoderOptions options; // Decoding options +}; + +// Internal, version-checked, entry point +WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*, int); + +// Initialize the configuration as empty. This function must always be +// called first, unless WebPGetFeatures() is to be called. +// Returns false in case of mismatched version. +static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) { + return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION); +} + +// Instantiate a new incremental decoder object with the requested +// configuration. The bitstream can be passed using 'data' and 'data_size' +// parameter, in which case the features will be parsed and stored into +// config->input. Otherwise, 'data' can be NULL and no parsing will occur. +// Note that 'config' can be NULL too, in which case a default configuration +// is used. If 'config' is not NULL, it must outlive the WebPIDecoder object +// as some references to its fields will be used. No internal copy of 'config' +// is made. +// The return WebPIDecoder object must always be deleted calling WebPIDelete(). +// Returns NULL in case of error (and config->status will then reflect +// the error condition, if available). +WEBP_EXTERN WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, + WebPDecoderConfig* config); + +// Non-incremental version. This version decodes the full data at once, taking +// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK +// if the decoding was successful). Note that 'config' cannot be NULL. +WEBP_EXTERN VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, + WebPDecoderConfig* config); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_DECODE_H_ diff --git a/internal/libwebp-1.2.1/src/webp/demux.h b/internal/libwebp-1.2.1/src/webp/demux.h new file mode 100644 index 0000000..846eeb1 --- /dev/null +++ b/internal/libwebp-1.2.1/src/webp/demux.h @@ -0,0 +1,363 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Demux API. +// Enables extraction of image and extended format data from WebP files. + +// Code Example: Demuxing WebP data to extract all the frames, ICC profile +// and EXIF/XMP metadata. +/* + WebPDemuxer* demux = WebPDemux(&webp_data); + + uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); + uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); + // ... (Get information about the features present in the WebP file). + uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); + + // ... (Iterate over all frames). + WebPIterator iter; + if (WebPDemuxGetFrame(demux, 1, &iter)) { + do { + // ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(), + // ... and get other frame properties like width, height, offsets etc. + // ... see 'struct WebPIterator' below for more info). + } while (WebPDemuxNextFrame(&iter)); + WebPDemuxReleaseIterator(&iter); + } + + // ... (Extract metadata). + WebPChunkIterator chunk_iter; + if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter); + // ... (Consume the ICC profile in 'chunk_iter.chunk'). + WebPDemuxReleaseChunkIterator(&chunk_iter); + if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter); + // ... (Consume the EXIF metadata in 'chunk_iter.chunk'). + WebPDemuxReleaseChunkIterator(&chunk_iter); + if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter); + // ... (Consume the XMP metadata in 'chunk_iter.chunk'). + WebPDemuxReleaseChunkIterator(&chunk_iter); + WebPDemuxDelete(demux); +*/ + +#ifndef WEBP_WEBP_DEMUX_H_ +#define WEBP_WEBP_DEMUX_H_ + +#include "./decode.h" // for WEBP_CSP_MODE +#include "./mux_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WEBP_DEMUX_ABI_VERSION 0x0107 // MAJOR(8b) + MINOR(8b) + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum WebPDemuxState WebPDemuxState; +// typedef enum WebPFormatFeature WebPFormatFeature; +typedef struct WebPDemuxer WebPDemuxer; +typedef struct WebPIterator WebPIterator; +typedef struct WebPChunkIterator WebPChunkIterator; +typedef struct WebPAnimInfo WebPAnimInfo; +typedef struct WebPAnimDecoderOptions WebPAnimDecoderOptions; + +//------------------------------------------------------------------------------ + +// Returns the version number of the demux library, packed in hexadecimal using +// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507. +WEBP_EXTERN int WebPGetDemuxVersion(void); + +//------------------------------------------------------------------------------ +// Life of a Demux object + +typedef enum WebPDemuxState { + WEBP_DEMUX_PARSE_ERROR = -1, // An error occurred while parsing. + WEBP_DEMUX_PARSING_HEADER = 0, // Not enough data to parse full header. + WEBP_DEMUX_PARSED_HEADER = 1, // Header parsing complete, + // data may be available. + WEBP_DEMUX_DONE = 2 // Entire file has been parsed. +} WebPDemuxState; + +// Internal, version-checked, entry point +WEBP_EXTERN WebPDemuxer* WebPDemuxInternal( + const WebPData*, int, WebPDemuxState*, int); + +// Parses the full WebP file given by 'data'. For single images the WebP file +// header alone or the file header and the chunk header may be absent. +// Returns a WebPDemuxer object on successful parse, NULL otherwise. +static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) { + return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION); +} + +// Parses the possibly incomplete WebP file given by 'data'. +// If 'state' is non-NULL it will be set to indicate the status of the demuxer. +// Returns NULL in case of error or if there isn't enough data to start parsing; +// and a WebPDemuxer object on successful parse. +// Note that WebPDemuxer keeps internal pointers to 'data' memory segment. +// If this data is volatile, the demuxer object should be deleted (by calling +// WebPDemuxDelete()) and WebPDemuxPartial() called again on the new data. +// This is usually an inexpensive operation. +static WEBP_INLINE WebPDemuxer* WebPDemuxPartial( + const WebPData* data, WebPDemuxState* state) { + return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION); +} + +// Frees memory associated with 'dmux'. +WEBP_EXTERN void WebPDemuxDelete(WebPDemuxer* dmux); + +//------------------------------------------------------------------------------ +// Data/information extraction. + +typedef enum WebPFormatFeature { + WEBP_FF_FORMAT_FLAGS, // bit-wise combination of WebPFeatureFlags + // corresponding to the 'VP8X' chunk (if present). + WEBP_FF_CANVAS_WIDTH, + WEBP_FF_CANVAS_HEIGHT, + WEBP_FF_LOOP_COUNT, // only relevant for animated file + WEBP_FF_BACKGROUND_COLOR, // idem. + WEBP_FF_FRAME_COUNT // Number of frames present in the demux object. + // In case of a partial demux, this is the number + // of frames seen so far, with the last frame + // possibly being partial. +} WebPFormatFeature; + +// Get the 'feature' value from the 'dmux'. +// NOTE: values are only valid if WebPDemux() was used or WebPDemuxPartial() +// returned a state > WEBP_DEMUX_PARSING_HEADER. +// If 'feature' is WEBP_FF_FORMAT_FLAGS, the returned value is a bit-wise +// combination of WebPFeatureFlags values. +// If 'feature' is WEBP_FF_LOOP_COUNT, WEBP_FF_BACKGROUND_COLOR, the returned +// value is only meaningful if the bitstream is animated. +WEBP_EXTERN uint32_t WebPDemuxGetI( + const WebPDemuxer* dmux, WebPFormatFeature feature); + +//------------------------------------------------------------------------------ +// Frame iteration. + +struct WebPIterator { + int frame_num; + int num_frames; // equivalent to WEBP_FF_FRAME_COUNT. + int x_offset, y_offset; // offset relative to the canvas. + int width, height; // dimensions of this frame. + int duration; // display duration in milliseconds. + WebPMuxAnimDispose dispose_method; // dispose method for the frame. + int complete; // true if 'fragment' contains a full frame. partial images + // may still be decoded with the WebP incremental decoder. + WebPData fragment; // The frame given by 'frame_num'. Note for historical + // reasons this is called a fragment. + int has_alpha; // True if the frame contains transparency. + WebPMuxAnimBlend blend_method; // Blend operation for the frame. + + uint32_t pad[2]; // padding for later use. + void* private_; // for internal use only. +}; + +// Retrieves frame 'frame_number' from 'dmux'. +// 'iter->fragment' points to the frame on return from this function. +// Setting 'frame_number' equal to 0 will return the last frame of the image. +// Returns false if 'dmux' is NULL or frame 'frame_number' is not present. +// Call WebPDemuxReleaseIterator() when use of the iterator is complete. +// NOTE: 'dmux' must persist for the lifetime of 'iter'. +WEBP_EXTERN int WebPDemuxGetFrame( + const WebPDemuxer* dmux, int frame_number, WebPIterator* iter); + +// Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or +// previous ('iter->frame_num' - 1) frame. These functions do not loop. +// Returns true on success, false otherwise. +WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter); +WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter); + +// Releases any memory associated with 'iter'. +// Must be called before any subsequent calls to WebPDemuxGetChunk() on the same +// iter. Also, must be called before destroying the associated WebPDemuxer with +// WebPDemuxDelete(). +WEBP_EXTERN void WebPDemuxReleaseIterator(WebPIterator* iter); + +//------------------------------------------------------------------------------ +// Chunk iteration. + +struct WebPChunkIterator { + // The current and total number of chunks with the fourcc given to + // WebPDemuxGetChunk(). + int chunk_num; + int num_chunks; + WebPData chunk; // The payload of the chunk. + + uint32_t pad[6]; // padding for later use + void* private_; +}; + +// Retrieves the 'chunk_number' instance of the chunk with id 'fourcc' from +// 'dmux'. +// 'fourcc' is a character array containing the fourcc of the chunk to return, +// e.g., "ICCP", "XMP ", "EXIF", etc. +// Setting 'chunk_number' equal to 0 will return the last chunk in a set. +// Returns true if the chunk is found, false otherwise. Image related chunk +// payloads are accessed through WebPDemuxGetFrame() and related functions. +// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete. +// NOTE: 'dmux' must persist for the lifetime of the iterator. +WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux, + const char fourcc[4], int chunk_number, + WebPChunkIterator* iter); + +// Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous +// ('iter->chunk_num' - 1) chunk. These functions do not loop. +// Returns true on success, false otherwise. +WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter); +WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter); + +// Releases any memory associated with 'iter'. +// Must be called before destroying the associated WebPDemuxer with +// WebPDemuxDelete(). +WEBP_EXTERN void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter); + +//------------------------------------------------------------------------------ +// WebPAnimDecoder API +// +// This API allows decoding (possibly) animated WebP images. +// +// Code Example: +/* + WebPAnimDecoderOptions dec_options; + WebPAnimDecoderOptionsInit(&dec_options); + // Tune 'dec_options' as needed. + WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options); + WebPAnimInfo anim_info; + WebPAnimDecoderGetInfo(dec, &anim_info); + for (uint32_t i = 0; i < anim_info.loop_count; ++i) { + while (WebPAnimDecoderHasMoreFrames(dec)) { + uint8_t* buf; + int timestamp; + WebPAnimDecoderGetNext(dec, &buf, ×tamp); + // ... (Render 'buf' based on 'timestamp'). + // ... (Do NOT free 'buf', as it is owned by 'dec'). + } + WebPAnimDecoderReset(dec); + } + const WebPDemuxer* demuxer = WebPAnimDecoderGetDemuxer(dec); + // ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data). + WebPAnimDecoderDelete(dec); +*/ + +typedef struct WebPAnimDecoder WebPAnimDecoder; // Main opaque object. + +// Global options. +struct WebPAnimDecoderOptions { + // Output colorspace. Only the following modes are supported: + // MODE_RGBA, MODE_BGRA, MODE_rgbA and MODE_bgrA. + WEBP_CSP_MODE color_mode; + int use_threads; // If true, use multi-threaded decoding. + uint32_t padding[7]; // Padding for later use. +}; + +// Internal, version-checked, entry point. +WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal( + WebPAnimDecoderOptions*, int); + +// Should always be called, to initialize a fresh WebPAnimDecoderOptions +// structure before modification. Returns false in case of version mismatch. +// WebPAnimDecoderOptionsInit() must have succeeded before using the +// 'dec_options' object. +static WEBP_INLINE int WebPAnimDecoderOptionsInit( + WebPAnimDecoderOptions* dec_options) { + return WebPAnimDecoderOptionsInitInternal(dec_options, + WEBP_DEMUX_ABI_VERSION); +} + +// Internal, version-checked, entry point. +WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal( + const WebPData*, const WebPAnimDecoderOptions*, int); + +// Creates and initializes a WebPAnimDecoder object. +// Parameters: +// webp_data - (in) WebP bitstream. This should remain unchanged during the +// lifetime of the output WebPAnimDecoder object. +// dec_options - (in) decoding options. Can be passed NULL to choose +// reasonable defaults (in particular, color mode MODE_RGBA +// will be picked). +// Returns: +// A pointer to the newly created WebPAnimDecoder object, or NULL in case of +// parsing error, invalid option or memory error. +static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew( + const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) { + return WebPAnimDecoderNewInternal(webp_data, dec_options, + WEBP_DEMUX_ABI_VERSION); +} + +// Global information about the animation.. +struct WebPAnimInfo { + uint32_t canvas_width; + uint32_t canvas_height; + uint32_t loop_count; + uint32_t bgcolor; + uint32_t frame_count; + uint32_t pad[4]; // padding for later use +}; + +// Get global information about the animation. +// Parameters: +// dec - (in) decoder instance to get information from. +// info - (out) global information fetched from the animation. +// Returns: +// True on success. +WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, + WebPAnimInfo* info); + +// Fetch the next frame from 'dec' based on options supplied to +// WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size +// 'canvas_width * 4 * canvas_height', and not just the frame sub-rectangle. The +// returned buffer 'buf' is valid only until the next call to +// WebPAnimDecoderGetNext(), WebPAnimDecoderReset() or WebPAnimDecoderDelete(). +// Parameters: +// dec - (in/out) decoder instance from which the next frame is to be fetched. +// buf - (out) decoded frame. +// timestamp - (out) timestamp of the frame in milliseconds. +// Returns: +// False if any of the arguments are NULL, or if there is a parsing or +// decoding error, or if there are no more frames. Otherwise, returns true. +WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, + uint8_t** buf, int* timestamp); + +// Check if there are more frames left to decode. +// Parameters: +// dec - (in) decoder instance to be checked. +// Returns: +// True if 'dec' is not NULL and some frames are yet to be decoded. +// Otherwise, returns false. +WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec); + +// Resets the WebPAnimDecoder object, so that next call to +// WebPAnimDecoderGetNext() will restart decoding from 1st frame. This would be +// helpful when all frames need to be decoded multiple times (e.g. +// info.loop_count times) without destroying and recreating the 'dec' object. +// Parameters: +// dec - (in/out) decoder instance to be reset +WEBP_EXTERN void WebPAnimDecoderReset(WebPAnimDecoder* dec); + +// Grab the internal demuxer object. +// Getting the demuxer object can be useful if one wants to use operations only +// available through demuxer; e.g. to get XMP/EXIF/ICC metadata. The returned +// demuxer object is owned by 'dec' and is valid only until the next call to +// WebPAnimDecoderDelete(). +// +// Parameters: +// dec - (in) decoder instance from which the demuxer object is to be fetched. +WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer( + const WebPAnimDecoder* dec); + +// Deletes the WebPAnimDecoder object. +// Parameters: +// dec - (in/out) decoder instance to be deleted +WEBP_EXTERN void WebPAnimDecoderDelete(WebPAnimDecoder* dec); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_DEMUX_H_ diff --git a/internal/libwebp-1.2.1/src/webp/encode.h b/internal/libwebp-1.2.1/src/webp/encode.h new file mode 100644 index 0000000..b4c599d --- /dev/null +++ b/internal/libwebp-1.2.1/src/webp/encode.h @@ -0,0 +1,552 @@ +// 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 interface +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_WEBP_ENCODE_H_ +#define WEBP_WEBP_ENCODE_H_ + +#include "./types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WEBP_ENCODER_ABI_VERSION 0x020f // MAJOR(8b) + MINOR(8b) + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum WebPImageHint WebPImageHint; +// typedef enum WebPEncCSP WebPEncCSP; +// typedef enum WebPPreset WebPPreset; +// typedef enum WebPEncodingError WebPEncodingError; +typedef struct WebPConfig WebPConfig; +typedef struct WebPPicture WebPPicture; // main structure for I/O +typedef struct WebPAuxStats WebPAuxStats; +typedef struct WebPMemoryWriter WebPMemoryWriter; + +// Return the encoder's version number, packed in hexadecimal using 8bits for +// each of major/minor/revision. E.g: v2.5.7 is 0x020507. +WEBP_EXTERN int WebPGetEncoderVersion(void); + +//------------------------------------------------------------------------------ +// One-stop-shop call! No questions asked: + +// Returns the size of the compressed data (pointed to by *output), or 0 if +// an error occurred. The compressed data must be released by the caller +// using the call 'WebPFree(*output)'. +// These functions compress using the lossy format, and the quality_factor +// can go from 0 (smaller output, lower quality) to 100 (best quality, +// larger output). +WEBP_EXTERN size_t WebPEncodeRGB(const uint8_t* rgb, + int width, int height, int stride, + float quality_factor, uint8_t** output); +WEBP_EXTERN size_t WebPEncodeBGR(const uint8_t* bgr, + int width, int height, int stride, + float quality_factor, uint8_t** output); +WEBP_EXTERN size_t WebPEncodeRGBA(const uint8_t* rgba, + int width, int height, int stride, + float quality_factor, uint8_t** output); +WEBP_EXTERN size_t WebPEncodeBGRA(const uint8_t* bgra, + int width, int height, int stride, + float quality_factor, uint8_t** output); + +// These functions are the equivalent of the above, but compressing in a +// lossless manner. Files are usually larger than lossy format, but will +// not suffer any compression loss. +// Note these functions, like the lossy versions, use the library's default +// settings. For lossless this means 'exact' is disabled. RGB values in +// transparent areas will be modified to improve compression. To avoid this, +// use WebPEncode() and set WebPConfig::exact to 1. +WEBP_EXTERN size_t WebPEncodeLosslessRGB(const uint8_t* rgb, + int width, int height, int stride, + uint8_t** output); +WEBP_EXTERN size_t WebPEncodeLosslessBGR(const uint8_t* bgr, + int width, int height, int stride, + uint8_t** output); +WEBP_EXTERN size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, + int width, int height, int stride, + uint8_t** output); +WEBP_EXTERN size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, + int width, int height, int stride, + uint8_t** output); + +//------------------------------------------------------------------------------ +// Coding parameters + +// Image characteristics hint for the underlying encoder. +typedef enum WebPImageHint { + WEBP_HINT_DEFAULT = 0, // default preset. + WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot + WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting + WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc). + WEBP_HINT_LAST +} WebPImageHint; + +// Compression parameters. +struct WebPConfig { + int lossless; // Lossless encoding (0=lossy(default), 1=lossless). + float quality; // between 0 and 100. For lossy, 0 gives the smallest + // size and 100 the largest. For lossless, this + // parameter is the amount of effort put into the + // compression: 0 is the fastest but gives larger + // files compared to the slowest, but best, 100. + int method; // quality/speed trade-off (0=fast, 6=slower-better) + + WebPImageHint image_hint; // Hint for image type (lossless only for now). + + int target_size; // if non-zero, set the desired target size in bytes. + // Takes precedence over the 'compression' parameter. + float target_PSNR; // if non-zero, specifies the minimal distortion to + // try to achieve. Takes precedence over target_size. + int segments; // maximum number of segments to use, in [1..4] + int sns_strength; // Spatial Noise Shaping. 0=off, 100=maximum. + int filter_strength; // range: [0 = off .. 100 = strongest] + int filter_sharpness; // range: [0 = off .. 7 = least sharp] + int filter_type; // filtering type: 0 = simple, 1 = strong (only used + // if filter_strength > 0 or autofilter > 0) + int autofilter; // Auto adjust filter's strength [0 = off, 1 = on] + int alpha_compression; // Algorithm for encoding the alpha plane (0 = none, + // 1 = compressed with WebP lossless). Default is 1. + int alpha_filtering; // Predictive filtering method for alpha plane. + // 0: none, 1: fast, 2: best. Default if 1. + int alpha_quality; // Between 0 (smallest size) and 100 (lossless). + // Default is 100. + int pass; // number of entropy-analysis passes (in [1..10]). + + int show_compressed; // if true, export the compressed picture back. + // In-loop filtering is not applied. + int preprocessing; // preprocessing filter: + // 0=none, 1=segment-smooth, 2=pseudo-random dithering + int partitions; // log2(number of token partitions) in [0..3]. Default + // is set to 0 for easier progressive decoding. + int partition_limit; // quality degradation allowed to fit the 512k limit + // on prediction modes coding (0: no degradation, + // 100: maximum possible degradation). + int emulate_jpeg_size; // If true, compression parameters will be remapped + // to better match the expected output size from + // JPEG compression. Generally, the output size will + // be similar but the degradation will be lower. + int thread_level; // If non-zero, try and use multi-threaded encoding. + int low_memory; // If set, reduce memory usage (but increase CPU use). + + int near_lossless; // Near lossless encoding [0 = max loss .. 100 = off + // (default)]. + int exact; // if non-zero, preserve the exact RGB values under + // transparent area. Otherwise, discard this invisible + // RGB information for better compression. The default + // value is 0. + + int use_delta_palette; // reserved for future lossless feature + int use_sharp_yuv; // if needed, use sharp (and slow) RGB->YUV conversion + + int qmin; // minimum permissible quality factor + int qmax; // maximum permissible quality factor +}; + +// Enumerate some predefined settings for WebPConfig, depending on the type +// of source picture. These presets are used when calling WebPConfigPreset(). +typedef enum WebPPreset { + WEBP_PRESET_DEFAULT = 0, // default preset. + WEBP_PRESET_PICTURE, // digital picture, like portrait, inner shot + WEBP_PRESET_PHOTO, // outdoor photograph, with natural lighting + WEBP_PRESET_DRAWING, // hand or line drawing, with high-contrast details + WEBP_PRESET_ICON, // small-sized colorful images + WEBP_PRESET_TEXT // text-like +} WebPPreset; + +// Internal, version-checked, entry point +WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int); + +// Should always be called, to initialize a fresh WebPConfig structure before +// modification. Returns false in case of version mismatch. WebPConfigInit() +// must have succeeded before using the 'config' object. +// Note that the default values are lossless=0 and quality=75. +static WEBP_INLINE int WebPConfigInit(WebPConfig* config) { + return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f, + WEBP_ENCODER_ABI_VERSION); +} + +// This function will initialize the configuration according to a predefined +// set of parameters (referred to by 'preset') and a given quality factor. +// This function can be called as a replacement to WebPConfigInit(). Will +// return false in case of error. +static WEBP_INLINE int WebPConfigPreset(WebPConfig* config, + WebPPreset preset, float quality) { + return WebPConfigInitInternal(config, preset, quality, + WEBP_ENCODER_ABI_VERSION); +} + +// Activate the lossless compression mode with the desired efficiency level +// between 0 (fastest, lowest compression) and 9 (slower, best compression). +// A good default level is '6', providing a fair tradeoff between compression +// speed and final compressed size. +// This function will overwrite several fields from config: 'method', 'quality' +// and 'lossless'. Returns false in case of parameter error. +WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config, int level); + +// Returns true if 'config' is non-NULL and all configuration parameters are +// within their valid ranges. +WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config); + +//------------------------------------------------------------------------------ +// Input / Output +// Structure for storing auxiliary statistics. + +struct WebPAuxStats { + int coded_size; // final size + + float PSNR[5]; // peak-signal-to-noise ratio for Y/U/V/All/Alpha + int block_count[3]; // number of intra4/intra16/skipped macroblocks + int header_bytes[2]; // approximate number of bytes spent for header + // and mode-partition #0 + int residual_bytes[3][4]; // approximate number of bytes spent for + // DC/AC/uv coefficients for each (0..3) segments. + int segment_size[4]; // number of macroblocks in each segments + int segment_quant[4]; // quantizer values for each segments + int segment_level[4]; // filtering strength for each segments [0..63] + + int alpha_data_size; // size of the transparency data + int layer_data_size; // size of the enhancement layer data + + // lossless encoder statistics + uint32_t lossless_features; // bit0:predictor bit1:cross-color transform + // bit2:subtract-green bit3:color indexing + int histogram_bits; // number of precision bits of histogram + int transform_bits; // precision bits for transform + int cache_bits; // number of bits for color cache lookup + int palette_size; // number of color in palette, if used + int lossless_size; // final lossless size + int lossless_hdr_size; // lossless header (transform, huffman etc) size + int lossless_data_size; // lossless image data size + + uint32_t pad[2]; // padding for later use +}; + +// Signature for output function. Should return true if writing was successful. +// data/data_size is the segment of data to write, and 'picture' is for +// reference (and so one can make use of picture->custom_ptr). +typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size, + const WebPPicture* picture); + +// WebPMemoryWrite: a special WebPWriterFunction that writes to memory using +// the following WebPMemoryWriter object (to be set as a custom_ptr). +struct WebPMemoryWriter { + uint8_t* mem; // final buffer (of size 'max_size', larger than 'size'). + size_t size; // final size + size_t max_size; // total capacity + uint32_t pad[1]; // padding for later use +}; + +// The following must be called first before any use. +WEBP_EXTERN void WebPMemoryWriterInit(WebPMemoryWriter* writer); + +// The following must be called to deallocate writer->mem memory. The 'writer' +// object itself is not deallocated. +WEBP_EXTERN void WebPMemoryWriterClear(WebPMemoryWriter* writer); +// The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon +// completion, writer.mem and writer.size will hold the coded data. +// writer.mem must be freed by calling WebPMemoryWriterClear. +WEBP_EXTERN int WebPMemoryWrite(const uint8_t* data, size_t data_size, + const WebPPicture* picture); + +// Progress hook, called from time to time to report progress. It can return +// false to request an abort of the encoding process, or true otherwise if +// everything is OK. +typedef int (*WebPProgressHook)(int percent, const WebPPicture* picture); + +// Color spaces. +typedef enum WebPEncCSP { + // chroma sampling + WEBP_YUV420 = 0, // 4:2:0 + WEBP_YUV420A = 4, // alpha channel variant + WEBP_CSP_UV_MASK = 3, // bit-mask to get the UV sampling factors + WEBP_CSP_ALPHA_BIT = 4 // bit that is set if alpha is present +} WebPEncCSP; + +// Encoding error conditions. +typedef enum WebPEncodingError { + VP8_ENC_OK = 0, + VP8_ENC_ERROR_OUT_OF_MEMORY, // memory error allocating objects + VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY, // memory error while flushing bits + VP8_ENC_ERROR_NULL_PARAMETER, // a pointer parameter is NULL + VP8_ENC_ERROR_INVALID_CONFIGURATION, // configuration is invalid + VP8_ENC_ERROR_BAD_DIMENSION, // picture has invalid width/height + VP8_ENC_ERROR_PARTITION0_OVERFLOW, // partition is bigger than 512k + VP8_ENC_ERROR_PARTITION_OVERFLOW, // partition is bigger than 16M + VP8_ENC_ERROR_BAD_WRITE, // error while flushing bytes + VP8_ENC_ERROR_FILE_TOO_BIG, // file is bigger than 4G + VP8_ENC_ERROR_USER_ABORT, // abort request by user + VP8_ENC_ERROR_LAST // list terminator. always last. +} WebPEncodingError; + +// maximum width/height allowed (inclusive), in pixels +#define WEBP_MAX_DIMENSION 16383 + +// Main exchange structure (input samples, output bytes, statistics) +// +// Once WebPPictureInit() has been called, it's ok to make all the INPUT fields +// (use_argb, y/u/v, argb, ...) point to user-owned data, even if +// WebPPictureAlloc() has been called. Depending on the value use_argb, +// it's guaranteed that either *argb or *y/*u/*v content will be kept untouched. +struct WebPPicture { + // INPUT + ////////////// + // Main flag for encoder selecting between ARGB or YUV input. + // It is recommended to use ARGB input (*argb, argb_stride) for lossless + // compression, and YUV input (*y, *u, *v, etc.) for lossy compression + // since these are the respective native colorspace for these formats. + int use_argb; + + // YUV input (mostly used for input to lossy compression) + WebPEncCSP colorspace; // colorspace: should be YUV420 for now (=Y'CbCr). + int width, height; // dimensions (less or equal to WEBP_MAX_DIMENSION) + uint8_t* y, *u, *v; // pointers to luma/chroma planes. + int y_stride, uv_stride; // luma/chroma strides. + uint8_t* a; // pointer to the alpha plane + int a_stride; // stride of the alpha plane + uint32_t pad1[2]; // padding for later use + + // ARGB input (mostly used for input to lossless compression) + uint32_t* argb; // Pointer to argb (32 bit) plane. + int argb_stride; // This is stride in pixels units, not bytes. + uint32_t pad2[3]; // padding for later use + + // OUTPUT + /////////////// + // Byte-emission hook, to store compressed bytes as they are ready. + WebPWriterFunction writer; // can be NULL + void* custom_ptr; // can be used by the writer. + + // map for extra information (only for lossy compression mode) + int extra_info_type; // 1: intra type, 2: segment, 3: quant + // 4: intra-16 prediction mode, + // 5: chroma prediction mode, + // 6: bit cost, 7: distortion + uint8_t* extra_info; // if not NULL, points to an array of size + // ((width + 15) / 16) * ((height + 15) / 16) that + // will be filled with a macroblock map, depending + // on extra_info_type. + + // STATS AND REPORTS + /////////////////////////// + // Pointer to side statistics (updated only if not NULL) + WebPAuxStats* stats; + + // Error code for the latest error encountered during encoding + WebPEncodingError error_code; + + // If not NULL, report progress during encoding. + WebPProgressHook progress_hook; + + void* user_data; // this field is free to be set to any value and + // used during callbacks (like progress-report e.g.). + + uint32_t pad3[3]; // padding for later use + + // Unused for now + uint8_t* pad4, *pad5; + uint32_t pad6[8]; // padding for later use + + // PRIVATE FIELDS + //////////////////// + void* memory_; // row chunk of memory for yuva planes + void* memory_argb_; // and for argb too. + void* pad7[2]; // padding for later use +}; + +// Internal, version-checked, entry point +WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int); + +// Should always be called, to initialize the structure. Returns false in case +// of version mismatch. WebPPictureInit() must have succeeded before using the +// 'picture' object. +// Note that, by default, use_argb is false and colorspace is WEBP_YUV420. +static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) { + return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION); +} + +//------------------------------------------------------------------------------ +// WebPPicture utils + +// Convenience allocation / deallocation based on picture->width/height: +// Allocate y/u/v buffers as per colorspace/width/height specification. +// Note! This function will free the previous buffer if needed. +// Returns false in case of memory error. +WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture); + +// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*(). +// Note that this function does _not_ free the memory used by the 'picture' +// object itself. +// Besides memory (which is reclaimed) all other fields of 'picture' are +// preserved. +WEBP_EXTERN void WebPPictureFree(WebPPicture* picture); + +// Copy the pixels of *src into *dst, using WebPPictureAlloc. Upon return, *dst +// will fully own the copied pixels (this is not a view). The 'dst' picture need +// not be initialized as its content is overwritten. +// 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 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, + int metric_type, // 0 = PSNR, 1 = SSIM, 2 = LSIM + float result[5]); + +// self-crops a picture to the rectangle defined by top/left/width/height. +// Returns false in case of memory allocation error, or if the rectangle is +// outside of the source picture. +// The rectangle for the view is defined by the top-left corner pixel +// coordinates (left, top) as well as its width and height. This rectangle +// must be fully be comprised inside the 'src' source picture. If the source +// picture uses the YUV420 colorspace, the top and left coordinates will be +// snapped to even values. +WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture, + int left, int top, int width, int height); + +// Extracts a view from 'src' picture into 'dst'. The rectangle for the view +// is defined by the top-left corner pixel coordinates (left, top) as well +// as its width and height. This rectangle must be fully be comprised inside +// the 'src' source picture. If the source picture uses the YUV420 colorspace, +// the top and left coordinates will be snapped to even values. +// Picture 'src' must out-live 'dst' picture. Self-extraction of view is allowed +// ('src' equal to 'dst') as a mean of fast-cropping (but note that doing so, +// the original dimension will be lost). Picture 'dst' need not be initialized +// with WebPPictureInit() if it is different from 'src', since its content will +// be overwritten. +// Returns false in case of memory allocation error or invalid parameters. +WEBP_EXTERN int WebPPictureView(const WebPPicture* src, + int left, int top, int width, int height, + WebPPicture* dst); + +// Returns true if the 'picture' is actually a view and therefore does +// not own the memory for pixels. +WEBP_EXTERN int WebPPictureIsView(const WebPPicture* picture); + +// Rescale a picture to new dimension width x height. +// If either 'width' or 'height' (but not both) is 0 the corresponding +// dimension will be calculated preserving the aspect ratio. +// No gamma correction is applied. +// Returns false in case of error (invalid parameter or insufficient memory). +WEBP_EXTERN int WebPPictureRescale(WebPPicture* pic, int width, int height); + +// Colorspace conversion function to import RGB samples. +// Previous buffer will be free'd, if any. +// *rgb buffer should have a size of at least height * rgb_stride. +// Returns false in case of memory error. +WEBP_EXTERN int WebPPictureImportRGB( + WebPPicture* picture, const uint8_t* rgb, int rgb_stride); +// Same, but for RGBA buffer. +WEBP_EXTERN int WebPPictureImportRGBA( + WebPPicture* picture, const uint8_t* rgba, int rgba_stride); +// Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format +// input buffer ignoring the alpha channel. Avoids needing to copy the data +// to a temporary 24-bit RGB buffer to import the RGB only. +WEBP_EXTERN int WebPPictureImportRGBX( + WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride); + +// Variants of the above, but taking BGR(A|X) input. +WEBP_EXTERN int WebPPictureImportBGR( + WebPPicture* picture, const uint8_t* bgr, int bgr_stride); +WEBP_EXTERN int WebPPictureImportBGRA( + WebPPicture* picture, const uint8_t* bgra, int bgra_stride); +WEBP_EXTERN int WebPPictureImportBGRX( + WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride); + +// Converts picture->argb data to the YUV420A format. The 'colorspace' +// parameter is deprecated and should be equal to WEBP_YUV420. +// Upon return, picture->use_argb is set to false. The presence of real +// non-opaque transparent values is detected, and 'colorspace' will be +// adjusted accordingly. Note that this method is lossy. +// Returns false in case of error. +WEBP_EXTERN int WebPPictureARGBToYUVA(WebPPicture* picture, + WebPEncCSP /*colorspace = WEBP_YUV420*/); + +// Same as WebPPictureARGBToYUVA(), but the conversion is done using +// pseudo-random dithering with a strength 'dithering' between +// 0.0 (no dithering) and 1.0 (maximum dithering). This is useful +// for photographic picture. +WEBP_EXTERN int WebPPictureARGBToYUVADithered( + WebPPicture* picture, WebPEncCSP colorspace, float dithering); + +// 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 +// 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. +// The input format must be YUV_420 or YUV_420A. The conversion from YUV420 to +// ARGB incurs a small loss too. +// Note that the use of this colorspace is discouraged if one has access to the +// raw ARGB samples, since using YUV420 is comparatively lossy. +// Returns false in case of error. +WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture); + +// Helper function: given a width x height plane of RGBA or YUV(A) samples +// clean-up or smoothen the YUV or RGB samples under fully transparent area, +// to help compressibility (no guarantee, though). +WEBP_EXTERN void WebPCleanupTransparentArea(WebPPicture* picture); + +// Scan the picture 'picture' for the presence of non fully opaque alpha values. +// Returns true in such case. Otherwise returns false (indicating that the +// alpha plane can be ignored altogether e.g.). +WEBP_EXTERN int WebPPictureHasTransparency(const WebPPicture* picture); + +// Remove the transparency information (if present) by blending the color with +// the background color 'background_rgb' (specified as 24bit RGB triplet). +// After this call, all alpha values are reset to 0xff. +WEBP_EXTERN void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb); + +//------------------------------------------------------------------------------ +// Main call + +// Main encoding call, after config and picture have been initialized. +// 'picture' must be less than 16384x16384 in dimension (cf WEBP_MAX_DIMENSION), +// and the 'config' object must be a valid one. +// Returns false in case of error, true otherwise. +// In case of error, picture->error_code is updated accordingly. +// 'picture' can hold the source samples in both YUV(A) or ARGB input, depending +// on the value of 'picture->use_argb'. It is highly recommended to use +// the former for lossy encoding, and the latter for lossless encoding +// (when config.lossless is true). Automatic conversion from one format to +// another is provided but they both incur some loss. +WEBP_EXTERN int WebPEncode(const WebPConfig* config, WebPPicture* picture); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_ENCODE_H_ diff --git a/internal/libwebp-1.2.1/src/webp/format_constants.h b/internal/libwebp-1.2.1/src/webp/format_constants.h new file mode 100644 index 0000000..eca6981 --- /dev/null +++ b/internal/libwebp-1.2.1/src/webp/format_constants.h @@ -0,0 +1,87 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Internal header for constants related to WebP file format. +// +// Author: Urvang (urvang@google.com) + +#ifndef WEBP_WEBP_FORMAT_CONSTANTS_H_ +#define WEBP_WEBP_FORMAT_CONSTANTS_H_ + +// Create fourcc of the chunk from the chunk tag characters. +#define MKFOURCC(a, b, c, d) ((a) | (b) << 8 | (c) << 16 | (uint32_t)(d) << 24) + +// VP8 related constants. +#define VP8_SIGNATURE 0x9d012a // Signature in VP8 data. +#define VP8_MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition +#define VP8_MAX_PARTITION_SIZE (1 << 24) // max size for token partition +#define VP8_FRAME_HEADER_SIZE 10 // Size of the frame header within VP8 data. + +// VP8L related constants. +#define VP8L_SIGNATURE_SIZE 1 // VP8L signature size. +#define VP8L_MAGIC_BYTE 0x2f // VP8L signature byte. +#define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store + // width and height. +#define VP8L_VERSION_BITS 3 // 3 bits reserved for version. +#define VP8L_VERSION 0 // version 0 +#define VP8L_FRAME_HEADER_SIZE 5 // Size of the VP8L frame header. + +#define MAX_PALETTE_SIZE 256 +#define MAX_CACHE_BITS 11 +#define HUFFMAN_CODES_PER_META_CODE 5 +#define ARGB_BLACK 0xff000000 + +#define DEFAULT_CODE_LENGTH 8 +#define MAX_ALLOWED_CODE_LENGTH 15 + +#define NUM_LITERAL_CODES 256 +#define NUM_LENGTH_CODES 24 +#define NUM_DISTANCE_CODES 40 +#define CODE_LENGTH_CODES 19 + +#define MIN_HUFFMAN_BITS 2 // min number of Huffman bits +#define MAX_HUFFMAN_BITS 9 // max number of Huffman bits + +#define TRANSFORM_PRESENT 1 // The bit to be written when next data + // to be read is a transform. +#define NUM_TRANSFORMS 4 // Maximum number of allowed transform + // in a bitstream. +typedef enum { + PREDICTOR_TRANSFORM = 0, + CROSS_COLOR_TRANSFORM = 1, + SUBTRACT_GREEN = 2, + COLOR_INDEXING_TRANSFORM = 3 +} VP8LImageTransformType; + +// Alpha related constants. +#define ALPHA_HEADER_LEN 1 +#define ALPHA_NO_COMPRESSION 0 +#define ALPHA_LOSSLESS_COMPRESSION 1 +#define ALPHA_PREPROCESSED_LEVELS 1 + +// Mux related constants. +#define TAG_SIZE 4 // Size of a chunk tag (e.g. "VP8L"). +#define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size. +#define CHUNK_HEADER_SIZE 8 // Size of a chunk header. +#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 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 x/y offset + +// Maximum chunk payload is such that adding the header and padding won't +// overflow a uint32_t. +#define MAX_CHUNK_PAYLOAD (~0U - CHUNK_HEADER_SIZE - 1) + +#endif // WEBP_WEBP_FORMAT_CONSTANTS_H_ diff --git a/internal/libwebp-1.2.1/src/webp/mux.h b/internal/libwebp-1.2.1/src/webp/mux.h new file mode 100644 index 0000000..7d27489 --- /dev/null +++ b/internal/libwebp-1.2.1/src/webp/mux.h @@ -0,0 +1,530 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// RIFF container manipulation and encoding for WebP images. +// +// Authors: Urvang (urvang@google.com) +// Vikas (vikasa@google.com) + +#ifndef WEBP_WEBP_MUX_H_ +#define WEBP_WEBP_MUX_H_ + +#include "./mux_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#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. +// +// Code Example#1: Create a WebPMux object with image data, color profile and +// XMP metadata. +/* + int copy_data = 0; + WebPMux* mux = WebPMuxNew(); + // ... (Prepare image data). + WebPMuxSetImage(mux, &image, copy_data); + // ... (Prepare ICCP color profile data). + WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data); + // ... (Prepare XMP metadata). + WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data); + // Get data from mux in WebP RIFF format. + WebPMuxAssemble(mux, &output_data); + WebPMuxDelete(mux); + // ... (Consume output_data; e.g. write output_data.bytes to file). + WebPDataClear(&output_data); +*/ + +// Code Example#2: Get image and color profile data from a WebP file. +/* + int copy_data = 0; + // ... (Read data from file). + WebPMux* mux = WebPMuxCreate(&data, copy_data); + WebPMuxGetFrame(mux, 1, &image); + // ... (Consume image; e.g. call WebPDecode() to decode the data). + WebPMuxGetChunk(mux, "ICCP", &icc_profile); + // ... (Consume icc_data). + WebPMuxDelete(mux); + WebPFree(data); +*/ + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum WebPMuxError WebPMuxError; +// typedef enum WebPChunkId WebPChunkId; +typedef struct WebPMux WebPMux; // main opaque object. +typedef struct WebPMuxFrameInfo WebPMuxFrameInfo; +typedef struct WebPMuxAnimParams WebPMuxAnimParams; +typedef struct WebPAnimEncoderOptions WebPAnimEncoderOptions; + +// Error codes +typedef enum WebPMuxError { + WEBP_MUX_OK = 1, + WEBP_MUX_NOT_FOUND = 0, + WEBP_MUX_INVALID_ARGUMENT = -1, + WEBP_MUX_BAD_DATA = -2, + WEBP_MUX_MEMORY_ERROR = -3, + WEBP_MUX_NOT_ENOUGH_DATA = -4 +} 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_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; + +//------------------------------------------------------------------------------ + +// Returns the version number of the mux library, packed in hexadecimal using +// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507. +WEBP_EXTERN int WebPGetMuxVersion(void); + +//------------------------------------------------------------------------------ +// Life of a Mux object + +// Internal, version-checked, entry point +WEBP_EXTERN WebPMux* WebPNewInternal(int); + +// Creates an empty mux object. +// Returns: +// A pointer to the newly created empty mux object. +// Or NULL in case of memory error. +static WEBP_INLINE WebPMux* WebPMuxNew(void) { + return WebPNewInternal(WEBP_MUX_ABI_VERSION); +} + +// Deletes the mux object. +// Parameters: +// mux - (in/out) object to be deleted +WEBP_EXTERN void WebPMuxDelete(WebPMux* mux); + +//------------------------------------------------------------------------------ +// Mux creation. + +// Internal, version-checked, entry point +WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int, int); + +// Creates a mux object from raw data given in WebP RIFF format. +// Parameters: +// bitstream - (in) the bitstream data in WebP RIFF format +// 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: +// A pointer to the mux object created from given data - on success. +// NULL - In case of invalid data or memory error. +static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream, + int copy_data) { + return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION); +} + +//------------------------------------------------------------------------------ +// Non-image chunks. + +// Note: Only non-image related chunks should be managed through chunk APIs. +// (Image related chunks are: "ANMF", "VP8 ", "VP8L" and "ALPH"). +// To add, get and delete images, use WebPMuxSetImage(), WebPMuxPushFrame(), +// WebPMuxGetFrame() and WebPMuxDeleteFrame(). + +// Adds a chunk with id 'fourcc' and data 'chunk_data' in the mux object. +// Any existing chunk(s) with the same id will be removed. +// Parameters: +// mux - (in/out) object to which the chunk is to be added +// fourcc - (in) a character array containing the fourcc of the given chunk; +// e.g., "ICCP", "XMP ", "EXIF" etc. +// chunk_data - (in) the chunk data to be added +// 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: +// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL +// or if fourcc corresponds to an image chunk. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxSetChunk( + WebPMux* mux, const char fourcc[4], const WebPData* chunk_data, + int copy_data); + +// Gets a reference to the data of the chunk with id 'fourcc' in the mux object. +// The caller should NOT free the returned data. +// Parameters: +// mux - (in) object from which the chunk data is to be fetched +// fourcc - (in) a character array containing the fourcc of the chunk; +// e.g., "ICCP", "XMP ", "EXIF" etc. +// chunk_data - (out) returned chunk data +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL +// or if fourcc corresponds to an image chunk. +// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given id. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetChunk( + const WebPMux* mux, const char fourcc[4], WebPData* chunk_data); + +// Deletes the chunk with the given 'fourcc' from the mux object. +// Parameters: +// mux - (in/out) object from which the chunk is to be deleted +// fourcc - (in) a character array containing the fourcc of the chunk; +// e.g., "ICCP", "XMP ", "EXIF" etc. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or fourcc is NULL +// or if fourcc corresponds to an image chunk. +// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given fourcc. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxDeleteChunk( + WebPMux* mux, const char fourcc[4]); + +//------------------------------------------------------------------------------ +// Images. + +// 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. + int x_offset; // x-offset of the frame. + 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 + // 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) 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) +// 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: +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +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 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 +// Parameters: +// mux - (in/out) object to which the frame is to be added +// frame - (in) frame data. +// 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: +// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL +// or if content of 'frame' is invalid. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxPushFrame( + WebPMux* mux, const WebPMuxFrameInfo* frame, int copy_data); + +// Gets the nth frame from the mux object. +// The content of 'frame->bitstream' is allocated using WebPMalloc(), and NOT +// owned by the 'mux' object. It MUST be deallocated by the caller by calling +// WebPDataClear(). +// nth=0 has a special meaning - last position. +// Parameters: +// mux - (in) object from which the info is to be fetched +// nth - (in) index of the frame in the mux object +// frame - (out) data of the returned frame +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL. +// WEBP_MUX_NOT_FOUND - if there are less than nth frames in the mux object. +// WEBP_MUX_BAD_DATA - if nth frame chunk in mux is invalid. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetFrame( + const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame); + +// Deletes a frame from the mux object. +// nth=0 has a special meaning - last position. +// Parameters: +// mux - (in/out) object from which a frame is to be deleted +// nth - (in) The position from which the frame is to be deleted +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL. +// WEBP_MUX_NOT_FOUND - If there are less than nth frames in the mux object +// before deletion. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth); + +//------------------------------------------------------------------------------ +// Animation. + +// Animation parameters. +struct WebPMuxAnimParams { + uint32_t bgcolor; // Background color of the canvas stored (in MSB order) as: + // Bits 00 to 07: Alpha. + // Bits 08 to 15: Red. + // Bits 16 to 23: Green. + // Bits 24 to 31: Blue. + int loop_count; // Number of times to repeat the animation [0 = infinite]. +}; + +// Sets the animation parameters in the mux object. Any existing ANIM chunks +// will be removed. +// Parameters: +// mux - (in/out) object in which ANIM chunk is to be set/added +// params - (in) animation parameters. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxSetAnimationParams( + WebPMux* mux, const WebPMuxAnimParams* params); + +// Gets the animation parameters from the mux object. +// Parameters: +// mux - (in) object from which the animation parameters to be fetched +// params - (out) animation parameters extracted from the ANIM chunk +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL. +// WEBP_MUX_NOT_FOUND - if ANIM chunk is not present in mux object. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetAnimationParams( + const WebPMux* mux, WebPMuxAnimParams* params); + +//------------------------------------------------------------------------------ +// Misc Utilities. + +// Sets the canvas size for the mux object. The width and height can be +// specified explicitly or left as zero (0, 0). +// * When width and height are specified explicitly, then this frame bound is +// enforced during subsequent calls to WebPMuxAssemble() and an error is +// reported if any animated frame does not completely fit within the canvas. +// * When unspecified (0, 0), the constructed canvas will get the frame bounds +// from the bounding-box over all frames after calling WebPMuxAssemble(). +// Parameters: +// mux - (in) object to which the canvas size is to be set +// width - (in) canvas width +// height - (in) canvas height +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL; or +// width or height are invalid or out of bounds +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux, + int width, int height); + +// Gets the canvas size from the mux object. +// Note: This method assumes that the VP8X chunk, if present, is up-to-date. +// That is, the mux object hasn't been modified since the last call to +// WebPMuxAssemble() or WebPMuxCreate(). +// Parameters: +// mux - (in) object from which the canvas size is to be fetched +// width - (out) canvas width +// height - (out) canvas height +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux, width or height is NULL. +// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetCanvasSize(const WebPMux* mux, + int* width, int* height); + +// Gets the feature flags from the mux object. +// Note: This method assumes that the VP8X chunk, if present, is up-to-date. +// That is, the mux object hasn't been modified since the last call to +// WebPMuxAssemble() or WebPMuxCreate(). +// Parameters: +// mux - (in) object from which the features are to be fetched +// flags - (out) the flags specifying which features are present in the +// mux object. This will be an OR of various flag values. +// Enum 'WebPFeatureFlags' can be used to test individual flag values. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux or flags is NULL. +// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, + uint32_t* flags); + +// Gets number of chunks with the given 'id' in the mux object. +// Parameters: +// mux - (in) object from which the info is to be fetched +// id - (in) chunk id specifying the type of chunk +// num_elements - (out) number of chunks with the given chunk id +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux, or num_elements is NULL. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxNumChunks(const WebPMux* mux, + WebPChunkId id, int* num_elements); + +// Assembles all chunks in WebP RIFF format and returns in 'assembled_data'. +// This function also validates the mux object. +// Note: The content of 'assembled_data' will be ignored and overwritten. +// Also, the content of 'assembled_data' is allocated using WebPMalloc(), and +// NOT owned by the 'mux' object. It MUST be deallocated by the caller by +// calling WebPDataClear(). It's always safe to call WebPDataClear() upon +// return, even in case of error. +// Parameters: +// mux - (in/out) object whose chunks are to be assembled +// assembled_data - (out) assembled WebP data +// Returns: +// WEBP_MUX_BAD_DATA - if mux object is invalid. +// WEBP_MUX_INVALID_ARGUMENT - if mux or assembled_data is NULL. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPMuxAssemble(WebPMux* mux, + WebPData* assembled_data); + +//------------------------------------------------------------------------------ +// WebPAnimEncoder API +// +// This API allows encoding (possibly) animated WebP images. +// +// Code Example: +/* + WebPAnimEncoderOptions enc_options; + WebPAnimEncoderOptionsInit(&enc_options); + // Tune 'enc_options' as needed. + WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options); + while() { + WebPConfig config; + WebPConfigInit(&config); + // Tune 'config' as needed. + WebPAnimEncoderAdd(enc, frame, timestamp_ms, &config); + } + WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL); + WebPAnimEncoderAssemble(enc, webp_data); + WebPAnimEncoderDelete(enc); + // Write the 'webp_data' to a file, or re-mux it further. +*/ + +typedef struct WebPAnimEncoder WebPAnimEncoder; // Main opaque object. + +// Forward declarations. Defined in encode.h. +struct WebPPicture; +struct WebPConfig; + +// Global options. +struct WebPAnimEncoderOptions { + WebPMuxAnimParams anim_params; // Animation parameters. + int minimize_size; // If true, minimize the output size (slow). Implicitly + // disables key-frame insertion. + int kmin; + int kmax; // Minimum and maximum distance between consecutive key + // 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 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. + + uint32_t padding[4]; // Padding for later use. +}; + +// Internal, version-checked, entry point. +WEBP_EXTERN int WebPAnimEncoderOptionsInitInternal( + WebPAnimEncoderOptions*, int); + +// Should always be called, to initialize a fresh WebPAnimEncoderOptions +// structure before modification. Returns false in case of version mismatch. +// WebPAnimEncoderOptionsInit() must have succeeded before using the +// 'enc_options' object. +static WEBP_INLINE int WebPAnimEncoderOptionsInit( + WebPAnimEncoderOptions* enc_options) { + return WebPAnimEncoderOptionsInitInternal(enc_options, WEBP_MUX_ABI_VERSION); +} + +// Internal, version-checked, entry point. +WEBP_EXTERN WebPAnimEncoder* WebPAnimEncoderNewInternal( + int, int, const WebPAnimEncoderOptions*, int); + +// Creates and initializes a WebPAnimEncoder object. +// Parameters: +// width/height - (in) canvas width and height of the animation. +// enc_options - (in) encoding options; can be passed NULL to pick +// reasonable defaults. +// Returns: +// A pointer to the newly created WebPAnimEncoder object. +// Or NULL in case of memory error. +static WEBP_INLINE WebPAnimEncoder* WebPAnimEncoderNew( + int width, int height, const WebPAnimEncoderOptions* enc_options) { + return WebPAnimEncoderNewInternal(width, height, enc_options, + WEBP_MUX_ABI_VERSION); +} + +// Optimize the given frame for WebP, encode it and add it to the +// WebPAnimEncoder object. +// The last call to 'WebPAnimEncoderAdd' should be with frame = NULL, which +// indicates that no more frames are to be added. This call is also used to +// determine the duration of the last frame. +// Parameters: +// enc - (in/out) object to which the frame is to be added. +// frame - (in/out) frame data in ARGB or YUV(A) format. If it is in YUV(A) +// format, it will be converted to ARGB, which incurs a small loss. +// timestamp_ms - (in) timestamp of this frame in milliseconds. +// Duration of a frame would be calculated as +// "timestamp of next frame - timestamp of this frame". +// Hence, timestamps should be in non-decreasing order. +// config - (in) encoding options; can be passed NULL to pick +// reasonable defaults. +// Returns: +// On error, returns false and frame->error_code is set appropriately. +// Otherwise, returns true. +WEBP_EXTERN int WebPAnimEncoderAdd( + WebPAnimEncoder* enc, struct WebPPicture* frame, int timestamp_ms, + const struct WebPConfig* config); + +// Assemble all frames added so far into a WebP bitstream. +// This call should be preceded by a call to 'WebPAnimEncoderAdd' with +// frame = NULL; if not, the duration of the last frame will be internally +// estimated. +// Parameters: +// enc - (in/out) object from which the frames are to be assembled. +// webp_data - (out) generated WebP bitstream. +// Returns: +// True on success. +WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc, + WebPData* webp_data); + +// Get error string corresponding to the most recent call using 'enc'. The +// returned string is owned by 'enc' and is valid only until the next call to +// WebPAnimEncoderAdd() or WebPAnimEncoderAssemble() or WebPAnimEncoderDelete(). +// Parameters: +// enc - (in/out) object from which the error string is to be fetched. +// Returns: +// NULL if 'enc' is NULL. Otherwise, returns the error string if the last call +// to 'enc' had an error, or an empty string if the last call was a success. +WEBP_EXTERN const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc); + +// Deletes the WebPAnimEncoder object. +// Parameters: +// enc - (in/out) object to be deleted +WEBP_EXTERN void WebPAnimEncoderDelete(WebPAnimEncoder* enc); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_MUX_H_ diff --git a/internal/libwebp-1.2.1/src/webp/mux_types.h b/internal/libwebp-1.2.1/src/webp/mux_types.h new file mode 100644 index 0000000..2fe8195 --- /dev/null +++ b/internal/libwebp-1.2.1/src/webp/mux_types.h @@ -0,0 +1,98 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Data-types common to the mux and demux libraries. +// +// Author: Urvang (urvang@google.com) + +#ifndef WEBP_WEBP_MUX_TYPES_H_ +#define WEBP_WEBP_MUX_TYPES_H_ + +#include // memset() +#include "./types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Note: forward declaring enumerations is not allowed in (strict) C and C++, +// the types are left here for reference. +// typedef enum WebPFeatureFlags WebPFeatureFlags; +// typedef enum WebPMuxAnimDispose WebPMuxAnimDispose; +// typedef enum WebPMuxAnimBlend WebPMuxAnimBlend; +typedef struct WebPData WebPData; + +// VP8X Feature Flags. +typedef enum WebPFeatureFlags { + ANIMATION_FLAG = 0x00000002, + XMP_FLAG = 0x00000004, + EXIF_FLAG = 0x00000008, + ALPHA_FLAG = 0x00000010, + ICCP_FLAG = 0x00000020, + + ALL_VALID_FLAGS = 0x0000003e +} WebPFeatureFlags; + +// Dispose method (animation only). Indicates how the area used by the current +// frame is to be treated before rendering the next frame on the canvas. +typedef enum WebPMuxAnimDispose { + WEBP_MUX_DISPOSE_NONE, // Do not dispose. + WEBP_MUX_DISPOSE_BACKGROUND // Dispose to background color. +} WebPMuxAnimDispose; + +// Blend operation (animation only). Indicates how transparent pixels of the +// current frame are blended with those of the previous canvas. +typedef enum WebPMuxAnimBlend { + WEBP_MUX_BLEND, // Blend. + WEBP_MUX_NO_BLEND // Do not blend. +} WebPMuxAnimBlend; + +// Data type used to describe 'raw' data, e.g., chunk data +// (ICC profile, metadata) and WebP compressed image data. +// 'bytes' memory must be allocated using WebPMalloc() and such. +struct WebPData { + const uint8_t* bytes; + size_t size; +}; + +// Initializes the contents of the 'webp_data' object with default values. +static WEBP_INLINE void WebPDataInit(WebPData* webp_data) { + if (webp_data != NULL) { + memset(webp_data, 0, sizeof(*webp_data)); + } +} + +// Clears the contents of the 'webp_data' object by calling WebPFree(). +// Does not deallocate the object itself. +static WEBP_INLINE void WebPDataClear(WebPData* webp_data) { + if (webp_data != NULL) { + WebPFree((void*)webp_data->bytes); + WebPDataInit(webp_data); + } +} + +// Allocates necessary storage for 'dst' and copies the contents of 'src'. +// Returns true on success. +static WEBP_INLINE int WebPDataCopy(const WebPData* src, WebPData* dst) { + if (src == NULL || dst == NULL) return 0; + WebPDataInit(dst); + if (src->bytes != NULL && src->size != 0) { + dst->bytes = (uint8_t*)WebPMalloc(src->size); + if (dst->bytes == NULL) return 0; + memcpy((void*)dst->bytes, src->bytes, src->size); + dst->size = src->size; + } + return 1; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_MUX_TYPES_H_ diff --git a/internal/libwebp-1.2.1/src/webp/types.h b/internal/libwebp-1.2.1/src/webp/types.h new file mode 100644 index 0000000..47f7f2b --- /dev/null +++ b/internal/libwebp-1.2.1/src/webp/types.h @@ -0,0 +1,68 @@ +// 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. +// ----------------------------------------------------------------------------- +// +// Common types + memory wrappers +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_WEBP_TYPES_H_ +#define WEBP_WEBP_TYPES_H_ + +#include // for size_t + +#ifndef _MSC_VER +#include +#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \ + (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) +#define WEBP_INLINE inline +#else +#define WEBP_INLINE +#endif +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +typedef long long int int64_t; +#define WEBP_INLINE __forceinline +#endif /* _MSC_VER */ + +#ifndef WEBP_EXTERN +// This explicitly marks library functions and allows for changing the +// signature for e.g., Windows DLL builds. +# if defined(__GNUC__) && __GNUC__ >= 4 +# define WEBP_EXTERN extern __attribute__ ((visibility ("default"))) +# else +# define WEBP_EXTERN extern +# endif /* __GNUC__ >= 4 */ +#endif /* WEBP_EXTERN */ + +// Macro to check ABI compatibility (same major revision number) +#define WEBP_ABI_IS_INCOMPATIBLE(a, b) (((a) >> 8) != ((b) >> 8)) + +#ifdef __cplusplus +extern "C" { +#endif + +// Allocates 'size' bytes of memory. Returns NULL upon error. Memory +// must be deallocated by calling WebPFree(). This function is made available +// by the core 'libwebp' library. +WEBP_EXTERN void* WebPMalloc(size_t size); + +// Releases memory returned by the WebPDecode*() functions (from decode.h). +WEBP_EXTERN void WebPFree(void* ptr); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_WEBP_TYPES_H_ diff --git a/internal/libwebp-1.2.1/swig/README b/internal/libwebp-1.2.1/swig/README new file mode 100644 index 0000000..725c071 --- /dev/null +++ b/internal/libwebp-1.2.1/swig/README @@ -0,0 +1,56 @@ +Building: +========= + +JNI SWIG bindings: +------------------ + $ gcc -shared -fPIC -fno-strict-aliasing -O2 \ + -I/path/to/your/jdk/includes \ + libwebp_java_wrap.c \ + -lwebp \ + -o libwebp_jni.so + +-------------------------------------- BEGIN PSEUDO EXAMPLE +import com.google.webp.libwebp; + +import java.lang.reflect.Method; + +public class libwebp_jni_example { + static { + System.loadLibrary("webp_jni"); + } + + /** + * usage: java -cp libwebp.jar:. libwebp_jni_example + */ + public static void main(String argv[]) { + final int version = libwebp.WebPGetDecoderVersion(); + System.out.println("libwebp version: " + Integer.toHexString(version)); + + System.out.println("libwebp methods:"); + final Method[] libwebpMethods = libwebp.class.getDeclaredMethods(); + for (int i = 0; i < libwebpMethods.length; i++) { + System.out.println(libwebpMethods[i]); + } + } +} +-------------------------------------- END PSEUDO EXAMPLE + + $ javac -cp libwebp.jar libwebp_jni_example.java + $ java -Djava.library.path=. -cp libwebp.jar:. libwebp_jni_example + +Python SWIG bindings: +--------------------- + $ python setup.py build_ext + $ python setup.py install --prefix=pylocal + +-------------------------------------- BEGIN PSEUDO EXAMPLE +import glob +import sys +sys.path.append(glob.glob('pylocal/lib/python*/site-packages')[0]) + +from com.google.webp import libwebp +print "libwebp decoder version: %x" % libwebp.WebPGetDecoderVersion() + +print "libwebp attributes:" +for attr in dir(libwebp): print attr +-------------------------------------- END PSEUDO EXAMPLE diff --git a/internal/libwebp-1.2.1/swig/libwebp.go b/internal/libwebp-1.2.1/swig/libwebp.go new file mode 100644 index 0000000..df205aa --- /dev/null +++ b/internal/libwebp-1.2.1/swig/libwebp.go @@ -0,0 +1,45 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 2.0.10 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + +package libwebp + +import _ "runtime/cgo" +import "unsafe" + +type _ unsafe.Pointer + +type _swig_fnptr *byte +type _swig_memberptr *byte + +//extern libwebpSwigCgocall +func SwigCgocall() + +//extern libwebpSwigCgocallDone +func SwigCgocallDone() + +//extern libwebpSwigCgocallBack +func SwigCgocallBack() + +//extern libwebpSwigCgocallBackDone +func SwigCgocallBackDone() + +func WebPGetDecoderVersion() int +func Wrapped_WebPGetInfo(string, []int, []int) int + +// WebPGetInfo has 2 output parameters, provide a version in the more natural +// go idiom: +func WebPGetInfo(webp []byte) (ok bool, width int, height int) { + w := []int{0} + h := []int{0} + ok = Wrapped_WebPGetInfo(string(webp), w, h) != 0 + width = w[0] + height = h[0] + return +} diff --git a/internal/libwebp-1.2.1/swig/libwebp.jar b/internal/libwebp-1.2.1/swig/libwebp.jar new file mode 100644 index 0000000000000000000000000000000000000000..2fc502b972101270fc1ecfb8c8c3948e0a2e45a6 GIT binary patch literal 2150 zcmWIWW@Zs#-~ht6J>Jd?NPv@pg~8V~#8KDN&rSc|DFy~+h5&DN4v-2asImZ@nni#r z;F^6M{XE@VgG2Ou-9G!CIql=Et9OytTUYDcne&^246YbIcv__A<*VcAd$DvC3+IfN zl1FCG(8^$${!H^nnwt1i@o=%}OP+~oS3FaFo%yNgV-X|RA=Rs{zjp^(1HvGOaDg3# z?h6*6XmWn8K5iAw=3j)2gl?f9X11+|u_l z`wM4N+w+?iSC%x+{dsWS=l^q`&)MAm_w)DX`3#S5OuyJWqes*xeT`9^!J{72V^)t< zlba_dR9a^ zdv^8CCaLu@saGDQSxsIpc7EHe$o^GNT}{0^lY(@o6-7B-&zPdN*gULadEnHAAt|b& z-bZ(wJ{lXEtt_?hMP+)|yF1gX4o{alRQ1o^Ug+))kp;g$96d9QYkK;%vM3+<_r>X3 z-Q~~vUQG^}V|V%&%aJK@i~p{Y+IK5T_q}LT_|_oJ_0znro{XPzdG*ZJudD1nPdxN& z@72g1Ev2Wb5|exL3RU$I_vRG_%1!=ivHI|<#bq5N!d=Z``kAntGT<;$4Yf?yV5lreaI%Z+i$IhiD zF}X+cn6pig!Pyy!XN;-}-JR0Ithr<}ZaK#B7M`6D812Lz&j0jsYx9MuX8~uYy)>NF z68^|g>a4py%L2x@ABEgPRbO`Bp0HEs@3$c7wXHY&%jQ7RM9foX$O+0yDOgA7lHd)k7SkqY*HaVkrqG{y$AeZb(YgSfG^WL)T zZfJRV>)f=9oRf`%Z)Xb5RV_-(wqCnPc(J;byYU*U<}K_s46~TSUYl?Ex$E!TF9o+< zH61V2$i8gTcsudSlD;Q`OHDleUwg%Z7=uMha5y&-+x&BRNjTv<*4Yc4V~G^K_MQT zj?HZ1u`{;yEm^W<32UWZxSC!KgZ!zAbSpvbM6u6j?-akAGkeQ@`}y$<1&r!KE)O+0 zk9%eac*I2YneJ8+$eqW>d1T#PaW94In`Lgz_vd+K`>L&X>L+2-ch)6k&b}oT&aZw> zd;P&|s)N^xRi9r8O_&w6diBa#r7VdmET(~g#i9xmMalVP>qyuhgZ&}*w5Yf6{iDz3f1Y@a;O{+`E~ zv(MOU-S$&8b+eR@qV102^GkBOYgf<7mE_Ly*38&%$M#`#R-$~kBz zrmf)EWy~viZcT!pfqsO5PS-x6HwDvJ%UQp(d|!AdQgjWcoWUcJ(mTopKe$14O2XQg zfdz~V3?G3NOMo{clL!N9{K4uQP?dxV;B^eBfMTnxBt z5`+oIfJ{ief?EfuW|W5meJ4fFLkrkTgQ;9n?@lPXC~q2LbG{=tilB n&_fM5G87Qz09luTp^xqms8R6j6X4Ct2GYb1g!_S3PXzM-9zhES literal 0 HcmV?d00001 diff --git a/internal/libwebp-1.2.1/swig/libwebp.py b/internal/libwebp-1.2.1/swig/libwebp.py new file mode 100644 index 0000000..2d126b5 --- /dev/null +++ b/internal/libwebp-1.2.1/swig/libwebp.py @@ -0,0 +1,235 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 3.0.12 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. + +from sys import version_info as _swig_python_version_info +if _swig_python_version_info >= (2, 7, 0): + def swig_import_helper(): + import importlib + pkg = __name__.rpartition('.')[0] + mname = '.'.join((pkg, '_libwebp')).lstrip('.') + try: + return importlib.import_module(mname) + except ImportError: + return importlib.import_module('_libwebp') + _libwebp = swig_import_helper() + del swig_import_helper +elif _swig_python_version_info >= (2, 6, 0): + def swig_import_helper(): + from os.path import dirname + import imp + fp = None + try: + fp, pathname, description = imp.find_module('_libwebp', [dirname(__file__)]) + except ImportError: + import _libwebp + return _libwebp + try: + _mod = imp.load_module('_libwebp', fp, pathname, description) + finally: + if fp is not None: + fp.close() + return _mod + _libwebp = swig_import_helper() + del swig_import_helper +else: + import _libwebp +del _swig_python_version_info + +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. + +try: + import builtins as __builtin__ +except ImportError: + import __builtin__ + +def _swig_setattr_nondynamic(self, class_type, name, value, static=1): + if (name == "thisown"): + return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'SwigPyObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name, None) + if method: + return method(self, value) + if (not static): + if _newclass: + object.__setattr__(self, name, value) + else: + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + + +def _swig_setattr(self, class_type, name, value): + return _swig_setattr_nondynamic(self, class_type, name, value, 0) + + +def _swig_getattr(self, class_type, name): + if (name == "thisown"): + return self.this.own() + method = class_type.__swig_getmethods__.get(name, None) + if method: + return method(self) + raise AttributeError("'%s' object has no attribute '%s'" % (class_type.__name__, name)) + + +def _swig_repr(self): + try: + strthis = "proxy of " + self.this.__repr__() + except __builtin__.Exception: + strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +try: + _object = object + _newclass = 1 +except __builtin__.Exception: + class _object: + pass + _newclass = 0 + + +def WebPGetDecoderVersion(): + """WebPGetDecoderVersion() -> int""" + return _libwebp.WebPGetDecoderVersion() + +def WebPGetInfo(data): + """WebPGetInfo(uint8_t data) -> (width, height)""" + return _libwebp.WebPGetInfo(data) + +def WebPDecodeRGB(data): + """WebPDecodeRGB(uint8_t data) -> (rgb, width, height)""" + return _libwebp.WebPDecodeRGB(data) + +def WebPDecodeRGBA(data): + """WebPDecodeRGBA(uint8_t data) -> (rgb, width, height)""" + return _libwebp.WebPDecodeRGBA(data) + +def WebPDecodeARGB(data): + """WebPDecodeARGB(uint8_t data) -> (rgb, width, height)""" + return _libwebp.WebPDecodeARGB(data) + +def WebPDecodeBGR(data): + """WebPDecodeBGR(uint8_t data) -> (rgb, width, height)""" + return _libwebp.WebPDecodeBGR(data) + +def WebPDecodeBGRA(data): + """WebPDecodeBGRA(uint8_t data) -> (rgb, width, height)""" + return _libwebp.WebPDecodeBGRA(data) + +def WebPGetEncoderVersion(): + """WebPGetEncoderVersion() -> int""" + return _libwebp.WebPGetEncoderVersion() + +def wrap_WebPEncodeRGB(rgb, unused1, unused2, width, height, stride, quality_factor): + """private, do not call directly.""" + return _libwebp.wrap_WebPEncodeRGB(rgb, unused1, unused2, width, height, stride, quality_factor) + +def wrap_WebPEncodeBGR(rgb, unused1, unused2, width, height, stride, quality_factor): + """private, do not call directly.""" + return _libwebp.wrap_WebPEncodeBGR(rgb, unused1, unused2, width, height, stride, quality_factor) + +def wrap_WebPEncodeRGBA(rgb, unused1, unused2, width, height, stride, quality_factor): + """private, do not call directly.""" + return _libwebp.wrap_WebPEncodeRGBA(rgb, unused1, unused2, width, height, stride, quality_factor) + +def wrap_WebPEncodeBGRA(rgb, unused1, unused2, width, height, stride, quality_factor): + """private, do not call directly.""" + return _libwebp.wrap_WebPEncodeBGRA(rgb, unused1, unused2, width, height, stride, quality_factor) + +def wrap_WebPEncodeLosslessRGB(rgb, unused1, unused2, width, height, stride): + """private, do not call directly.""" + return _libwebp.wrap_WebPEncodeLosslessRGB(rgb, unused1, unused2, width, height, stride) + +def wrap_WebPEncodeLosslessBGR(rgb, unused1, unused2, width, height, stride): + """private, do not call directly.""" + return _libwebp.wrap_WebPEncodeLosslessBGR(rgb, unused1, unused2, width, height, stride) + +def wrap_WebPEncodeLosslessRGBA(rgb, unused1, unused2, width, height, stride): + """private, do not call directly.""" + return _libwebp.wrap_WebPEncodeLosslessRGBA(rgb, unused1, unused2, width, height, stride) + +def wrap_WebPEncodeLosslessBGRA(rgb, unused1, unused2, width, height, stride): + """private, do not call directly.""" + return _libwebp.wrap_WebPEncodeLosslessBGRA(rgb, unused1, unused2, width, height, stride) + +_UNUSED = 1 + + +def WebPEncodeRGB(rgb, width, height, stride, quality_factor): + """WebPEncodeRGB(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp""" + webp = wrap_WebPEncodeRGB( + rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor) + if len(webp[0]) == 0: + return None + return webp[0] + + +def WebPEncodeRGBA(rgb, width, height, stride, quality_factor): + """WebPEncodeRGBA(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp""" + webp = wrap_WebPEncodeRGBA( + rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor) + if len(webp[0]) == 0: + return None + return webp[0] + + +def WebPEncodeBGR(rgb, width, height, stride, quality_factor): + """WebPEncodeBGR(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp""" + webp = wrap_WebPEncodeBGR( + rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor) + if len(webp[0]) == 0: + return None + return webp[0] + + +def WebPEncodeBGRA(rgb, width, height, stride, quality_factor): + """WebPEncodeBGRA(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp""" + webp = wrap_WebPEncodeBGRA( + rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor) + if len(webp[0]) == 0: + return None + return webp[0] + + +def WebPEncodeLosslessRGB(rgb, width, height, stride): + """WebPEncodeLosslessRGB(uint8_t rgb, int width, int height, int stride) -> lossless_webp""" + webp = wrap_WebPEncodeLosslessRGB(rgb, _UNUSED, _UNUSED, width, height, stride) + if len(webp[0]) == 0: + return None + return webp[0] + + +def WebPEncodeLosslessRGBA(rgb, width, height, stride): + """WebPEncodeLosslessRGBA(uint8_t rgb, int width, int height, int stride) -> lossless_webp""" + webp = wrap_WebPEncodeLosslessRGBA(rgb, _UNUSED, _UNUSED, width, height, stride) + if len(webp[0]) == 0: + return None + return webp[0] + + +def WebPEncodeLosslessBGR(rgb, width, height, stride): + """WebPEncodeLosslessBGR(uint8_t rgb, int width, int height, int stride) -> lossless_webp""" + webp = wrap_WebPEncodeLosslessBGR(rgb, _UNUSED, _UNUSED, width, height, stride) + if len(webp[0]) == 0: + return None + return webp[0] + + +def WebPEncodeLosslessBGRA(rgb, width, height, stride): + """WebPEncodeLosslessBGRA(uint8_t rgb, int width, int height, int stride) -> lossless_webp""" + webp = wrap_WebPEncodeLosslessBGRA(rgb, _UNUSED, _UNUSED, width, height, stride) + if len(webp[0]) == 0: + return None + return webp[0] + +# This file is compatible with both classic and new-style classes. + + diff --git a/internal/libwebp-1.2.1/swig/libwebp.swig b/internal/libwebp-1.2.1/swig/libwebp.swig new file mode 100644 index 0000000..ca38298 --- /dev/null +++ b/internal/libwebp-1.2.1/swig/libwebp.swig @@ -0,0 +1,438 @@ +// Copyright 2011 Google Inc. +// +// 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. +// ----------------------------------------------------------------------------- +// +// libwebp swig interface definition +// +// Author: James Zern (jzern@google.com) + +/* + Go bindings: + $ swig -go \ + -outdir . \ + -o libwebp_go_wrap.c libwebp.swig + + Java bindings: + $ mkdir -p java/com/google/webp + $ swig -java \ + -package com.google.webp \ + -outdir java/com/google/webp \ + -o libwebp_java_wrap.c libwebp.swig + + Python bindings: + $ swig -python \ + -outdir . \ + -o libwebp_python_wrap.c libwebp.swig +*/ + +#ifdef SWIGPYTHON +%module(package="com.google.webp") libwebp +%begin %{ +#define SWIG_PYTHON_STRICT_BYTE_CHAR +%} +#else +%module libwebp +#endif /* SWIGPYTHON */ + +%include "constraints.i" +%include "typemaps.i" + +#ifdef SWIGGO +%apply (char* STRING, size_t LENGTH) { (const uint8_t* data, size_t data_size) } + +%rename(wrapped_WebPGetInfo) WebPGetInfo(const uint8_t* data, size_t data_size, + int* width, int* height); +#endif /* SWIGGO */ + +#ifdef SWIGJAVA +%include "arrays_java.i"; +%include "enums.swg" /*NB: requires JDK-1.5+ + See: http://www.swig.org/Doc1.3/Java.html#enumerations */ + +// map uint8_t* such that a byte[] is used +%{ +#include "webp/types.h" +%} +// from arrays_java.i (signed char) +JAVA_ARRAYS_DECL(uint8_t, jbyte, Byte, Uint8) +JAVA_ARRAYS_IMPL(uint8_t, jbyte, Byte, Uint8) +JAVA_ARRAYS_TYPEMAPS(uint8_t, byte, jbyte, Uint8, "[B") +%apply uint8_t[] { uint8_t* } +#endif /* SWIGJAVA */ + +#ifdef SWIGPYTHON +%apply (char* STRING, size_t LENGTH) { (const uint8_t* data, size_t data_size) } +%typemap(out) uint8_t* { + $result = PyString_FromStringAndSize( + (const char*)$1, + ($1 == NULL) ? 0 : ReturnedBufferSize("$symname", arg3, arg4)); +} + +%typemap (in) const uint8_t* rgb (Py_buffer rgb_buffer) { + // NB: with Python < 2.6 the old style buffer protocol may be used: + // Py_ssize_t unused; + // PyObject_AsReadBuffer($input, (const void**)(&$1), &unused); + if (!PyObject_CheckBuffer($input)) { + SWIG_exception_fail(SWIG_TypeError, + "in method '$symname', argument $argnum" + " does not support the buffer interface"); + } + if (PyObject_GetBuffer($input, &rgb_buffer, PyBUF_SIMPLE)) { + SWIG_exception_fail(SWIG_RuntimeError, + "in method '$symname', unable to get buffer view"); + } + $1 = ($1_ltype)rgb_buffer.buf; +} + +%typemap(freearg) const uint8_t* rgb { + PyBuffer_Release(&rgb_buffer$argnum); +} + +%define DECODE_AUTODOC(func) +%feature("autodoc", #func "(uint8_t data) -> (rgb, width, height)") func; +%enddef + +%feature("autodoc", "1"); +DECODE_AUTODOC(WebPDecodeRGB); +DECODE_AUTODOC(WebPDecodeRGBA); +DECODE_AUTODOC(WebPDecodeARGB); +DECODE_AUTODOC(WebPDecodeBGR); +DECODE_AUTODOC(WebPDecodeBGRA); +%feature("autodoc", "WebPGetInfo(uint8_t data) -> (width, height)") WebPGetInfo; +#endif /* SWIGPYTHON */ + +//------------------------------------------------------------------------------ +// Decoder specific + +%apply int* OUTPUT { int* width, int* height } + +int WebPGetDecoderVersion(void); +int WebPGetInfo(const uint8_t* data, size_t data_size, + int* width, int* height); + +#if defined(SWIGJAVA) || defined(SWIGPYTHON) + +// free the buffer returned by these functions after copying into +// the native type +%newobject WebPDecodeRGB; +%newobject WebPDecodeRGBA; +%newobject WebPDecodeARGB; +%newobject WebPDecodeBGR; +%newobject WebPDecodeBGRA; +%typemap(newfree) uint8_t* "free($1);" + +uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, + int* width, int* height); +uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, + int* width, int* height); +uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size, + int* width, int* height); +uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, + int* width, int* height); +uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, + int* width, int* height); + +#endif /* SWIGJAVA || SWIGPYTHON */ + +//------------------------------------------------------------------------------ +// Encoder specific + +#if defined(SWIGJAVA) || defined(SWIGPYTHON) + +int WebPGetEncoderVersion(void); + +#endif /* SWIGJAVA || SWIGPYTHON */ + +//------------------------------------------------------------------------------ +// Wrapper code additions + +%{ +#include "webp/decode.h" +#include "webp/encode.h" +%} + +#ifdef SWIGJAVA +%{ +#define FillMeInAsSizeCannotBeDeterminedAutomatically \ + (result ? (jint)ReturnedBufferSize(__FUNCTION__, arg3, arg4) : 0) +%} +#endif /* SWIGJAVA */ + +#if defined(SWIGJAVA) || defined(SWIGPYTHON) +%{ +static size_t ReturnedBufferSize( + const char* function, int* width, int* height) { + static const struct sizemap { + const char* function; + int size_multiplier; + } size_map[] = { +#ifdef SWIGJAVA + { "Java_com_google_webp_libwebpJNI_WebPDecodeRGB", 3 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeRGBA", 4 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeARGB", 4 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeBGR", 3 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeBGRA", 4 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA", 1 }, +#endif +#ifdef SWIGPYTHON + { "WebPDecodeRGB", 3 }, + { "WebPDecodeRGBA", 4 }, + { "WebPDecodeARGB", 4 }, + { "WebPDecodeBGR", 3 }, + { "WebPDecodeBGRA", 4 }, + { "wrap_WebPEncodeRGB", 1 }, + { "wrap_WebPEncodeBGR", 1 }, + { "wrap_WebPEncodeRGBA", 1 }, + { "wrap_WebPEncodeBGRA", 1 }, + { "wrap_WebPEncodeLosslessRGB", 1 }, + { "wrap_WebPEncodeLosslessBGR", 1 }, + { "wrap_WebPEncodeLosslessRGBA", 1 }, + { "wrap_WebPEncodeLosslessBGRA", 1 }, +#endif + { NULL, 0 } + }; + const struct sizemap* p; + size_t size = 0; + + for (p = size_map; p->function; ++p) { + if (!strcmp(function, p->function)) { + size = *width * *height * p->size_multiplier; + break; + } + } + + return size; +} +%} + +%{ +typedef size_t (*WebPEncodeFunction)(const uint8_t* rgb, + int width, int height, int stride, + float quality_factor, uint8_t** output); +typedef size_t (*WebPEncodeLosslessFunction)(const uint8_t* rgb, + int width, int height, int stride, + uint8_t** output); + +static uint8_t* EncodeLossy(const uint8_t* rgb, + int width, int height, int stride, + float quality_factor, + WebPEncodeFunction encfn, + int* output_size, int* unused) { + uint8_t* output = NULL; + const size_t image_size = + encfn(rgb, width, height, stride, quality_factor, &output); + // the values of following two will be interpreted by ReturnedBufferSize() + // as 'width' and 'height' in the size calculation. + *output_size = image_size; + *unused = 1; + return image_size ? output : NULL; +} + +static uint8_t* EncodeLossless(const uint8_t* rgb, + int width, int height, int stride, + WebPEncodeLosslessFunction encfn, + int* output_size, int* unused) { + uint8_t* output = NULL; + const size_t image_size = encfn(rgb, width, height, stride, &output); + // the values of the following two will be interpreted by + // ReturnedBufferSize() as 'width' and 'height' in the size calculation. + *output_size = image_size; + *unused = 1; + return image_size ? output : NULL; +} +%} + +#endif /* SWIGJAVA || SWIGPYTHON */ + +//------------------------------------------------------------------------------ +// libwebp/encode wrapper functions + +#if defined(SWIGJAVA) || defined(SWIGPYTHON) + +%apply int* INPUT { int* unused1, int* unused2 } +%apply int* OUTPUT { int* output_size } + +// free the buffer returned by these functions after copying into +// the native type +%newobject wrap_WebPEncodeRGB; +%newobject wrap_WebPEncodeBGR; +%newobject wrap_WebPEncodeRGBA; +%newobject wrap_WebPEncodeBGRA; +%newobject wrap_WebPEncodeLosslessRGB; +%newobject wrap_WebPEncodeLosslessBGR; +%newobject wrap_WebPEncodeLosslessRGBA; +%newobject wrap_WebPEncodeLosslessBGRA; + +#ifdef SWIGJAVA +// There's no reason to call these directly +%javamethodmodifiers wrap_WebPEncodeRGB "private"; +%javamethodmodifiers wrap_WebPEncodeBGR "private"; +%javamethodmodifiers wrap_WebPEncodeRGBA "private"; +%javamethodmodifiers wrap_WebPEncodeBGRA "private"; +%javamethodmodifiers wrap_WebPEncodeLosslessRGB "private"; +%javamethodmodifiers wrap_WebPEncodeLosslessBGR "private"; +%javamethodmodifiers wrap_WebPEncodeLosslessRGBA "private"; +%javamethodmodifiers wrap_WebPEncodeLosslessBGRA "private"; +#endif /* SWIGJAVA */ + +#ifdef SWIGPYTHON +// This autodoc will serve as a catch-all for wrap_*. +%feature("autodoc", "private, do not call directly."); +#endif + +%inline %{ +// Changes the return type of WebPEncode* to more closely match Decode*. +// This also makes it easier to wrap the output buffer in a native type rather +// than dealing with the return pointer. +// The additional parameters are to allow reuse of ReturnedBufferSize(), +// unused2 and output_size will be used in this case. +#define LOSSY_WRAPPER(FUNC) \ + static uint8_t* wrap_##FUNC( \ + const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \ + int width, int height, int stride, float quality_factor) { \ + return EncodeLossy(rgb, width, height, stride, quality_factor, \ + FUNC, output_size, unused2); \ + } \ + +LOSSY_WRAPPER(WebPEncodeRGB) +LOSSY_WRAPPER(WebPEncodeBGR) +LOSSY_WRAPPER(WebPEncodeRGBA) +LOSSY_WRAPPER(WebPEncodeBGRA) + +#undef LOSSY_WRAPPER + +#define LOSSLESS_WRAPPER(FUNC) \ + static uint8_t* wrap_##FUNC( \ + const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \ + int width, int height, int stride) { \ + return EncodeLossless(rgb, width, height, stride, \ + FUNC, output_size, unused2); \ + } \ + +LOSSLESS_WRAPPER(WebPEncodeLosslessRGB) +LOSSLESS_WRAPPER(WebPEncodeLosslessBGR) +LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA) +LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA) + +#undef LOSSLESS_WRAPPER + +%} + +#endif /* SWIGJAVA || SWIGPYTHON */ + +//------------------------------------------------------------------------------ +// Language specific + +#ifdef SWIGGO +%insert(go_wrapper) %{ + +// WebPGetInfo has 2 output parameters, provide a version in the more natural +// go idiom: +func WebPGetInfo(webp []byte) (ok bool, width int, height int) { + w := []int{0} + h := []int{0} + ok = Wrapped_WebPGetInfo(string(webp), w, h) != 0 + width = w[0] + height = h[0] + return +} + +%} +#endif /* SWIGGO */ + +#ifdef SWIGJAVA +%{ +/* Work around broken gcj jni.h */ +#ifdef __GCJ_JNI_H__ +# undef JNIEXPORT +# define JNIEXPORT +# undef JNICALL +# define JNICALL +#endif +%} + +%pragma(java) modulecode=%{ + private static final int UNUSED = 1; + private static int outputSize[] = { 0 }; +%} + + +%define CALL_ENCODE_LOSSY_WRAPPER(func) +%pragma(java) modulecode=%{ + public static byte[] func( + byte[] rgb, int width, int height, int stride, float quality_factor) { + return wrap_##func( + rgb, UNUSED, UNUSED, outputSize, width, height, stride, quality_factor); + } +%} +%enddef + +%define CALL_ENCODE_LOSSLESS_WRAPPER(func) +%pragma(java) modulecode=%{ + public static byte[] func( + byte[] rgb, int width, int height, int stride) { + return wrap_##func( + rgb, UNUSED, UNUSED, outputSize, width, height, stride); + } +%} +%enddef + +CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGB) +CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGBA) +CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGR) +CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGRA) +CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGB) +CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA) +CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGR) +CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA) +#endif /* SWIGJAVA */ + +#ifdef SWIGPYTHON +%pythoncode %{ +_UNUSED = 1 +%} + +%define CALL_ENCODE_LOSSY_WRAPPER(func) +%pythoncode %{ +def func(rgb, width, height, stride, quality_factor): + """func(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp""" + webp = wrap_##func( + rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor) + if len(webp[0]) == 0: + return None + return webp[0] +%} +%enddef + +%define CALL_ENCODE_LOSSLESS_WRAPPER(func) +%pythoncode %{ +def func(rgb, width, height, stride): + """func(uint8_t rgb, int width, int height, int stride) -> lossless_webp""" + webp = wrap_##func(rgb, _UNUSED, _UNUSED, width, height, stride) + if len(webp[0]) == 0: + return None + return webp[0] +%} +%enddef + +CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGB) +CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGBA) +CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGR) +CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGRA) +CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGB) +CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA) +CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGR) +CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA) +#endif /* SWIGPYTHON */ diff --git a/internal/libwebp-1.2.1/swig/libwebp_gc.c b/internal/libwebp-1.2.1/swig/libwebp_gc.c new file mode 100644 index 0000000..308b7f8 --- /dev/null +++ b/internal/libwebp-1.2.1/swig/libwebp_gc.c @@ -0,0 +1,52 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 2.0.10 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + +/* This file should be compiled with 6c/8c. */ +#pragma dynimport _ _ "libwebp_go.so" + +#include "runtime.h" +#include "cgocall.h" + +#ifdef _64BIT +#define SWIG_PARM_SIZE 8 +#else +#define SWIG_PARM_SIZE 4 +#endif + +#pragma dynimport _wrap_WebPGetDecoderVersion _wrap_WebPGetDecoderVersion "" +extern void (*_wrap_WebPGetDecoderVersion)(void*); +static void (*x_wrap_WebPGetDecoderVersion)(void*) = _wrap_WebPGetDecoderVersion; + +void +·WebPGetDecoderVersion(struct { + uint8 x[SWIG_PARM_SIZE]; +} p) + +{ + runtime·cgocall(x_wrap_WebPGetDecoderVersion, &p); +} + + + +#pragma dynimport _wrap_wrapped_WebPGetInfo _wrap_wrapped_WebPGetInfo "" +extern void (*_wrap_wrapped_WebPGetInfo)(void*); +static void (*x_wrap_wrapped_WebPGetInfo)(void*) = _wrap_wrapped_WebPGetInfo; + +void +·Wrapped_WebPGetInfo(struct { + uint8 x[(2 * SWIG_PARM_SIZE) + (3 * SWIG_PARM_SIZE) + (3 * SWIG_PARM_SIZE) + SWIG_PARM_SIZE]; +} p) + +{ + runtime·cgocall(x_wrap_wrapped_WebPGetInfo, &p); +} + + + diff --git a/internal/libwebp-1.2.1/swig/libwebp_go_wrap.c b/internal/libwebp-1.2.1/swig/libwebp_go_wrap.c new file mode 100644 index 0000000..351d523 --- /dev/null +++ b/internal/libwebp-1.2.1/swig/libwebp_go_wrap.c @@ -0,0 +1,274 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 2.0.10 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ +#define SWIGMODULE libwebp +/* ----------------------------------------------------------------------------- + * This section contains generic SWIG labels for method/variable + * declarations/attributes, and other compiler dependent labels. + * ----------------------------------------------------------------------------- */ + +/* template workaround for compilers that cannot correctly implement the C++ standard */ +#ifndef SWIGTEMPLATEDISAMBIGUATOR +# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) +# define SWIGTEMPLATEDISAMBIGUATOR template +# elif defined(__HP_aCC) +/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ +/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ +# define SWIGTEMPLATEDISAMBIGUATOR template +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +#endif + +/* inline attribute */ +#ifndef SWIGINLINE +# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +# else +# define SWIGINLINE +# endif +#endif + +/* attribute recognised by some compilers to avoid 'unused' warnings */ +#ifndef SWIGUNUSED +# if defined(__GNUC__) +# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +# elif defined(__ICC) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +#endif + +#ifndef SWIG_MSC_UNSUPPRESS_4505 +# if defined(_MSC_VER) +# pragma warning(disable : 4505) /* unreferenced local function has been removed */ +# endif +#endif + +#ifndef SWIGUNUSEDPARM +# ifdef __cplusplus +# define SWIGUNUSEDPARM(p) +# else +# define SWIGUNUSEDPARM(p) p SWIGUNUSED +# endif +#endif + +/* internal SWIG method */ +#ifndef SWIGINTERN +# define SWIGINTERN static SWIGUNUSED +#endif + +/* internal inline SWIG method */ +#ifndef SWIGINTERNINLINE +# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE +#endif + +/* exporting methods */ +#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# ifndef GCC_HASCLASSVISIBILITY +# define GCC_HASCLASSVISIBILITY +# endif +#endif + +#ifndef SWIGEXPORT +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(STATIC_LINKED) +# define SWIGEXPORT +# else +# define SWIGEXPORT __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) +# define SWIGEXPORT __attribute__ ((visibility("default"))) +# else +# define SWIGEXPORT +# endif +# endif +#endif + +/* calling conventions for Windows */ +#ifndef SWIGSTDCALL +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define SWIGSTDCALL __stdcall +# else +# define SWIGSTDCALL +# endif +#endif + +/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ +#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +#endif + +/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ +#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) +# define _SCL_SECURE_NO_DEPRECATE +#endif + + + +#include +#include +#include +#include +#include + + + +typedef long long intgo; +typedef unsigned long long uintgo; + + + +typedef struct { char *p; intgo n; } _gostring_; +typedef struct { void* array; intgo len; intgo cap; } _goslice_; + + + + +#define swiggo_size_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1]; +#define swiggo_size_assert(t, n) swiggo_size_assert_eq(sizeof(t), n, swiggo_sizeof_##t##_is_not_##n) + +swiggo_size_assert(char, 1) +swiggo_size_assert(short, 2) +swiggo_size_assert(int, 4) +typedef long long swiggo_long_long; +swiggo_size_assert(swiggo_long_long, 8) +swiggo_size_assert(float, 4) +swiggo_size_assert(double, 8) + +#ifdef __cplusplus +extern "C" { +#endif +extern void crosscall2(void (*fn)(void *, int), void *, int); +extern void _cgo_allocate(void *, int); +extern void _cgo_panic(void *, int); +#ifdef __cplusplus +} +#endif + +static void *_swig_goallocate(size_t len) { + struct { + size_t len; + void *ret; + } a; + a.len = len; + crosscall2(_cgo_allocate, &a, (int) sizeof a); + return a.ret; +} + +static void _swig_gopanic(const char *p) { + struct { + const char *p; + } a; + a.p = p; + crosscall2(_cgo_panic, &a, (int) sizeof a); +} + + + + +static _gostring_ _swig_makegostring(const char *p, size_t l) { + _gostring_ ret; + ret.p = (char*)_swig_goallocate(l + 1); + memcpy(ret.p, p, l); + ret.n = l; + return ret; +} + +#define SWIG_contract_assert(expr, msg) \ + if (!(expr)) { _swig_gopanic(msg); } else + + +#define SWIG_exception(code, msg) _swig_gopanic(msg) + + +#include "webp/decode.h" +#include "webp/encode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void +_wrap_WebPGetDecoderVersion(void *swig_v) +{ + int result; + + struct swigargs { + long : 0; + intgo result; + } *swig_a = (struct swigargs *) swig_v; + + + result = (int)WebPGetDecoderVersion(); + swig_a->result = result; +} + + +void +_wrap_wrapped_WebPGetInfo(void *swig_v) +{ + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int temp3 ; + int temp4 ; + int result; + + struct swigargs { + _gostring_ arg1; + _goslice_ arg3; + _goslice_ arg4; + long : 0; + intgo result; + } *swig_a = (struct swigargs *) swig_v; + + + arg1 = (uint8_t *)swig_a->arg1.p; + arg2 = (size_t)swig_a->arg1.n; + + { + if (swig_a->arg3.len == 0) { + _swig_gopanic("array must contain at least 1 element"); + } + arg3 = &temp3; + } + { + if (swig_a->arg4.len == 0) { + _swig_gopanic("array must contain at least 1 element"); + } + arg4 = &temp4; + } + + result = (int)WebPGetInfo((uint8_t const *)arg1,arg2,arg3,arg4); + swig_a->result = result; + { + int* a = (int *) swig_a->arg3.array; + a[0] = temp3; + } + { + int* a = (int *) swig_a->arg4.array; + a[0] = temp4; + } + + +} + + +#ifdef __cplusplus +} +#endif + diff --git a/internal/libwebp-1.2.1/swig/libwebp_java_wrap.c b/internal/libwebp-1.2.1/swig/libwebp_java_wrap.c new file mode 100644 index 0000000..c8d4b13 --- /dev/null +++ b/internal/libwebp-1.2.1/swig/libwebp_java_wrap.c @@ -0,0 +1,1765 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 2.0.4 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + +#define SWIGJAVA + +/* ----------------------------------------------------------------------------- + * This section contains generic SWIG labels for method/variable + * declarations/attributes, and other compiler dependent labels. + * ----------------------------------------------------------------------------- */ + +/* template workaround for compilers that cannot correctly implement the C++ standard */ +#ifndef SWIGTEMPLATEDISAMBIGUATOR +# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) +# define SWIGTEMPLATEDISAMBIGUATOR template +# elif defined(__HP_aCC) +/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ +/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ +# define SWIGTEMPLATEDISAMBIGUATOR template +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +#endif + +/* inline attribute */ +#ifndef SWIGINLINE +# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +# else +# define SWIGINLINE +# endif +#endif + +/* attribute recognised by some compilers to avoid 'unused' warnings */ +#ifndef SWIGUNUSED +# if defined(__GNUC__) +# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +# elif defined(__ICC) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +#endif + +#ifndef SWIG_MSC_UNSUPPRESS_4505 +# if defined(_MSC_VER) +# pragma warning(disable : 4505) /* unreferenced local function has been removed */ +# endif +#endif + +#ifndef SWIGUNUSEDPARM +# ifdef __cplusplus +# define SWIGUNUSEDPARM(p) +# else +# define SWIGUNUSEDPARM(p) p SWIGUNUSED +# endif +#endif + +/* internal SWIG method */ +#ifndef SWIGINTERN +# define SWIGINTERN static SWIGUNUSED +#endif + +/* internal inline SWIG method */ +#ifndef SWIGINTERNINLINE +# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE +#endif + +/* exporting methods */ +#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# ifndef GCC_HASCLASSVISIBILITY +# define GCC_HASCLASSVISIBILITY +# endif +#endif + +#ifndef SWIGEXPORT +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(STATIC_LINKED) +# define SWIGEXPORT +# else +# define SWIGEXPORT __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) +# define SWIGEXPORT __attribute__ ((visibility("default"))) +# else +# define SWIGEXPORT +# endif +# endif +#endif + +/* calling conventions for Windows */ +#ifndef SWIGSTDCALL +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define SWIGSTDCALL __stdcall +# else +# define SWIGSTDCALL +# endif +#endif + +/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ +#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +#endif + +/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ +#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) +# define _SCL_SECURE_NO_DEPRECATE +#endif + + + +/* Fix for jlong on some versions of gcc on Windows */ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) + typedef long long __int64; +#endif + +/* Fix for jlong on 64-bit x86 Solaris */ +#if defined(__x86_64) +# ifdef _LP64 +# undef _LP64 +# endif +#endif + +#include +#include +#include + + +/* Support for throwing Java exceptions */ +typedef enum { + SWIG_JavaOutOfMemoryError = 1, + SWIG_JavaIOException, + SWIG_JavaRuntimeException, + SWIG_JavaIndexOutOfBoundsException, + SWIG_JavaArithmeticException, + SWIG_JavaIllegalArgumentException, + SWIG_JavaNullPointerException, + SWIG_JavaDirectorPureVirtual, + SWIG_JavaUnknownError +} SWIG_JavaExceptionCodes; + +typedef struct { + SWIG_JavaExceptionCodes code; + const char *java_exception; +} SWIG_JavaExceptions_t; + + +static void SWIGUNUSED SWIG_JavaThrowException(JNIEnv *jenv, SWIG_JavaExceptionCodes code, const char *msg) { + jclass excep; + static const SWIG_JavaExceptions_t java_exceptions[] = { + { SWIG_JavaOutOfMemoryError, "java/lang/OutOfMemoryError" }, + { SWIG_JavaIOException, "java/io/IOException" }, + { SWIG_JavaRuntimeException, "java/lang/RuntimeException" }, + { SWIG_JavaIndexOutOfBoundsException, "java/lang/IndexOutOfBoundsException" }, + { SWIG_JavaArithmeticException, "java/lang/ArithmeticException" }, + { SWIG_JavaIllegalArgumentException, "java/lang/IllegalArgumentException" }, + { SWIG_JavaNullPointerException, "java/lang/NullPointerException" }, + { SWIG_JavaDirectorPureVirtual, "java/lang/RuntimeException" }, + { SWIG_JavaUnknownError, "java/lang/UnknownError" }, + { (SWIG_JavaExceptionCodes)0, "java/lang/UnknownError" } + }; + const SWIG_JavaExceptions_t *except_ptr = java_exceptions; + + while (except_ptr->code != code && except_ptr->code) + except_ptr++; + + (*jenv)->ExceptionClear(jenv); + excep = (*jenv)->FindClass(jenv, except_ptr->java_exception); + if (excep) + (*jenv)->ThrowNew(jenv, excep, msg); +} + + +/* Contract support */ + +#define SWIG_contract_assert(nullreturn, expr, msg) if (!(expr)) {SWIG_JavaThrowException(jenv, SWIG_JavaIllegalArgumentException, msg); return nullreturn; } else + +/* Errors in SWIG */ +#define SWIG_UnknownError -1 +#define SWIG_IOError -2 +#define SWIG_RuntimeError -3 +#define SWIG_IndexError -4 +#define SWIG_TypeError -5 +#define SWIG_DivisionByZero -6 +#define SWIG_OverflowError -7 +#define SWIG_SyntaxError -8 +#define SWIG_ValueError -9 +#define SWIG_SystemError -10 +#define SWIG_AttributeError -11 +#define SWIG_MemoryError -12 +#define SWIG_NullReferenceError -13 + + + + +SWIGINTERN void SWIG_JavaException(JNIEnv *jenv, int code, const char *msg) { + SWIG_JavaExceptionCodes exception_code = SWIG_JavaUnknownError; + switch(code) { + case SWIG_MemoryError: + exception_code = SWIG_JavaOutOfMemoryError; + break; + case SWIG_IOError: + exception_code = SWIG_JavaIOException; + break; + case SWIG_SystemError: + case SWIG_RuntimeError: + exception_code = SWIG_JavaRuntimeException; + break; + case SWIG_OverflowError: + case SWIG_IndexError: + exception_code = SWIG_JavaIndexOutOfBoundsException; + break; + case SWIG_DivisionByZero: + exception_code = SWIG_JavaArithmeticException; + break; + case SWIG_SyntaxError: + case SWIG_ValueError: + case SWIG_TypeError: + exception_code = SWIG_JavaIllegalArgumentException; + break; + case SWIG_UnknownError: + default: + exception_code = SWIG_JavaUnknownError; + break; + } + SWIG_JavaThrowException(jenv, exception_code, msg); +} + + +#if defined(SWIG_NOINCLUDE) || defined(SWIG_NOARRAYS) + + +int SWIG_JavaArrayInSchar (JNIEnv *jenv, jbyte **jarr, signed char **carr, jbyteArray input); +void SWIG_JavaArrayArgoutSchar (JNIEnv *jenv, jbyte *jarr, signed char *carr, jbyteArray input); +jbyteArray SWIG_JavaArrayOutSchar (JNIEnv *jenv, signed char *result, jsize sz); + + +int SWIG_JavaArrayInUchar (JNIEnv *jenv, jshort **jarr, unsigned char **carr, jshortArray input); +void SWIG_JavaArrayArgoutUchar (JNIEnv *jenv, jshort *jarr, unsigned char *carr, jshortArray input); +jshortArray SWIG_JavaArrayOutUchar (JNIEnv *jenv, unsigned char *result, jsize sz); + + +int SWIG_JavaArrayInShort (JNIEnv *jenv, jshort **jarr, short **carr, jshortArray input); +void SWIG_JavaArrayArgoutShort (JNIEnv *jenv, jshort *jarr, short *carr, jshortArray input); +jshortArray SWIG_JavaArrayOutShort (JNIEnv *jenv, short *result, jsize sz); + + +int SWIG_JavaArrayInUshort (JNIEnv *jenv, jint **jarr, unsigned short **carr, jintArray input); +void SWIG_JavaArrayArgoutUshort (JNIEnv *jenv, jint *jarr, unsigned short *carr, jintArray input); +jintArray SWIG_JavaArrayOutUshort (JNIEnv *jenv, unsigned short *result, jsize sz); + + +int SWIG_JavaArrayInInt (JNIEnv *jenv, jint **jarr, int **carr, jintArray input); +void SWIG_JavaArrayArgoutInt (JNIEnv *jenv, jint *jarr, int *carr, jintArray input); +jintArray SWIG_JavaArrayOutInt (JNIEnv *jenv, int *result, jsize sz); + + +int SWIG_JavaArrayInUint (JNIEnv *jenv, jlong **jarr, unsigned int **carr, jlongArray input); +void SWIG_JavaArrayArgoutUint (JNIEnv *jenv, jlong *jarr, unsigned int *carr, jlongArray input); +jlongArray SWIG_JavaArrayOutUint (JNIEnv *jenv, unsigned int *result, jsize sz); + + +int SWIG_JavaArrayInLong (JNIEnv *jenv, jint **jarr, long **carr, jintArray input); +void SWIG_JavaArrayArgoutLong (JNIEnv *jenv, jint *jarr, long *carr, jintArray input); +jintArray SWIG_JavaArrayOutLong (JNIEnv *jenv, long *result, jsize sz); + + +int SWIG_JavaArrayInUlong (JNIEnv *jenv, jlong **jarr, unsigned long **carr, jlongArray input); +void SWIG_JavaArrayArgoutUlong (JNIEnv *jenv, jlong *jarr, unsigned long *carr, jlongArray input); +jlongArray SWIG_JavaArrayOutUlong (JNIEnv *jenv, unsigned long *result, jsize sz); + + +int SWIG_JavaArrayInLonglong (JNIEnv *jenv, jlong **jarr, jlong **carr, jlongArray input); +void SWIG_JavaArrayArgoutLonglong (JNIEnv *jenv, jlong *jarr, jlong *carr, jlongArray input); +jlongArray SWIG_JavaArrayOutLonglong (JNIEnv *jenv, jlong *result, jsize sz); + + +int SWIG_JavaArrayInFloat (JNIEnv *jenv, jfloat **jarr, float **carr, jfloatArray input); +void SWIG_JavaArrayArgoutFloat (JNIEnv *jenv, jfloat *jarr, float *carr, jfloatArray input); +jfloatArray SWIG_JavaArrayOutFloat (JNIEnv *jenv, float *result, jsize sz); + + +int SWIG_JavaArrayInDouble (JNIEnv *jenv, jdouble **jarr, double **carr, jdoubleArray input); +void SWIG_JavaArrayArgoutDouble (JNIEnv *jenv, jdouble *jarr, double *carr, jdoubleArray input); +jdoubleArray SWIG_JavaArrayOutDouble (JNIEnv *jenv, double *result, jsize sz); + + +#else + + +/* signed char[] support */ +int SWIG_JavaArrayInSchar (JNIEnv *jenv, jbyte **jarr, signed char **carr, jbyteArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetByteArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (signed char*) calloc(sz, sizeof(signed char)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseByteArrayElements(jenv, input, jarr, 0); +} + +jbyteArray SWIG_JavaArrayOutSchar (JNIEnv *jenv, signed char *result, jsize sz) { + jbyte *arr; + int i; + jbyteArray jresult = (*jenv)->NewByteArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetByteArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseByteArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* unsigned char[] support */ +int SWIG_JavaArrayInUchar (JNIEnv *jenv, jshort **jarr, unsigned char **carr, jshortArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetShortArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (unsigned char*) calloc(sz, sizeof(unsigned char)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseShortArrayElements(jenv, input, jarr, 0); +} + +jshortArray SWIG_JavaArrayOutUchar (JNIEnv *jenv, unsigned char *result, jsize sz) { + jshort *arr; + int i; + jshortArray jresult = (*jenv)->NewShortArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetShortArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseShortArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* short[] support */ +int SWIG_JavaArrayInShort (JNIEnv *jenv, jshort **jarr, short **carr, jshortArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetShortArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (short*) calloc(sz, sizeof(short)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseShortArrayElements(jenv, input, jarr, 0); +} + +jshortArray SWIG_JavaArrayOutShort (JNIEnv *jenv, short *result, jsize sz) { + jshort *arr; + int i; + jshortArray jresult = (*jenv)->NewShortArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetShortArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseShortArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* unsigned short[] support */ +int SWIG_JavaArrayInUshort (JNIEnv *jenv, jint **jarr, unsigned short **carr, jintArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetIntArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (unsigned short*) calloc(sz, sizeof(unsigned short)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseIntArrayElements(jenv, input, jarr, 0); +} + +jintArray SWIG_JavaArrayOutUshort (JNIEnv *jenv, unsigned short *result, jsize sz) { + jint *arr; + int i; + jintArray jresult = (*jenv)->NewIntArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetIntArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseIntArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* int[] support */ +int SWIG_JavaArrayInInt (JNIEnv *jenv, jint **jarr, int **carr, jintArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetIntArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (int*) calloc(sz, sizeof(int)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseIntArrayElements(jenv, input, jarr, 0); +} + +jintArray SWIG_JavaArrayOutInt (JNIEnv *jenv, int *result, jsize sz) { + jint *arr; + int i; + jintArray jresult = (*jenv)->NewIntArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetIntArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseIntArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* unsigned int[] support */ +int SWIG_JavaArrayInUint (JNIEnv *jenv, jlong **jarr, unsigned int **carr, jlongArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetLongArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (unsigned int*) calloc(sz, sizeof(unsigned int)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseLongArrayElements(jenv, input, jarr, 0); +} + +jlongArray SWIG_JavaArrayOutUint (JNIEnv *jenv, unsigned int *result, jsize sz) { + jlong *arr; + int i; + jlongArray jresult = (*jenv)->NewLongArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetLongArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseLongArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* long[] support */ +int SWIG_JavaArrayInLong (JNIEnv *jenv, jint **jarr, long **carr, jintArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetIntArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (long*) calloc(sz, sizeof(long)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseIntArrayElements(jenv, input, jarr, 0); +} + +jintArray SWIG_JavaArrayOutLong (JNIEnv *jenv, long *result, jsize sz) { + jint *arr; + int i; + jintArray jresult = (*jenv)->NewIntArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetIntArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseIntArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* unsigned long[] support */ +int SWIG_JavaArrayInUlong (JNIEnv *jenv, jlong **jarr, unsigned long **carr, jlongArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetLongArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (unsigned long*) calloc(sz, sizeof(unsigned long)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseLongArrayElements(jenv, input, jarr, 0); +} + +jlongArray SWIG_JavaArrayOutUlong (JNIEnv *jenv, unsigned long *result, jsize sz) { + jlong *arr; + int i; + jlongArray jresult = (*jenv)->NewLongArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetLongArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseLongArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* jlong[] support */ +int SWIG_JavaArrayInLonglong (JNIEnv *jenv, jlong **jarr, jlong **carr, jlongArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetLongArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (jlong*) calloc(sz, sizeof(jlong)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseLongArrayElements(jenv, input, jarr, 0); +} + +jlongArray SWIG_JavaArrayOutLonglong (JNIEnv *jenv, jlong *result, jsize sz) { + jlong *arr; + int i; + jlongArray jresult = (*jenv)->NewLongArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetLongArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseLongArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* float[] support */ +int SWIG_JavaArrayInFloat (JNIEnv *jenv, jfloat **jarr, float **carr, jfloatArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetFloatArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (float*) calloc(sz, sizeof(float)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseFloatArrayElements(jenv, input, jarr, 0); +} + +jfloatArray SWIG_JavaArrayOutFloat (JNIEnv *jenv, float *result, jsize sz) { + jfloat *arr; + int i; + jfloatArray jresult = (*jenv)->NewFloatArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetFloatArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseFloatArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +/* double[] support */ +int SWIG_JavaArrayInDouble (JNIEnv *jenv, jdouble **jarr, double **carr, jdoubleArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetDoubleArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (double*) calloc(sz, sizeof(double)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseDoubleArrayElements(jenv, input, jarr, 0); +} + +jdoubleArray SWIG_JavaArrayOutDouble (JNIEnv *jenv, double *result, jsize sz) { + jdouble *arr; + int i; + jdoubleArray jresult = (*jenv)->NewDoubleArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetDoubleArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseDoubleArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +#endif + + +#include "webp/types.h" + + +int SWIG_JavaArrayInUint8 (JNIEnv *jenv, jbyte **jarr, uint8_t **carr, jbyteArray input); +void SWIG_JavaArrayArgoutUint8 (JNIEnv *jenv, jbyte *jarr, uint8_t *carr, jbyteArray input); +jbyteArray SWIG_JavaArrayOutUint8 (JNIEnv *jenv, uint8_t *result, jsize sz); + + +/* uint8_t[] support */ +int SWIG_JavaArrayInUint8 (JNIEnv *jenv, jbyte **jarr, uint8_t **carr, jbyteArray input) { + int i; + jsize sz; + if (!input) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array"); + return 0; + } + sz = (*jenv)->GetArrayLength(jenv, input); + *jarr = (*jenv)->GetByteArrayElements(jenv, input, 0); + if (!*jarr) + return 0; + *carr = (uint8_t*) calloc(sz, sizeof(uint8_t)); + if (!*carr) { + SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed"); + return 0; + } + for (i=0; iGetArrayLength(jenv, input); + for (i=0; iReleaseByteArrayElements(jenv, input, jarr, 0); +} + +jbyteArray SWIG_JavaArrayOutUint8 (JNIEnv *jenv, uint8_t *result, jsize sz) { + jbyte *arr; + int i; + jbyteArray jresult = (*jenv)->NewByteArray(jenv, sz); + if (!jresult) + return NULL; + arr = (*jenv)->GetByteArrayElements(jenv, jresult, 0); + if (!arr) + return NULL; + for (i=0; iReleaseByteArrayElements(jenv, jresult, arr, 0); + return jresult; +} + + +#include "webp/decode.h" +#include "webp/encode.h" + + +#define FillMeInAsSizeCannotBeDeterminedAutomatically \ + (result ? (jint)ReturnedBufferSize(__FUNCTION__, arg3, arg4) : 0) + + +static size_t ReturnedBufferSize( + const char* function, int* width, int* height) { + static const struct sizemap { + const char* function; + int size_multiplier; + } size_map[] = { +#ifdef SWIGJAVA + { "Java_com_google_webp_libwebpJNI_WebPDecodeRGB", 3 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeRGBA", 4 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeARGB", 4 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeBGR", 3 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeBGRA", 4 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA", 1 }, +#endif +#ifdef SWIGPYTHON + { "WebPDecodeRGB", 3 }, + { "WebPDecodeRGBA", 4 }, + { "WebPDecodeARGB", 4 }, + { "WebPDecodeBGR", 3 }, + { "WebPDecodeBGRA", 4 }, + { "wrap_WebPEncodeRGB", 1 }, + { "wrap_WebPEncodeBGR", 1 }, + { "wrap_WebPEncodeRGBA", 1 }, + { "wrap_WebPEncodeBGRA", 1 }, + { "wrap_WebPEncodeLosslessRGB", 1 }, + { "wrap_WebPEncodeLosslessBGR", 1 }, + { "wrap_WebPEncodeLosslessRGBA", 1 }, + { "wrap_WebPEncodeLosslessBGRA", 1 }, +#endif + { NULL, 0 } + }; + const struct sizemap* p; + size_t size = 0; + + for (p = size_map; p->function; ++p) { + if (!strcmp(function, p->function)) { + size = *width * *height * p->size_multiplier; + break; + } + } + + return size; +} + + +typedef size_t (*WebPEncodeFunction)(const uint8_t* rgb, + int width, int height, int stride, + float quality_factor, uint8_t** output); +typedef size_t (*WebPEncodeLosslessFunction)(const uint8_t* rgb, + int width, int height, int stride, + uint8_t** output); + +static uint8_t* EncodeLossy(const uint8_t* rgb, + int width, int height, int stride, + float quality_factor, + WebPEncodeFunction encfn, + int* output_size, int* unused) { + uint8_t* output = NULL; + const size_t image_size = + encfn(rgb, width, height, stride, quality_factor, &output); + // the values of following two will be interpreted by ReturnedBufferSize() + // as 'width' and 'height' in the size calculation. + *output_size = image_size; + *unused = 1; + return image_size ? output : NULL; +} + +static uint8_t* EncodeLossless(const uint8_t* rgb, + int width, int height, int stride, + WebPEncodeLosslessFunction encfn, + int* output_size, int* unused) { + uint8_t* output = NULL; + const size_t image_size = encfn(rgb, width, height, stride, &output); + // the values of the following two will be interpreted by + // ReturnedBufferSize() as 'width' and 'height' in the size calculation. + *output_size = image_size; + *unused = 1; + return image_size ? output : NULL; +} + + +// Changes the return type of WebPEncode* to more closely match Decode*. +// This also makes it easier to wrap the output buffer in a native type rather +// than dealing with the return pointer. +// The additional parameters are to allow reuse of ReturnedBufferSize(), +// unused2 and output_size will be used in this case. +#define LOSSY_WRAPPER(FUNC) \ + static uint8_t* wrap_##FUNC( \ + const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \ + int width, int height, int stride, float quality_factor) { \ + return EncodeLossy(rgb, width, height, stride, quality_factor, \ + FUNC, output_size, unused2); \ + } \ + +LOSSY_WRAPPER(WebPEncodeRGB) +LOSSY_WRAPPER(WebPEncodeBGR) +LOSSY_WRAPPER(WebPEncodeRGBA) +LOSSY_WRAPPER(WebPEncodeBGRA) + +#undef LOSSY_WRAPPER + +#define LOSSLESS_WRAPPER(FUNC) \ + static uint8_t* wrap_##FUNC( \ + const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \ + int width, int height, int stride) { \ + return EncodeLossless(rgb, width, height, stride, \ + FUNC, output_size, unused2); \ + } \ + +LOSSLESS_WRAPPER(WebPEncodeLosslessRGB) +LOSSLESS_WRAPPER(WebPEncodeLosslessBGR) +LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA) +LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA) + +#undef LOSSLESS_WRAPPER + + + +/* Work around broken gcj jni.h */ +#ifdef __GCJ_JNI_H__ +# undef JNIEXPORT +# define JNIEXPORT +# undef JNICALL +# define JNICALL +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetDecoderVersion(JNIEnv *jenv, jclass jcls) { + jint jresult = 0 ; + int result; + + (void)jenv; + (void)jcls; + result = (int)WebPGetDecoderVersion(); + jresult = (jint)result; + return jresult; +} + + +SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetInfo(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) { + jint jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + jbyte *jarr1 ; + int temp3 ; + int temp4 ; + int result; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (size_t)jarg2; + { + if (!jarg3) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg3 = &temp3; + } + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + result = (int)WebPGetInfo((uint8_t const *)arg1,arg2,arg3,arg4); + jresult = (jint)result; + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp3; + (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue); + } + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeRGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + jbyte *jarr1 ; + int temp3 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (size_t)jarg2; + { + if (!jarg3) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg3 = &temp3; + } + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + result = (uint8_t *)WebPDecodeRGB((uint8_t const *)arg1,arg2,arg3,arg4); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp3; + (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue); + } + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeRGBA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + jbyte *jarr1 ; + int temp3 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (size_t)jarg2; + { + if (!jarg3) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg3 = &temp3; + } + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + result = (uint8_t *)WebPDecodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp3; + (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue); + } + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeARGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + jbyte *jarr1 ; + int temp3 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (size_t)jarg2; + { + if (!jarg3) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg3 = &temp3; + } + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + result = (uint8_t *)WebPDecodeARGB((uint8_t const *)arg1,arg2,arg3,arg4); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp3; + (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue); + } + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeBGR(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + jbyte *jarr1 ; + int temp3 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (size_t)jarg2; + { + if (!jarg3) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg3 = &temp3; + } + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + result = (uint8_t *)WebPDecodeBGR((uint8_t const *)arg1,arg2,arg3,arg4); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp3; + (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue); + } + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeBGRA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + jbyte *jarr1 ; + int temp3 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (size_t)jarg2; + { + if (!jarg3) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg3 = &temp3; + } + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + result = (uint8_t *)WebPDecodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp3; + (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue); + } + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + free(result); + return jresult; +} + + +SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetEncoderVersion(JNIEnv *jenv, jclass jcls) { + jint jresult = 0 ; + int result; + + (void)jenv; + (void)jcls; + result = (int)WebPGetEncoderVersion(); + jresult = (jint)result; + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + float arg8 ; + jbyte *jarr1 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (int *)&jarg2; + arg3 = (int *)&jarg3; + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + arg5 = (int)jarg5; + arg6 = (int)jarg6; + arg7 = (int)jarg7; + arg8 = (float)jarg8; + result = (uint8_t *)wrap_WebPEncodeRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + float arg8 ; + jbyte *jarr1 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (int *)&jarg2; + arg3 = (int *)&jarg3; + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + arg5 = (int)jarg5; + arg6 = (int)jarg6; + arg7 = (int)jarg7; + arg8 = (float)jarg8; + result = (uint8_t *)wrap_WebPEncodeBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + float arg8 ; + jbyte *jarr1 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (int *)&jarg2; + arg3 = (int *)&jarg3; + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + arg5 = (int)jarg5; + arg6 = (int)jarg6; + arg7 = (int)jarg7; + arg8 = (float)jarg8; + result = (uint8_t *)wrap_WebPEncodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + float arg8 ; + jbyte *jarr1 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (int *)&jarg2; + arg3 = (int *)&jarg3; + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + arg5 = (int)jarg5; + arg6 = (int)jarg6; + arg7 = (int)jarg7; + arg8 = (float)jarg8; + result = (uint8_t *)wrap_WebPEncodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + jbyte *jarr1 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (int *)&jarg2; + arg3 = (int *)&jarg3; + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + arg5 = (int)jarg5; + arg6 = (int)jarg6; + arg7 = (int)jarg7; + result = (uint8_t *)wrap_WebPEncodeLosslessRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + jbyte *jarr1 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (int *)&jarg2; + arg3 = (int *)&jarg3; + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + arg5 = (int)jarg5; + arg6 = (int)jarg6; + arg7 = (int)jarg7; + result = (uint8_t *)wrap_WebPEncodeLosslessBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + jbyte *jarr1 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (int *)&jarg2; + arg3 = (int *)&jarg3; + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + arg5 = (int)jarg5; + arg6 = (int)jarg6; + arg7 = (int)jarg7; + result = (uint8_t *)wrap_WebPEncodeLosslessRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + + free(result); + return jresult; +} + + +SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) { + jbyteArray jresult = 0 ; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + jbyte *jarr1 ; + int temp4 ; + uint8_t *result = 0 ; + + (void)jenv; + (void)jcls; + if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0; + arg2 = (int *)&jarg2; + arg3 = (int *)&jarg3; + { + if (!jarg4) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null"); + return 0; + } + if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) { + SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element"); + return 0; + } + arg4 = &temp4; + } + arg5 = (int)jarg5; + arg6 = (int)jarg6; + arg7 = (int)jarg7; + result = (uint8_t *)wrap_WebPEncodeLosslessBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7); + jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically); + SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1); + { + jint jvalue = (jint)temp4; + (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue); + } + free(arg1); + + + + free(result); + return jresult; +} + + +#ifdef __cplusplus +} +#endif + diff --git a/internal/libwebp-1.2.1/swig/libwebp_python_wrap.c b/internal/libwebp-1.2.1/swig/libwebp_python_wrap.c new file mode 100644 index 0000000..3aa2728 --- /dev/null +++ b/internal/libwebp-1.2.1/swig/libwebp_python_wrap.c @@ -0,0 +1,5628 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 3.0.12 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + +#define SWIG_PYTHON_STRICT_BYTE_CHAR + + + +#ifndef SWIGPYTHON +#define SWIGPYTHON +#endif + +#define SWIG_PYTHON_DIRECTOR_NO_VTABLE + +/* ----------------------------------------------------------------------------- + * This section contains generic SWIG labels for method/variable + * declarations/attributes, and other compiler dependent labels. + * ----------------------------------------------------------------------------- */ + +/* template workaround for compilers that cannot correctly implement the C++ standard */ +#ifndef SWIGTEMPLATEDISAMBIGUATOR +# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) +# define SWIGTEMPLATEDISAMBIGUATOR template +# elif defined(__HP_aCC) +/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ +/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ +# define SWIGTEMPLATEDISAMBIGUATOR template +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +#endif + +/* inline attribute */ +#ifndef SWIGINLINE +# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +# else +# define SWIGINLINE +# endif +#endif + +/* attribute recognised by some compilers to avoid 'unused' warnings */ +#ifndef SWIGUNUSED +# if defined(__GNUC__) +# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +# elif defined(__ICC) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +#endif + +#ifndef SWIG_MSC_UNSUPPRESS_4505 +# if defined(_MSC_VER) +# pragma warning(disable : 4505) /* unreferenced local function has been removed */ +# endif +#endif + +#ifndef SWIGUNUSEDPARM +# ifdef __cplusplus +# define SWIGUNUSEDPARM(p) +# else +# define SWIGUNUSEDPARM(p) p SWIGUNUSED +# endif +#endif + +/* internal SWIG method */ +#ifndef SWIGINTERN +# define SWIGINTERN static SWIGUNUSED +#endif + +/* internal inline SWIG method */ +#ifndef SWIGINTERNINLINE +# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE +#endif + +/* exporting methods */ +#if defined(__GNUC__) +# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# ifndef GCC_HASCLASSVISIBILITY +# define GCC_HASCLASSVISIBILITY +# endif +# endif +#endif + +#ifndef SWIGEXPORT +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(STATIC_LINKED) +# define SWIGEXPORT +# else +# define SWIGEXPORT __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) +# define SWIGEXPORT __attribute__ ((visibility("default"))) +# else +# define SWIGEXPORT +# endif +# endif +#endif + +/* calling conventions for Windows */ +#ifndef SWIGSTDCALL +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define SWIGSTDCALL __stdcall +# else +# define SWIGSTDCALL +# endif +#endif + +/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ +#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +#endif + +/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ +#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) +# define _SCL_SECURE_NO_DEPRECATE +#endif + +/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ +#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) +# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 +#endif + +/* Intel's compiler complains if a variable which was never initialised is + * cast to void, which is a common idiom which we use to indicate that we + * are aware a variable isn't used. So we just silence that warning. + * See: https://github.com/swig/swig/issues/192 for more discussion. + */ +#ifdef __INTEL_COMPILER +# pragma warning disable 592 +#endif + + +#if defined(_DEBUG) && defined(SWIG_PYTHON_INTERPRETER_NO_DEBUG) +/* Use debug wrappers with the Python release dll */ +# undef _DEBUG +# include +# define _DEBUG +#else +# include +#endif + +/* ----------------------------------------------------------------------------- + * swigrun.swg + * + * This file contains generic C API SWIG runtime support for pointer + * type checking. + * ----------------------------------------------------------------------------- */ + +/* This should only be incremented when either the layout of swig_type_info changes, + or for whatever reason, the runtime changes incompatibly */ +#define SWIG_RUNTIME_VERSION "4" + +/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ +#ifdef SWIG_TYPE_TABLE +# define SWIG_QUOTE_STRING(x) #x +# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) +# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) +#else +# define SWIG_TYPE_TABLE_NAME +#endif + +/* + You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for + creating a static or dynamic library from the SWIG runtime code. + In 99.9% of the cases, SWIG just needs to declare them as 'static'. + + But only do this if strictly necessary, ie, if you have problems + with your compiler or suchlike. +*/ + +#ifndef SWIGRUNTIME +# define SWIGRUNTIME SWIGINTERN +#endif + +#ifndef SWIGRUNTIMEINLINE +# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE +#endif + +/* Generic buffer size */ +#ifndef SWIG_BUFFER_SIZE +# define SWIG_BUFFER_SIZE 1024 +#endif + +/* Flags for pointer conversions */ +#define SWIG_POINTER_DISOWN 0x1 +#define SWIG_CAST_NEW_MEMORY 0x2 + +/* Flags for new pointer objects */ +#define SWIG_POINTER_OWN 0x1 + + +/* + Flags/methods for returning states. + + The SWIG conversion methods, as ConvertPtr, return an integer + that tells if the conversion was successful or not. And if not, + an error code can be returned (see swigerrors.swg for the codes). + + Use the following macros/flags to set or process the returning + states. + + In old versions of SWIG, code such as the following was usually written: + + if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) { + // success code + } else { + //fail code + } + + Now you can be more explicit: + + int res = SWIG_ConvertPtr(obj,vptr,ty.flags); + if (SWIG_IsOK(res)) { + // success code + } else { + // fail code + } + + which is the same really, but now you can also do + + Type *ptr; + int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags); + if (SWIG_IsOK(res)) { + // success code + if (SWIG_IsNewObj(res) { + ... + delete *ptr; + } else { + ... + } + } else { + // fail code + } + + I.e., now SWIG_ConvertPtr can return new objects and you can + identify the case and take care of the deallocation. Of course that + also requires SWIG_ConvertPtr to return new result values, such as + + int SWIG_ConvertPtr(obj, ptr,...) { + if () { + if () { + *ptr = ; + return SWIG_NEWOBJ; + } else { + *ptr = ; + return SWIG_OLDOBJ; + } + } else { + return SWIG_BADOBJ; + } + } + + Of course, returning the plain '0(success)/-1(fail)' still works, but you can be + more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the + SWIG errors code. + + Finally, if the SWIG_CASTRANK_MODE is enabled, the result code + allows to return the 'cast rank', for example, if you have this + + int food(double) + int fooi(int); + + and you call + + food(1) // cast rank '1' (1 -> 1.0) + fooi(1) // cast rank '0' + + just use the SWIG_AddCast()/SWIG_CheckState() +*/ + +#define SWIG_OK (0) +#define SWIG_ERROR (-1) +#define SWIG_IsOK(r) (r >= 0) +#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) + +/* The CastRankLimit says how many bits are used for the cast rank */ +#define SWIG_CASTRANKLIMIT (1 << 8) +/* The NewMask denotes the object was created (using new/malloc) */ +#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1) +/* The TmpMask is for in/out typemaps that use temporal objects */ +#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1) +/* Simple returning values */ +#define SWIG_BADOBJ (SWIG_ERROR) +#define SWIG_OLDOBJ (SWIG_OK) +#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) +#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) +/* Check, add and del mask methods */ +#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) +#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) +#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) +#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r) +#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r) +#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK)) + +/* Cast-Rank Mode */ +#if defined(SWIG_CASTRANK_MODE) +# ifndef SWIG_TypeRank +# define SWIG_TypeRank unsigned long +# endif +# ifndef SWIG_MAXCASTRANK /* Default cast allowed */ +# define SWIG_MAXCASTRANK (2) +# endif +# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1) +# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK) +SWIGINTERNINLINE int SWIG_AddCast(int r) { + return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r; +} +SWIGINTERNINLINE int SWIG_CheckState(int r) { + return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0; +} +#else /* no cast-rank mode */ +# define SWIG_AddCast(r) (r) +# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0) +#endif + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *(*swig_converter_func)(void *, int *); +typedef struct swig_type_info *(*swig_dycast_func)(void **); + +/* Structure to store information on one type */ +typedef struct swig_type_info { + const char *name; /* mangled name of this type */ + const char *str; /* human readable name of this type */ + swig_dycast_func dcast; /* dynamic cast function down a hierarchy */ + struct swig_cast_info *cast; /* linked list of types that can cast into this type */ + void *clientdata; /* language specific type data */ + int owndata; /* flag if the structure owns the clientdata */ +} swig_type_info; + +/* Structure to store a type and conversion function used for casting */ +typedef struct swig_cast_info { + swig_type_info *type; /* pointer to type that is equivalent to this type */ + swig_converter_func converter; /* function to cast the void pointers */ + struct swig_cast_info *next; /* pointer to next cast in linked list */ + struct swig_cast_info *prev; /* pointer to the previous cast */ +} swig_cast_info; + +/* Structure used to store module information + * Each module generates one structure like this, and the runtime collects + * all of these structures and stores them in a circularly linked list.*/ +typedef struct swig_module_info { + swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */ + size_t size; /* Number of types in this module */ + struct swig_module_info *next; /* Pointer to next element in circularly linked list */ + swig_type_info **type_initial; /* Array of initially generated type structures */ + swig_cast_info **cast_initial; /* Array of initially generated casting structures */ + void *clientdata; /* Language specific module data */ +} swig_module_info; + +/* + Compare two type names skipping the space characters, therefore + "char*" == "char *" and "Class" == "Class", etc. + + Return 0 when the two name types are equivalent, as in + strncmp, but skipping ' '. +*/ +SWIGRUNTIME int +SWIG_TypeNameComp(const char *f1, const char *l1, + const char *f2, const char *l2) { + for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { + while ((*f1 == ' ') && (f1 != l1)) ++f1; + while ((*f2 == ' ') && (f2 != l2)) ++f2; + if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1; + } + return (int)((l1 - f1) - (l2 - f2)); +} + +/* + Check type equivalence in a name list like ||... + Return 0 if equal, -1 if nb < tb, 1 if nb > tb +*/ +SWIGRUNTIME int +SWIG_TypeCmp(const char *nb, const char *tb) { + int equiv = 1; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (equiv != 0 && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = SWIG_TypeNameComp(nb, ne, tb, te); + if (*ne) ++ne; + } + return equiv; +} + +/* + Check type equivalence in a name list like ||... + Return 0 if not equal, 1 if equal +*/ +SWIGRUNTIME int +SWIG_TypeEquiv(const char *nb, const char *tb) { + return SWIG_TypeCmp(nb, tb) == 0 ? 1 : 0; +} + +/* + Check the typename +*/ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheck(const char *c, swig_type_info *ty) { + if (ty) { + swig_cast_info *iter = ty->cast; + while (iter) { + if (strcmp(iter->type->name, c) == 0) { + if (iter == ty->cast) + return iter; + /* Move iter to the top of the linked list */ + iter->prev->next = iter->next; + if (iter->next) + iter->next->prev = iter->prev; + iter->next = ty->cast; + iter->prev = 0; + if (ty->cast) ty->cast->prev = iter; + ty->cast = iter; + return iter; + } + iter = iter->next; + } + } + return 0; +} + +/* + Identical to SWIG_TypeCheck, except strcmp is replaced with a pointer comparison +*/ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *ty) { + if (ty) { + swig_cast_info *iter = ty->cast; + while (iter) { + if (iter->type == from) { + if (iter == ty->cast) + return iter; + /* Move iter to the top of the linked list */ + iter->prev->next = iter->next; + if (iter->next) + iter->next->prev = iter->prev; + iter->next = ty->cast; + iter->prev = 0; + if (ty->cast) ty->cast->prev = iter; + ty->cast = iter; + return iter; + } + iter = iter->next; + } + } + return 0; +} + +/* + Cast a pointer up an inheritance hierarchy +*/ +SWIGRUNTIMEINLINE void * +SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) { + return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory); +} + +/* + Dynamic pointer casting. Down an inheritance hierarchy +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { + swig_type_info *lastty = ty; + if (!ty || !ty->dcast) return ty; + while (ty && (ty->dcast)) { + ty = (*ty->dcast)(ptr); + if (ty) lastty = ty; + } + return lastty; +} + +/* + Return the name associated with this type +*/ +SWIGRUNTIMEINLINE const char * +SWIG_TypeName(const swig_type_info *ty) { + return ty->name; +} + +/* + Return the pretty name associated with this type, + that is an unmangled type name in a form presentable to the user. +*/ +SWIGRUNTIME const char * +SWIG_TypePrettyName(const swig_type_info *type) { + /* The "str" field contains the equivalent pretty names of the + type, separated by vertical-bar characters. We choose + to print the last name, as it is often (?) the most + specific. */ + if (!type) return NULL; + if (type->str != NULL) { + const char *last_name = type->str; + const char *s; + for (s = type->str; *s; s++) + if (*s == '|') last_name = s+1; + return last_name; + } + else + return type->name; +} + +/* + Set the clientdata field for a type +*/ +SWIGRUNTIME void +SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { + swig_cast_info *cast = ti->cast; + /* if (ti->clientdata == clientdata) return; */ + ti->clientdata = clientdata; + + while (cast) { + if (!cast->converter) { + swig_type_info *tc = cast->type; + if (!tc->clientdata) { + SWIG_TypeClientData(tc, clientdata); + } + } + cast = cast->next; + } +} +SWIGRUNTIME void +SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) { + SWIG_TypeClientData(ti, clientdata); + ti->owndata = 1; +} + +/* + Search for a swig_type_info structure only by mangled name + Search is a O(log #types) + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_MangledTypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + swig_module_info *iter = start; + do { + if (iter->size) { + size_t l = 0; + size_t r = iter->size - 1; + do { + /* since l+r >= 0, we can (>> 1) instead (/ 2) */ + size_t i = (l + r) >> 1; + const char *iname = iter->types[i]->name; + if (iname) { + int compare = strcmp(name, iname); + if (compare == 0) { + return iter->types[i]; + } else if (compare < 0) { + if (i) { + r = i - 1; + } else { + break; + } + } else if (compare > 0) { + l = i + 1; + } + } else { + break; /* should never happen */ + } + } while (l <= r); + } + iter = iter->next; + } while (iter != end); + return 0; +} + +/* + Search for a swig_type_info structure for either a mangled name or a human readable name. + It first searches the mangled names of the types, which is a O(log #types) + If a type is not found it then searches the human readable names, which is O(#types). + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + /* STEP 1: Search the name field using binary search */ + swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name); + if (ret) { + return ret; + } else { + /* STEP 2: If the type hasn't been found, do a complete search + of the str field (the human readable name) */ + swig_module_info *iter = start; + do { + size_t i = 0; + for (; i < iter->size; ++i) { + if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name))) + return iter->types[i]; + } + iter = iter->next; + } while (iter != end); + } + + /* neither found a match */ + return 0; +} + +/* + Pack binary data into a string +*/ +SWIGRUNTIME char * +SWIG_PackData(char *c, void *ptr, size_t sz) { + static const char hex[17] = "0123456789abcdef"; + const unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + unsigned char uu = *u; + *(c++) = hex[(uu & 0xf0) >> 4]; + *(c++) = hex[uu & 0xf]; + } + return c; +} + +/* + Unpack binary data from a string +*/ +SWIGRUNTIME const char * +SWIG_UnpackData(const char *c, void *ptr, size_t sz) { + unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + char d = *(c++); + unsigned char uu; + if ((d >= '0') && (d <= '9')) + uu = (unsigned char)((d - '0') << 4); + else if ((d >= 'a') && (d <= 'f')) + uu = (unsigned char)((d - ('a'-10)) << 4); + else + return (char *) 0; + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu |= (unsigned char)(d - '0'); + else if ((d >= 'a') && (d <= 'f')) + uu |= (unsigned char)(d - ('a'-10)); + else + return (char *) 0; + *u = uu; + } + return c; +} + +/* + Pack 'void *' into a string buffer. +*/ +SWIGRUNTIME char * +SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) { + char *r = buff; + if ((2*sizeof(void *) + 2) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,&ptr,sizeof(void *)); + if (strlen(name) + 1 > (bsz - (r - buff))) return 0; + strcpy(r,name); + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + *ptr = (void *) 0; + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sizeof(void *)); +} + +SWIGRUNTIME char * +SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) { + char *r = buff; + size_t lname = (name ? strlen(name) : 0); + if ((2*sz + 2 + lname) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + if (lname) { + strncpy(r,name,lname+1); + } else { + *r = 0; + } + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + memset(ptr,0,sz); + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sz); +} + +#ifdef __cplusplus +} +#endif + +/* Errors in SWIG */ +#define SWIG_UnknownError -1 +#define SWIG_IOError -2 +#define SWIG_RuntimeError -3 +#define SWIG_IndexError -4 +#define SWIG_TypeError -5 +#define SWIG_DivisionByZero -6 +#define SWIG_OverflowError -7 +#define SWIG_SyntaxError -8 +#define SWIG_ValueError -9 +#define SWIG_SystemError -10 +#define SWIG_AttributeError -11 +#define SWIG_MemoryError -12 +#define SWIG_NullReferenceError -13 + + + +/* Compatibility macros for Python 3 */ +#if PY_VERSION_HEX >= 0x03000000 + +#define PyClass_Check(obj) PyObject_IsInstance(obj, (PyObject *)&PyType_Type) +#define PyInt_Check(x) PyLong_Check(x) +#define PyInt_AsLong(x) PyLong_AsLong(x) +#define PyInt_FromLong(x) PyLong_FromLong(x) +#define PyInt_FromSize_t(x) PyLong_FromSize_t(x) +#define PyString_Check(name) PyBytes_Check(name) +#define PyString_FromString(x) PyUnicode_FromString(x) +#define PyString_FromStringAndSize(x, y) PyBytes_FromStringAndSize(x, y) +#define PyString_Format(fmt, args) PyUnicode_Format(fmt, args) +#define PyString_AsString(str) PyBytes_AsString(str) +#define PyString_Size(str) PyBytes_Size(str) +#define PyString_InternFromString(key) PyUnicode_InternFromString(key) +#define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE +#define PyString_AS_STRING(x) PyUnicode_AS_STRING(x) +#define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x) + +#endif + +#ifndef Py_TYPE +# define Py_TYPE(op) ((op)->ob_type) +#endif + +/* SWIG APIs for compatibility of both Python 2 & 3 */ + +#if PY_VERSION_HEX >= 0x03000000 +# define SWIG_Python_str_FromFormat PyUnicode_FromFormat +#else +# define SWIG_Python_str_FromFormat PyString_FromFormat +#endif + + +/* Warning: This function will allocate a new string in Python 3, + * so please call SWIG_Python_str_DelForPy3(x) to free the space. + */ +SWIGINTERN char* +SWIG_Python_str_AsChar(PyObject *str) +{ +#if PY_VERSION_HEX >= 0x03000000 + char *cstr; + char *newstr; + Py_ssize_t len; + str = PyUnicode_AsUTF8String(str); + PyBytes_AsStringAndSize(str, &cstr, &len); + newstr = (char *) malloc(len+1); + memcpy(newstr, cstr, len+1); + Py_XDECREF(str); + return newstr; +#else + return PyString_AsString(str); +#endif +} + +#if PY_VERSION_HEX >= 0x03000000 +# define SWIG_Python_str_DelForPy3(x) free( (void*) (x) ) +#else +# define SWIG_Python_str_DelForPy3(x) +#endif + + +SWIGINTERN PyObject* +SWIG_Python_str_FromChar(const char *c) +{ +#if PY_VERSION_HEX >= 0x03000000 + return PyUnicode_FromString(c); +#else + return PyString_FromString(c); +#endif +} + +/* Add PyOS_snprintf for old Pythons */ +#if PY_VERSION_HEX < 0x02020000 +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM) +# define PyOS_snprintf _snprintf +# else +# define PyOS_snprintf snprintf +# endif +#endif + +/* A crude PyString_FromFormat implementation for old Pythons */ +#if PY_VERSION_HEX < 0x02020000 + +#ifndef SWIG_PYBUFFER_SIZE +# define SWIG_PYBUFFER_SIZE 1024 +#endif + +static PyObject * +PyString_FromFormat(const char *fmt, ...) { + va_list ap; + char buf[SWIG_PYBUFFER_SIZE * 2]; + int res; + va_start(ap, fmt); + res = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return (res < 0 || res >= (int)sizeof(buf)) ? 0 : PyString_FromString(buf); +} +#endif + +#ifndef PyObject_DEL +# define PyObject_DEL PyObject_Del +#endif + +/* A crude PyExc_StopIteration exception for old Pythons */ +#if PY_VERSION_HEX < 0x02020000 +# ifndef PyExc_StopIteration +# define PyExc_StopIteration PyExc_RuntimeError +# endif +# ifndef PyObject_GenericGetAttr +# define PyObject_GenericGetAttr 0 +# endif +#endif + +/* Py_NotImplemented is defined in 2.1 and up. */ +#if PY_VERSION_HEX < 0x02010000 +# ifndef Py_NotImplemented +# define Py_NotImplemented PyExc_RuntimeError +# endif +#endif + +/* A crude PyString_AsStringAndSize implementation for old Pythons */ +#if PY_VERSION_HEX < 0x02010000 +# ifndef PyString_AsStringAndSize +# define PyString_AsStringAndSize(obj, s, len) {*s = PyString_AsString(obj); *len = *s ? strlen(*s) : 0;} +# endif +#endif + +/* PySequence_Size for old Pythons */ +#if PY_VERSION_HEX < 0x02000000 +# ifndef PySequence_Size +# define PySequence_Size PySequence_Length +# endif +#endif + +/* PyBool_FromLong for old Pythons */ +#if PY_VERSION_HEX < 0x02030000 +static +PyObject *PyBool_FromLong(long ok) +{ + PyObject *result = ok ? Py_True : Py_False; + Py_INCREF(result); + return result; +} +#endif + +/* Py_ssize_t for old Pythons */ +/* This code is as recommended by: */ +/* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */ +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +# define PY_SSIZE_T_MAX INT_MAX +# define PY_SSIZE_T_MIN INT_MIN +typedef inquiry lenfunc; +typedef intargfunc ssizeargfunc; +typedef intintargfunc ssizessizeargfunc; +typedef intobjargproc ssizeobjargproc; +typedef intintobjargproc ssizessizeobjargproc; +typedef getreadbufferproc readbufferproc; +typedef getwritebufferproc writebufferproc; +typedef getsegcountproc segcountproc; +typedef getcharbufferproc charbufferproc; +static long PyNumber_AsSsize_t (PyObject *x, void *SWIGUNUSEDPARM(exc)) +{ + long result = 0; + PyObject *i = PyNumber_Int(x); + if (i) { + result = PyInt_AsLong(i); + Py_DECREF(i); + } + return result; +} +#endif + +#if PY_VERSION_HEX < 0x02050000 +#define PyInt_FromSize_t(x) PyInt_FromLong((long)x) +#endif + +#if PY_VERSION_HEX < 0x02040000 +#define Py_VISIT(op) \ + do { \ + if (op) { \ + int vret = visit((op), arg); \ + if (vret) \ + return vret; \ + } \ + } while (0) +#endif + +#if PY_VERSION_HEX < 0x02030000 +typedef struct { + PyTypeObject type; + PyNumberMethods as_number; + PyMappingMethods as_mapping; + PySequenceMethods as_sequence; + PyBufferProcs as_buffer; + PyObject *name, *slots; +} PyHeapTypeObject; +#endif + +#if PY_VERSION_HEX < 0x02030000 +typedef destructor freefunc; +#endif + +#if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 6) || \ + (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 0) || \ + (PY_MAJOR_VERSION > 3)) +# define SWIGPY_USE_CAPSULE +# define SWIGPY_CAPSULE_NAME ((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION ".type_pointer_capsule" SWIG_TYPE_TABLE_NAME) +#endif + +#if PY_VERSION_HEX < 0x03020000 +#define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type) +#define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name) +#define Py_hash_t long +#endif + +/* ----------------------------------------------------------------------------- + * error manipulation + * ----------------------------------------------------------------------------- */ + +SWIGRUNTIME PyObject* +SWIG_Python_ErrorType(int code) { + PyObject* type = 0; + switch(code) { + case SWIG_MemoryError: + type = PyExc_MemoryError; + break; + case SWIG_IOError: + type = PyExc_IOError; + break; + case SWIG_RuntimeError: + type = PyExc_RuntimeError; + break; + case SWIG_IndexError: + type = PyExc_IndexError; + break; + case SWIG_TypeError: + type = PyExc_TypeError; + break; + case SWIG_DivisionByZero: + type = PyExc_ZeroDivisionError; + break; + case SWIG_OverflowError: + type = PyExc_OverflowError; + break; + case SWIG_SyntaxError: + type = PyExc_SyntaxError; + break; + case SWIG_ValueError: + type = PyExc_ValueError; + break; + case SWIG_SystemError: + type = PyExc_SystemError; + break; + case SWIG_AttributeError: + type = PyExc_AttributeError; + break; + default: + type = PyExc_RuntimeError; + } + return type; +} + + +SWIGRUNTIME void +SWIG_Python_AddErrorMsg(const char* mesg) +{ + PyObject *type = 0; + PyObject *value = 0; + PyObject *traceback = 0; + + if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback); + if (value) { + char *tmp; + PyObject *old_str = PyObject_Str(value); + PyErr_Clear(); + Py_XINCREF(type); + + PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg); + SWIG_Python_str_DelForPy3(tmp); + Py_DECREF(old_str); + Py_DECREF(value); + } else { + PyErr_SetString(PyExc_RuntimeError, mesg); + } +} + +#if defined(SWIG_PYTHON_NO_THREADS) +# if defined(SWIG_PYTHON_THREADS) +# undef SWIG_PYTHON_THREADS +# endif +#endif +#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */ +# if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL) +# if (PY_VERSION_HEX >= 0x02030000) /* For 2.3 or later, use the PyGILState calls */ +# define SWIG_PYTHON_USE_GIL +# endif +# endif +# if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */ +# ifndef SWIG_PYTHON_INITIALIZE_THREADS +# define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads() +# endif +# ifdef __cplusplus /* C++ code */ + class SWIG_Python_Thread_Block { + bool status; + PyGILState_STATE state; + public: + void end() { if (status) { PyGILState_Release(state); status = false;} } + SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {} + ~SWIG_Python_Thread_Block() { end(); } + }; + class SWIG_Python_Thread_Allow { + bool status; + PyThreadState *save; + public: + void end() { if (status) { PyEval_RestoreThread(save); status = false; }} + SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {} + ~SWIG_Python_Thread_Allow() { end(); } + }; +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block +# define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end() +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow +# define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end() +# else /* C code */ +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure() +# define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block) +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread() +# define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow) +# endif +# else /* Old thread way, not implemented, user must provide it */ +# if !defined(SWIG_PYTHON_INITIALIZE_THREADS) +# define SWIG_PYTHON_INITIALIZE_THREADS +# endif +# if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK) +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK +# endif +# if !defined(SWIG_PYTHON_THREAD_END_BLOCK) +# define SWIG_PYTHON_THREAD_END_BLOCK +# endif +# if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW) +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW +# endif +# if !defined(SWIG_PYTHON_THREAD_END_ALLOW) +# define SWIG_PYTHON_THREAD_END_ALLOW +# endif +# endif +#else /* No thread support */ +# define SWIG_PYTHON_INITIALIZE_THREADS +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK +# define SWIG_PYTHON_THREAD_END_BLOCK +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW +# define SWIG_PYTHON_THREAD_END_ALLOW +#endif + +/* ----------------------------------------------------------------------------- + * Python API portion that goes into the runtime + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------------------------- + * Constant declarations + * ----------------------------------------------------------------------------- */ + +/* Constant Types */ +#define SWIG_PY_POINTER 4 +#define SWIG_PY_BINARY 5 + +/* Constant information structure */ +typedef struct swig_const_info { + int type; + char *name; + long lvalue; + double dvalue; + void *pvalue; + swig_type_info **ptype; +} swig_const_info; + + +/* ----------------------------------------------------------------------------- + * Wrapper of PyInstanceMethod_New() used in Python 3 + * It is exported to the generated module, used for -fastproxy + * ----------------------------------------------------------------------------- */ +#if PY_VERSION_HEX >= 0x03000000 +SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func) +{ + return PyInstanceMethod_New(func); +} +#else +SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *SWIGUNUSEDPARM(func)) +{ + return NULL; +} +#endif + +#ifdef __cplusplus +} +#endif + + +/* ----------------------------------------------------------------------------- + * pyrun.swg + * + * This file contains the runtime support for Python modules + * and includes code for managing global variables and pointer + * type checking. + * + * ----------------------------------------------------------------------------- */ + +/* Common SWIG API */ + +/* for raw pointers */ +#define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0) +#define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags) +#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own) + +#ifdef SWIGPYTHON_BUILTIN +#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(self, ptr, type, flags) +#else +#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) +#endif + +#define SWIG_InternalNewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) + +#define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty) +#define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src) +#define swig_owntype int + +/* for raw packed data */ +#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) +#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) + +/* for class or struct pointers */ +#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags) +#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags) + +/* for C or C++ function pointers */ +#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type) +#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(NULL, ptr, type, 0) + +/* for C++ member pointers, ie, member methods */ +#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) +#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) + + +/* Runtime API */ + +#define SWIG_GetModule(clientdata) SWIG_Python_GetModule(clientdata) +#define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer) +#define SWIG_NewClientData(obj) SwigPyClientData_New(obj) + +#define SWIG_SetErrorObj SWIG_Python_SetErrorObj +#define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg +#define SWIG_ErrorType(code) SWIG_Python_ErrorType(code) +#define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg) +#define SWIG_fail goto fail + + +/* Runtime API implementation */ + +/* Error manipulation */ + +SWIGINTERN void +SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyErr_SetObject(errtype, obj); + Py_DECREF(obj); + SWIG_PYTHON_THREAD_END_BLOCK; +} + +SWIGINTERN void +SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyErr_SetString(errtype, msg); + SWIG_PYTHON_THREAD_END_BLOCK; +} + +#define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj) + +/* Set a constant value */ + +#if defined(SWIGPYTHON_BUILTIN) + +SWIGINTERN void +SwigPyBuiltin_AddPublicSymbol(PyObject *seq, const char *key) { + PyObject *s = PyString_InternFromString(key); + PyList_Append(seq, s); + Py_DECREF(s); +} + +SWIGINTERN void +SWIG_Python_SetConstant(PyObject *d, PyObject *public_interface, const char *name, PyObject *obj) { +#if PY_VERSION_HEX < 0x02030000 + PyDict_SetItemString(d, (char *)name, obj); +#else + PyDict_SetItemString(d, name, obj); +#endif + Py_DECREF(obj); + if (public_interface) + SwigPyBuiltin_AddPublicSymbol(public_interface, name); +} + +#else + +SWIGINTERN void +SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) { +#if PY_VERSION_HEX < 0x02030000 + PyDict_SetItemString(d, (char *)name, obj); +#else + PyDict_SetItemString(d, name, obj); +#endif + Py_DECREF(obj); +} + +#endif + +/* Append a value to the result obj */ + +SWIGINTERN PyObject* +SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) { +#if !defined(SWIG_PYTHON_OUTPUT_TUPLE) + if (!result) { + result = obj; + } else if (result == Py_None) { + Py_DECREF(result); + result = obj; + } else { + if (!PyList_Check(result)) { + PyObject *o2 = result; + result = PyList_New(1); + PyList_SetItem(result, 0, o2); + } + PyList_Append(result,obj); + Py_DECREF(obj); + } + return result; +#else + PyObject* o2; + PyObject* o3; + if (!result) { + result = obj; + } else if (result == Py_None) { + Py_DECREF(result); + result = obj; + } else { + if (!PyTuple_Check(result)) { + o2 = result; + result = PyTuple_New(1); + PyTuple_SET_ITEM(result, 0, o2); + } + o3 = PyTuple_New(1); + PyTuple_SET_ITEM(o3, 0, obj); + o2 = result; + result = PySequence_Concat(o2, o3); + Py_DECREF(o2); + Py_DECREF(o3); + } + return result; +#endif +} + +/* Unpack the argument tuple */ + +SWIGINTERN Py_ssize_t +SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs) +{ + if (!args) { + if (!min && !max) { + return 1; + } else { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none", + name, (min == max ? "" : "at least "), (int)min); + return 0; + } + } + if (!PyTuple_Check(args)) { + if (min <= 1 && max >= 1) { + Py_ssize_t i; + objs[0] = args; + for (i = 1; i < max; ++i) { + objs[i] = 0; + } + return 2; + } + PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple"); + return 0; + } else { + Py_ssize_t l = PyTuple_GET_SIZE(args); + if (l < min) { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at least "), (int)min, (int)l); + return 0; + } else if (l > max) { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at most "), (int)max, (int)l); + return 0; + } else { + Py_ssize_t i; + for (i = 0; i < l; ++i) { + objs[i] = PyTuple_GET_ITEM(args, i); + } + for (; l < max; ++l) { + objs[l] = 0; + } + return i + 1; + } + } +} + +/* A functor is a function object with one single object argument */ +#if PY_VERSION_HEX >= 0x02020000 +#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL); +#else +#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunction(functor, "O", obj); +#endif + +/* + Helper for static pointer initialization for both C and C++ code, for example + static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...); +*/ +#ifdef __cplusplus +#define SWIG_STATIC_POINTER(var) var +#else +#define SWIG_STATIC_POINTER(var) var = 0; if (!var) var +#endif + +/* ----------------------------------------------------------------------------- + * Pointer declarations + * ----------------------------------------------------------------------------- */ + +/* Flags for new pointer objects */ +#define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1) +#define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN) + +#define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1) + +#define SWIG_BUILTIN_TP_INIT (SWIG_POINTER_OWN << 2) +#define SWIG_BUILTIN_INIT (SWIG_BUILTIN_TP_INIT | SWIG_POINTER_OWN) + +#ifdef __cplusplus +extern "C" { +#endif + +/* How to access Py_None */ +#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# ifndef SWIG_PYTHON_NO_BUILD_NONE +# ifndef SWIG_PYTHON_BUILD_NONE +# define SWIG_PYTHON_BUILD_NONE +# endif +# endif +#endif + +#ifdef SWIG_PYTHON_BUILD_NONE +# ifdef Py_None +# undef Py_None +# define Py_None SWIG_Py_None() +# endif +SWIGRUNTIMEINLINE PyObject * +_SWIG_Py_None(void) +{ + PyObject *none = Py_BuildValue((char*)""); + Py_DECREF(none); + return none; +} +SWIGRUNTIME PyObject * +SWIG_Py_None(void) +{ + static PyObject *SWIG_STATIC_POINTER(none) = _SWIG_Py_None(); + return none; +} +#endif + +/* The python void return value */ + +SWIGRUNTIMEINLINE PyObject * +SWIG_Py_Void(void) +{ + PyObject *none = Py_None; + Py_INCREF(none); + return none; +} + +/* SwigPyClientData */ + +typedef struct { + PyObject *klass; + PyObject *newraw; + PyObject *newargs; + PyObject *destroy; + int delargs; + int implicitconv; + PyTypeObject *pytype; +} SwigPyClientData; + +SWIGRUNTIMEINLINE int +SWIG_Python_CheckImplicit(swig_type_info *ty) +{ + SwigPyClientData *data = (SwigPyClientData *)ty->clientdata; + return data ? data->implicitconv : 0; +} + +SWIGRUNTIMEINLINE PyObject * +SWIG_Python_ExceptionType(swig_type_info *desc) { + SwigPyClientData *data = desc ? (SwigPyClientData *) desc->clientdata : 0; + PyObject *klass = data ? data->klass : 0; + return (klass ? klass : PyExc_RuntimeError); +} + + +SWIGRUNTIME SwigPyClientData * +SwigPyClientData_New(PyObject* obj) +{ + if (!obj) { + return 0; + } else { + SwigPyClientData *data = (SwigPyClientData *)malloc(sizeof(SwigPyClientData)); + /* the klass element */ + data->klass = obj; + Py_INCREF(data->klass); + /* the newraw method and newargs arguments used to create a new raw instance */ + if (PyClass_Check(obj)) { + data->newraw = 0; + data->newargs = obj; + Py_INCREF(obj); + } else { +#if (PY_VERSION_HEX < 0x02020000) + data->newraw = 0; +#else + data->newraw = PyObject_GetAttrString(data->klass, (char *)"__new__"); +#endif + if (data->newraw) { + Py_INCREF(data->newraw); + data->newargs = PyTuple_New(1); + PyTuple_SetItem(data->newargs, 0, obj); + } else { + data->newargs = obj; + } + Py_INCREF(data->newargs); + } + /* the destroy method, aka as the C++ delete method */ + data->destroy = PyObject_GetAttrString(data->klass, (char *)"__swig_destroy__"); + if (PyErr_Occurred()) { + PyErr_Clear(); + data->destroy = 0; + } + if (data->destroy) { + int flags; + Py_INCREF(data->destroy); + flags = PyCFunction_GET_FLAGS(data->destroy); +#ifdef METH_O + data->delargs = !(flags & (METH_O)); +#else + data->delargs = 0; +#endif + } else { + data->delargs = 0; + } + data->implicitconv = 0; + data->pytype = 0; + return data; + } +} + +SWIGRUNTIME void +SwigPyClientData_Del(SwigPyClientData *data) { + Py_XDECREF(data->newraw); + Py_XDECREF(data->newargs); + Py_XDECREF(data->destroy); +} + +/* =============== SwigPyObject =====================*/ + +typedef struct { + PyObject_HEAD + void *ptr; + swig_type_info *ty; + int own; + PyObject *next; +#ifdef SWIGPYTHON_BUILTIN + PyObject *dict; +#endif +} SwigPyObject; + + +#ifdef SWIGPYTHON_BUILTIN + +SWIGRUNTIME PyObject * +SwigPyObject_get___dict__(PyObject *v, PyObject *SWIGUNUSEDPARM(args)) +{ + SwigPyObject *sobj = (SwigPyObject *)v; + + if (!sobj->dict) + sobj->dict = PyDict_New(); + + Py_INCREF(sobj->dict); + return sobj->dict; +} + +#endif + +SWIGRUNTIME PyObject * +SwigPyObject_long(SwigPyObject *v) +{ + return PyLong_FromVoidPtr(v->ptr); +} + +SWIGRUNTIME PyObject * +SwigPyObject_format(const char* fmt, SwigPyObject *v) +{ + PyObject *res = NULL; + PyObject *args = PyTuple_New(1); + if (args) { + if (PyTuple_SetItem(args, 0, SwigPyObject_long(v)) == 0) { + PyObject *ofmt = SWIG_Python_str_FromChar(fmt); + if (ofmt) { +#if PY_VERSION_HEX >= 0x03000000 + res = PyUnicode_Format(ofmt,args); +#else + res = PyString_Format(ofmt,args); +#endif + Py_DECREF(ofmt); + } + Py_DECREF(args); + } + } + return res; +} + +SWIGRUNTIME PyObject * +SwigPyObject_oct(SwigPyObject *v) +{ + return SwigPyObject_format("%o",v); +} + +SWIGRUNTIME PyObject * +SwigPyObject_hex(SwigPyObject *v) +{ + return SwigPyObject_format("%x",v); +} + +SWIGRUNTIME PyObject * +#ifdef METH_NOARGS +SwigPyObject_repr(SwigPyObject *v) +#else +SwigPyObject_repr(SwigPyObject *v, PyObject *args) +#endif +{ + const char *name = SWIG_TypePrettyName(v->ty); + PyObject *repr = SWIG_Python_str_FromFormat("", (name ? name : "unknown"), (void *)v); + if (v->next) { +# ifdef METH_NOARGS + PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next); +# else + PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next, args); +# endif +# if PY_VERSION_HEX >= 0x03000000 + PyObject *joined = PyUnicode_Concat(repr, nrep); + Py_DecRef(repr); + Py_DecRef(nrep); + repr = joined; +# else + PyString_ConcatAndDel(&repr,nrep); +# endif + } + return repr; +} + +/* We need a version taking two PyObject* parameters so it's a valid + * PyCFunction to use in swigobject_methods[]. */ +SWIGRUNTIME PyObject * +SwigPyObject_repr2(PyObject *v, PyObject *SWIGUNUSEDPARM(args)) +{ + return SwigPyObject_repr((SwigPyObject*)v); +} + +SWIGRUNTIME int +SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w) +{ + void *i = v->ptr; + void *j = w->ptr; + return (i < j) ? -1 : ((i > j) ? 1 : 0); +} + +/* Added for Python 3.x, would it also be useful for Python 2.x? */ +SWIGRUNTIME PyObject* +SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op) +{ + PyObject* res; + if( op != Py_EQ && op != Py_NE ) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0); + return res; +} + + +SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void); + +#ifdef SWIGPYTHON_BUILTIN +static swig_type_info *SwigPyObject_stype = 0; +SWIGRUNTIME PyTypeObject* +SwigPyObject_type(void) { + SwigPyClientData *cd; + assert(SwigPyObject_stype); + cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; + assert(cd); + assert(cd->pytype); + return cd->pytype; +} +#else +SWIGRUNTIME PyTypeObject* +SwigPyObject_type(void) { + static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyObject_TypeOnce(); + return type; +} +#endif + +SWIGRUNTIMEINLINE int +SwigPyObject_Check(PyObject *op) { +#ifdef SWIGPYTHON_BUILTIN + PyTypeObject *target_tp = SwigPyObject_type(); + if (PyType_IsSubtype(op->ob_type, target_tp)) + return 1; + return (strcmp(op->ob_type->tp_name, "SwigPyObject") == 0); +#else + return (Py_TYPE(op) == SwigPyObject_type()) + || (strcmp(Py_TYPE(op)->tp_name,"SwigPyObject") == 0); +#endif +} + +SWIGRUNTIME PyObject * +SwigPyObject_New(void *ptr, swig_type_info *ty, int own); + +SWIGRUNTIME void +SwigPyObject_dealloc(PyObject *v) +{ + SwigPyObject *sobj = (SwigPyObject *) v; + PyObject *next = sobj->next; + if (sobj->own == SWIG_POINTER_OWN) { + swig_type_info *ty = sobj->ty; + SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; + PyObject *destroy = data ? data->destroy : 0; + if (destroy) { + /* destroy is always a VARARGS method */ + PyObject *res; + + /* PyObject_CallFunction() has the potential to silently drop + the active active exception. In cases of unnamed temporary + variable or where we just finished iterating over a generator + StopIteration will be active right now, and this needs to + remain true upon return from SwigPyObject_dealloc. So save + and restore. */ + + PyObject *val = NULL, *type = NULL, *tb = NULL; + PyErr_Fetch(&val, &type, &tb); + + if (data->delargs) { + /* we need to create a temporary object to carry the destroy operation */ + PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0); + res = SWIG_Python_CallFunctor(destroy, tmp); + Py_DECREF(tmp); + } else { + PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); + PyObject *mself = PyCFunction_GET_SELF(destroy); + res = ((*meth)(mself, v)); + } + if (!res) + PyErr_WriteUnraisable(destroy); + + PyErr_Restore(val, type, tb); + + Py_XDECREF(res); + } +#if !defined(SWIG_PYTHON_SILENT_MEMLEAK) + else { + const char *name = SWIG_TypePrettyName(ty); + printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown")); + } +#endif + } + Py_XDECREF(next); + PyObject_DEL(v); +} + +SWIGRUNTIME PyObject* +SwigPyObject_append(PyObject* v, PyObject* next) +{ + SwigPyObject *sobj = (SwigPyObject *) v; +#ifndef METH_O + PyObject *tmp = 0; + if (!PyArg_ParseTuple(next,(char *)"O:append", &tmp)) return NULL; + next = tmp; +#endif + if (!SwigPyObject_Check(next)) { + PyErr_SetString(PyExc_TypeError, "Attempt to append a non SwigPyObject"); + return NULL; + } + sobj->next = next; + Py_INCREF(next); + return SWIG_Py_Void(); +} + +SWIGRUNTIME PyObject* +SwigPyObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +{ + SwigPyObject *sobj = (SwigPyObject *) v; + if (sobj->next) { + Py_INCREF(sobj->next); + return sobj->next; + } else { + return SWIG_Py_Void(); + } +} + +SWIGINTERN PyObject* +#ifdef METH_NOARGS +SwigPyObject_disown(PyObject *v) +#else +SwigPyObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +#endif +{ + SwigPyObject *sobj = (SwigPyObject *)v; + sobj->own = 0; + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject* +#ifdef METH_NOARGS +SwigPyObject_acquire(PyObject *v) +#else +SwigPyObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +#endif +{ + SwigPyObject *sobj = (SwigPyObject *)v; + sobj->own = SWIG_POINTER_OWN; + return SWIG_Py_Void(); +} + +#ifdef METH_NOARGS +static PyObject* +SwigPyObject_disown2(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +{ + return SwigPyObject_disown(v); +} + +static PyObject* +SwigPyObject_acquire2(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +{ + return SwigPyObject_acquire(v); +} +#endif + +SWIGINTERN PyObject* +SwigPyObject_own(PyObject *v, PyObject *args) +{ + PyObject *val = 0; +#if (PY_VERSION_HEX < 0x02020000) + if (!PyArg_ParseTuple(args,(char *)"|O:own",&val)) +#elif (PY_VERSION_HEX < 0x02050000) + if (!PyArg_UnpackTuple(args, (char *)"own", 0, 1, &val)) +#else + if (!PyArg_UnpackTuple(args, "own", 0, 1, &val)) +#endif + { + return NULL; + } + else + { + SwigPyObject *sobj = (SwigPyObject *)v; + PyObject *obj = PyBool_FromLong(sobj->own); + if (val) { +#ifdef METH_NOARGS + if (PyObject_IsTrue(val)) { + SwigPyObject_acquire(v); + } else { + SwigPyObject_disown(v); + } +#else + if (PyObject_IsTrue(val)) { + SwigPyObject_acquire(v,args); + } else { + SwigPyObject_disown(v,args); + } +#endif + } + return obj; + } +} + +#ifdef METH_O +static PyMethodDef +swigobject_methods[] = { + {(char *)"disown", (PyCFunction)SwigPyObject_disown2, METH_NOARGS, (char *)"releases ownership of the pointer"}, + {(char *)"acquire", (PyCFunction)SwigPyObject_acquire2,METH_NOARGS, (char *)"acquires ownership of the pointer"}, + {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, + {(char *)"append", (PyCFunction)SwigPyObject_append, METH_O, (char *)"appends another 'this' object"}, + {(char *)"next", (PyCFunction)SwigPyObject_next, METH_NOARGS, (char *)"returns the next 'this' object"}, + {(char *)"__repr__",(PyCFunction)SwigPyObject_repr2, METH_NOARGS, (char *)"returns object representation"}, + {0, 0, 0, 0} +}; +#else +static PyMethodDef +swigobject_methods[] = { + {(char *)"disown", (PyCFunction)SwigPyObject_disown, METH_VARARGS, (char *)"releases ownership of the pointer"}, + {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_VARARGS, (char *)"acquires ownership of the pointer"}, + {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, + {(char *)"append", (PyCFunction)SwigPyObject_append, METH_VARARGS, (char *)"appends another 'this' object"}, + {(char *)"next", (PyCFunction)SwigPyObject_next, METH_VARARGS, (char *)"returns the next 'this' object"}, + {(char *)"__repr__",(PyCFunction)SwigPyObject_repr, METH_VARARGS, (char *)"returns object representation"}, + {0, 0, 0, 0} +}; +#endif + +#if PY_VERSION_HEX < 0x02020000 +SWIGINTERN PyObject * +SwigPyObject_getattr(SwigPyObject *sobj,char *name) +{ + return Py_FindMethod(swigobject_methods, (PyObject *)sobj, name); +} +#endif + +SWIGRUNTIME PyTypeObject* +SwigPyObject_TypeOnce(void) { + static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer"; + + static PyNumberMethods SwigPyObject_as_number = { + (binaryfunc)0, /*nb_add*/ + (binaryfunc)0, /*nb_subtract*/ + (binaryfunc)0, /*nb_multiply*/ + /* nb_divide removed in Python 3 */ +#if PY_VERSION_HEX < 0x03000000 + (binaryfunc)0, /*nb_divide*/ +#endif + (binaryfunc)0, /*nb_remainder*/ + (binaryfunc)0, /*nb_divmod*/ + (ternaryfunc)0,/*nb_power*/ + (unaryfunc)0, /*nb_negative*/ + (unaryfunc)0, /*nb_positive*/ + (unaryfunc)0, /*nb_absolute*/ + (inquiry)0, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ +#if PY_VERSION_HEX < 0x03000000 + 0, /*nb_coerce*/ +#endif + (unaryfunc)SwigPyObject_long, /*nb_int*/ +#if PY_VERSION_HEX < 0x03000000 + (unaryfunc)SwigPyObject_long, /*nb_long*/ +#else + 0, /*nb_reserved*/ +#endif + (unaryfunc)0, /*nb_float*/ +#if PY_VERSION_HEX < 0x03000000 + (unaryfunc)SwigPyObject_oct, /*nb_oct*/ + (unaryfunc)SwigPyObject_hex, /*nb_hex*/ +#endif +#if PY_VERSION_HEX >= 0x03050000 /* 3.5 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_matrix_multiply */ +#elif PY_VERSION_HEX >= 0x03000000 /* 3.0 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index, nb_inplace_divide removed */ +#elif PY_VERSION_HEX >= 0x02050000 /* 2.5.0 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */ +#elif PY_VERSION_HEX >= 0x02020000 /* 2.2.0 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_true_divide */ +#elif PY_VERSION_HEX >= 0x02000000 /* 2.0.0 */ + 0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_or */ +#endif + }; + + static PyTypeObject swigpyobject_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp = { +#if PY_VERSION_HEX >= 0x03000000 + PyVarObject_HEAD_INIT(NULL, 0) +#else + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ +#endif + (char *)"SwigPyObject", /* tp_name */ + sizeof(SwigPyObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)SwigPyObject_dealloc, /* tp_dealloc */ + 0, /* tp_print */ +#if PY_VERSION_HEX < 0x02020000 + (getattrfunc)SwigPyObject_getattr, /* tp_getattr */ +#else + (getattrfunc)0, /* tp_getattr */ +#endif + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03000000 + 0, /* tp_reserved in 3.0.1, tp_compare in 3.0.0 but not used */ +#else + (cmpfunc)SwigPyObject_compare, /* tp_compare */ +#endif + (reprfunc)SwigPyObject_repr, /* tp_repr */ + &SwigPyObject_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + swigobject_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0, /* tp_iter */ + 0, /* tp_iternext */ + swigobject_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#if PY_VERSION_HEX >= 0x02060000 + 0, /* tp_version_tag */ +#endif +#if PY_VERSION_HEX >= 0x03040000 + 0, /* tp_finalize */ +#endif +#ifdef COUNT_ALLOCS + 0, /* tp_allocs */ + 0, /* tp_frees */ + 0, /* tp_maxalloc */ +#if PY_VERSION_HEX >= 0x02050000 + 0, /* tp_prev */ +#endif + 0 /* tp_next */ +#endif + }; + swigpyobject_type = tmp; + type_init = 1; +#if PY_VERSION_HEX < 0x02020000 + swigpyobject_type.ob_type = &PyType_Type; +#else + if (PyType_Ready(&swigpyobject_type) < 0) + return NULL; +#endif + } + return &swigpyobject_type; +} + +SWIGRUNTIME PyObject * +SwigPyObject_New(void *ptr, swig_type_info *ty, int own) +{ + SwigPyObject *sobj = PyObject_NEW(SwigPyObject, SwigPyObject_type()); + if (sobj) { + sobj->ptr = ptr; + sobj->ty = ty; + sobj->own = own; + sobj->next = 0; + } + return (PyObject *)sobj; +} + +/* ----------------------------------------------------------------------------- + * Implements a simple Swig Packed type, and use it instead of string + * ----------------------------------------------------------------------------- */ + +typedef struct { + PyObject_HEAD + void *pack; + swig_type_info *ty; + size_t size; +} SwigPyPacked; + +SWIGRUNTIME int +SwigPyPacked_print(SwigPyPacked *v, FILE *fp, int SWIGUNUSEDPARM(flags)) +{ + char result[SWIG_BUFFER_SIZE]; + fputs("pack, v->size, 0, sizeof(result))) { + fputs("at ", fp); + fputs(result, fp); + } + fputs(v->ty->name,fp); + fputs(">", fp); + return 0; +} + +SWIGRUNTIME PyObject * +SwigPyPacked_repr(SwigPyPacked *v) +{ + char result[SWIG_BUFFER_SIZE]; + if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) { + return SWIG_Python_str_FromFormat("", result, v->ty->name); + } else { + return SWIG_Python_str_FromFormat("", v->ty->name); + } +} + +SWIGRUNTIME PyObject * +SwigPyPacked_str(SwigPyPacked *v) +{ + char result[SWIG_BUFFER_SIZE]; + if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){ + return SWIG_Python_str_FromFormat("%s%s", result, v->ty->name); + } else { + return SWIG_Python_str_FromChar(v->ty->name); + } +} + +SWIGRUNTIME int +SwigPyPacked_compare(SwigPyPacked *v, SwigPyPacked *w) +{ + size_t i = v->size; + size_t j = w->size; + int s = (i < j) ? -1 : ((i > j) ? 1 : 0); + return s ? s : strncmp((char *)v->pack, (char *)w->pack, 2*v->size); +} + +SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void); + +SWIGRUNTIME PyTypeObject* +SwigPyPacked_type(void) { + static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyPacked_TypeOnce(); + return type; +} + +SWIGRUNTIMEINLINE int +SwigPyPacked_Check(PyObject *op) { + return ((op)->ob_type == SwigPyPacked_TypeOnce()) + || (strcmp((op)->ob_type->tp_name,"SwigPyPacked") == 0); +} + +SWIGRUNTIME void +SwigPyPacked_dealloc(PyObject *v) +{ + if (SwigPyPacked_Check(v)) { + SwigPyPacked *sobj = (SwigPyPacked *) v; + free(sobj->pack); + } + PyObject_DEL(v); +} + +SWIGRUNTIME PyTypeObject* +SwigPyPacked_TypeOnce(void) { + static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer"; + static PyTypeObject swigpypacked_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp = { +#if PY_VERSION_HEX>=0x03000000 + PyVarObject_HEAD_INIT(NULL, 0) +#else + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ +#endif + (char *)"SwigPyPacked", /* tp_name */ + sizeof(SwigPyPacked), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)SwigPyPacked_dealloc, /* tp_dealloc */ + (printfunc)SwigPyPacked_print, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX>=0x03000000 + 0, /* tp_reserved in 3.0.1 */ +#else + (cmpfunc)SwigPyPacked_compare, /* tp_compare */ +#endif + (reprfunc)SwigPyPacked_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)SwigPyPacked_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + swigpacked_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#if PY_VERSION_HEX >= 0x02060000 + 0, /* tp_version_tag */ +#endif +#if PY_VERSION_HEX >= 0x03040000 + 0, /* tp_finalize */ +#endif +#ifdef COUNT_ALLOCS + 0, /* tp_allocs */ + 0, /* tp_frees */ + 0, /* tp_maxalloc */ +#if PY_VERSION_HEX >= 0x02050000 + 0, /* tp_prev */ +#endif + 0 /* tp_next */ +#endif + }; + swigpypacked_type = tmp; + type_init = 1; +#if PY_VERSION_HEX < 0x02020000 + swigpypacked_type.ob_type = &PyType_Type; +#else + if (PyType_Ready(&swigpypacked_type) < 0) + return NULL; +#endif + } + return &swigpypacked_type; +} + +SWIGRUNTIME PyObject * +SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty) +{ + SwigPyPacked *sobj = PyObject_NEW(SwigPyPacked, SwigPyPacked_type()); + if (sobj) { + void *pack = malloc(size); + if (pack) { + memcpy(pack, ptr, size); + sobj->pack = pack; + sobj->ty = ty; + sobj->size = size; + } else { + PyObject_DEL((PyObject *) sobj); + sobj = 0; + } + } + return (PyObject *) sobj; +} + +SWIGRUNTIME swig_type_info * +SwigPyPacked_UnpackData(PyObject *obj, void *ptr, size_t size) +{ + if (SwigPyPacked_Check(obj)) { + SwigPyPacked *sobj = (SwigPyPacked *)obj; + if (sobj->size != size) return 0; + memcpy(ptr, sobj->pack, size); + return sobj->ty; + } else { + return 0; + } +} + +/* ----------------------------------------------------------------------------- + * pointers/data manipulation + * ----------------------------------------------------------------------------- */ + +SWIGRUNTIMEINLINE PyObject * +_SWIG_This(void) +{ + return SWIG_Python_str_FromChar("this"); +} + +static PyObject *swig_this = NULL; + +SWIGRUNTIME PyObject * +SWIG_This(void) +{ + if (swig_this == NULL) + swig_this = _SWIG_This(); + return swig_this; +} + +/* #define SWIG_PYTHON_SLOW_GETSET_THIS */ + +/* TODO: I don't know how to implement the fast getset in Python 3 right now */ +#if PY_VERSION_HEX>=0x03000000 +#define SWIG_PYTHON_SLOW_GETSET_THIS +#endif + +SWIGRUNTIME SwigPyObject * +SWIG_Python_GetSwigThis(PyObject *pyobj) +{ + PyObject *obj; + + if (SwigPyObject_Check(pyobj)) + return (SwigPyObject *) pyobj; + +#ifdef SWIGPYTHON_BUILTIN + (void)obj; +# ifdef PyWeakref_CheckProxy + if (PyWeakref_CheckProxy(pyobj)) { + pyobj = PyWeakref_GET_OBJECT(pyobj); + if (pyobj && SwigPyObject_Check(pyobj)) + return (SwigPyObject*) pyobj; + } +# endif + return NULL; +#else + + obj = 0; + +#if (!defined(SWIG_PYTHON_SLOW_GETSET_THIS) && (PY_VERSION_HEX >= 0x02030000)) + if (PyInstance_Check(pyobj)) { + obj = _PyInstance_Lookup(pyobj, SWIG_This()); + } else { + PyObject **dictptr = _PyObject_GetDictPtr(pyobj); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0; + } else { +#ifdef PyWeakref_CheckProxy + if (PyWeakref_CheckProxy(pyobj)) { + PyObject *wobj = PyWeakref_GET_OBJECT(pyobj); + return wobj ? SWIG_Python_GetSwigThis(wobj) : 0; + } +#endif + obj = PyObject_GetAttr(pyobj,SWIG_This()); + if (obj) { + Py_DECREF(obj); + } else { + if (PyErr_Occurred()) PyErr_Clear(); + return 0; + } + } + } +#else + obj = PyObject_GetAttr(pyobj,SWIG_This()); + if (obj) { + Py_DECREF(obj); + } else { + if (PyErr_Occurred()) PyErr_Clear(); + return 0; + } +#endif + if (obj && !SwigPyObject_Check(obj)) { + /* a PyObject is called 'this', try to get the 'real this' + SwigPyObject from it */ + return SWIG_Python_GetSwigThis(obj); + } + return (SwigPyObject *)obj; +#endif +} + +/* Acquire a pointer value */ + +SWIGRUNTIME int +SWIG_Python_AcquirePtr(PyObject *obj, int own) { + if (own == SWIG_POINTER_OWN) { + SwigPyObject *sobj = SWIG_Python_GetSwigThis(obj); + if (sobj) { + int oldown = sobj->own; + sobj->own = own; + return oldown; + } + } + return 0; +} + +/* Convert a pointer value */ + +SWIGRUNTIME int +SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) { + int res; + SwigPyObject *sobj; + int implicit_conv = (flags & SWIG_POINTER_IMPLICIT_CONV) != 0; + + if (!obj) + return SWIG_ERROR; + if (obj == Py_None && !implicit_conv) { + if (ptr) + *ptr = 0; + return SWIG_OK; + } + + res = SWIG_ERROR; + + sobj = SWIG_Python_GetSwigThis(obj); + if (own) + *own = 0; + while (sobj) { + void *vptr = sobj->ptr; + if (ty) { + swig_type_info *to = sobj->ty; + if (to == ty) { + /* no type cast needed */ + if (ptr) *ptr = vptr; + break; + } else { + swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); + if (!tc) { + sobj = (SwigPyObject *)sobj->next; + } else { + if (ptr) { + int newmemory = 0; + *ptr = SWIG_TypeCast(tc,vptr,&newmemory); + if (newmemory == SWIG_CAST_NEW_MEMORY) { + assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */ + if (own) + *own = *own | SWIG_CAST_NEW_MEMORY; + } + } + break; + } + } + } else { + if (ptr) *ptr = vptr; + break; + } + } + if (sobj) { + if (own) + *own = *own | sobj->own; + if (flags & SWIG_POINTER_DISOWN) { + sobj->own = 0; + } + res = SWIG_OK; + } else { + if (implicit_conv) { + SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; + if (data && !data->implicitconv) { + PyObject *klass = data->klass; + if (klass) { + PyObject *impconv; + data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/ + impconv = SWIG_Python_CallFunctor(klass, obj); + data->implicitconv = 0; + if (PyErr_Occurred()) { + PyErr_Clear(); + impconv = 0; + } + if (impconv) { + SwigPyObject *iobj = SWIG_Python_GetSwigThis(impconv); + if (iobj) { + void *vptr; + res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0); + if (SWIG_IsOK(res)) { + if (ptr) { + *ptr = vptr; + /* transfer the ownership to 'ptr' */ + iobj->own = 0; + res = SWIG_AddCast(res); + res = SWIG_AddNewMask(res); + } else { + res = SWIG_AddCast(res); + } + } + } + Py_DECREF(impconv); + } + } + } + } + if (!SWIG_IsOK(res) && obj == Py_None) { + if (ptr) + *ptr = 0; + if (PyErr_Occurred()) + PyErr_Clear(); + res = SWIG_OK; + } + } + return res; +} + +/* Convert a function ptr value */ + +SWIGRUNTIME int +SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) { + if (!PyCFunction_Check(obj)) { + return SWIG_ConvertPtr(obj, ptr, ty, 0); + } else { + void *vptr = 0; + + /* here we get the method pointer for callbacks */ + const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc); + const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0; + if (desc) + desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0; + if (!desc) + return SWIG_ERROR; + if (ty) { + swig_cast_info *tc = SWIG_TypeCheck(desc,ty); + if (tc) { + int newmemory = 0; + *ptr = SWIG_TypeCast(tc,vptr,&newmemory); + assert(!newmemory); /* newmemory handling not yet implemented */ + } else { + return SWIG_ERROR; + } + } else { + *ptr = vptr; + } + return SWIG_OK; + } +} + +/* Convert a packed value value */ + +SWIGRUNTIME int +SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) { + swig_type_info *to = SwigPyPacked_UnpackData(obj, ptr, sz); + if (!to) return SWIG_ERROR; + if (ty) { + if (to != ty) { + /* check type cast? */ + swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); + if (!tc) return SWIG_ERROR; + } + } + return SWIG_OK; +} + +/* ----------------------------------------------------------------------------- + * Create a new pointer object + * ----------------------------------------------------------------------------- */ + +/* + Create a new instance object, without calling __init__, and set the + 'this' attribute. +*/ + +SWIGRUNTIME PyObject* +SWIG_Python_NewShadowInstance(SwigPyClientData *data, PyObject *swig_this) +{ +#if (PY_VERSION_HEX >= 0x02020000) + PyObject *inst = 0; + PyObject *newraw = data->newraw; + if (newraw) { + inst = PyObject_Call(newraw, data->newargs, NULL); + if (inst) { +#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) + PyObject **dictptr = _PyObject_GetDictPtr(inst); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict == NULL) { + dict = PyDict_New(); + *dictptr = dict; + PyDict_SetItem(dict, SWIG_This(), swig_this); + } + } +#else + PyObject *key = SWIG_This(); + PyObject_SetAttr(inst, key, swig_this); +#endif + } + } else { +#if PY_VERSION_HEX >= 0x03000000 + inst = ((PyTypeObject*) data->newargs)->tp_new((PyTypeObject*) data->newargs, Py_None, Py_None); + if (inst) { + PyObject_SetAttr(inst, SWIG_This(), swig_this); + Py_TYPE(inst)->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; + } +#else + PyObject *dict = PyDict_New(); + if (dict) { + PyDict_SetItem(dict, SWIG_This(), swig_this); + inst = PyInstance_NewRaw(data->newargs, dict); + Py_DECREF(dict); + } +#endif + } + return inst; +#else +#if (PY_VERSION_HEX >= 0x02010000) + PyObject *inst = 0; + PyObject *dict = PyDict_New(); + if (dict) { + PyDict_SetItem(dict, SWIG_This(), swig_this); + inst = PyInstance_NewRaw(data->newargs, dict); + Py_DECREF(dict); + } + return (PyObject *) inst; +#else + PyInstanceObject *inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type); + if (inst == NULL) { + return NULL; + } + inst->in_class = (PyClassObject *)data->newargs; + Py_INCREF(inst->in_class); + inst->in_dict = PyDict_New(); + if (inst->in_dict == NULL) { + Py_DECREF(inst); + return NULL; + } +#ifdef Py_TPFLAGS_HAVE_WEAKREFS + inst->in_weakreflist = NULL; +#endif +#ifdef Py_TPFLAGS_GC + PyObject_GC_Init(inst); +#endif + PyDict_SetItem(inst->in_dict, SWIG_This(), swig_this); + return (PyObject *) inst; +#endif +#endif +} + +SWIGRUNTIME void +SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this) +{ + PyObject *dict; +#if (PY_VERSION_HEX >= 0x02020000) && !defined(SWIG_PYTHON_SLOW_GETSET_THIS) + PyObject **dictptr = _PyObject_GetDictPtr(inst); + if (dictptr != NULL) { + dict = *dictptr; + if (dict == NULL) { + dict = PyDict_New(); + *dictptr = dict; + } + PyDict_SetItem(dict, SWIG_This(), swig_this); + return; + } +#endif + dict = PyObject_GetAttrString(inst, (char*)"__dict__"); + PyDict_SetItem(dict, SWIG_This(), swig_this); + Py_DECREF(dict); +} + + +SWIGINTERN PyObject * +SWIG_Python_InitShadowInstance(PyObject *args) { + PyObject *obj[2]; + if (!SWIG_Python_UnpackTuple(args, "swiginit", 2, 2, obj)) { + return NULL; + } else { + SwigPyObject *sthis = SWIG_Python_GetSwigThis(obj[0]); + if (sthis) { + SwigPyObject_append((PyObject*) sthis, obj[1]); + } else { + SWIG_Python_SetSwigThis(obj[0], obj[1]); + } + return SWIG_Py_Void(); + } +} + +/* Create a new pointer object */ + +SWIGRUNTIME PyObject * +SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int flags) { + SwigPyClientData *clientdata; + PyObject * robj; + int own; + + if (!ptr) + return SWIG_Py_Void(); + + clientdata = type ? (SwigPyClientData *)(type->clientdata) : 0; + own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0; + if (clientdata && clientdata->pytype) { + SwigPyObject *newobj; + if (flags & SWIG_BUILTIN_TP_INIT) { + newobj = (SwigPyObject*) self; + if (newobj->ptr) { + PyObject *next_self = clientdata->pytype->tp_alloc(clientdata->pytype, 0); + while (newobj->next) + newobj = (SwigPyObject *) newobj->next; + newobj->next = next_self; + newobj = (SwigPyObject *)next_self; +#ifdef SWIGPYTHON_BUILTIN + newobj->dict = 0; +#endif + } + } else { + newobj = PyObject_New(SwigPyObject, clientdata->pytype); +#ifdef SWIGPYTHON_BUILTIN + newobj->dict = 0; +#endif + } + if (newobj) { + newobj->ptr = ptr; + newobj->ty = type; + newobj->own = own; + newobj->next = 0; + return (PyObject*) newobj; + } + return SWIG_Py_Void(); + } + + assert(!(flags & SWIG_BUILTIN_TP_INIT)); + + robj = SwigPyObject_New(ptr, type, own); + if (robj && clientdata && !(flags & SWIG_POINTER_NOSHADOW)) { + PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj); + Py_DECREF(robj); + robj = inst; + } + return robj; +} + +/* Create a new packed object */ + +SWIGRUNTIMEINLINE PyObject * +SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) { + return ptr ? SwigPyPacked_New((void *) ptr, sz, type) : SWIG_Py_Void(); +} + +/* -----------------------------------------------------------------------------* + * Get type list + * -----------------------------------------------------------------------------*/ + +#ifdef SWIG_LINK_RUNTIME +void *SWIG_ReturnGlobalTypeList(void *); +#endif + +SWIGRUNTIME swig_module_info * +SWIG_Python_GetModule(void *SWIGUNUSEDPARM(clientdata)) { + static void *type_pointer = (void *)0; + /* first check if module already created */ + if (!type_pointer) { +#ifdef SWIG_LINK_RUNTIME + type_pointer = SWIG_ReturnGlobalTypeList((void *)0); +#else +# ifdef SWIGPY_USE_CAPSULE + type_pointer = PyCapsule_Import(SWIGPY_CAPSULE_NAME, 0); +# else + type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, + (char*)"type_pointer" SWIG_TYPE_TABLE_NAME); +# endif + if (PyErr_Occurred()) { + PyErr_Clear(); + type_pointer = (void *)0; + } +#endif + } + return (swig_module_info *) type_pointer; +} + +#if PY_MAJOR_VERSION < 2 +/* PyModule_AddObject function was introduced in Python 2.0. The following function + is copied out of Python/modsupport.c in python version 2.3.4 */ +SWIGINTERN int +PyModule_AddObject(PyObject *m, char *name, PyObject *o) +{ + PyObject *dict; + if (!PyModule_Check(m)) { + PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs module as first arg"); + return SWIG_ERROR; + } + if (!o) { + PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs non-NULL value"); + return SWIG_ERROR; + } + + dict = PyModule_GetDict(m); + if (dict == NULL) { + /* Internal error -- modules must have a dict! */ + PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__", + PyModule_GetName(m)); + return SWIG_ERROR; + } + if (PyDict_SetItemString(dict, name, o)) + return SWIG_ERROR; + Py_DECREF(o); + return SWIG_OK; +} +#endif + +SWIGRUNTIME void +#ifdef SWIGPY_USE_CAPSULE +SWIG_Python_DestroyModule(PyObject *obj) +#else +SWIG_Python_DestroyModule(void *vptr) +#endif +{ +#ifdef SWIGPY_USE_CAPSULE + swig_module_info *swig_module = (swig_module_info *) PyCapsule_GetPointer(obj, SWIGPY_CAPSULE_NAME); +#else + swig_module_info *swig_module = (swig_module_info *) vptr; +#endif + swig_type_info **types = swig_module->types; + size_t i; + for (i =0; i < swig_module->size; ++i) { + swig_type_info *ty = types[i]; + if (ty->owndata) { + SwigPyClientData *data = (SwigPyClientData *) ty->clientdata; + if (data) SwigPyClientData_Del(data); + } + } + Py_DECREF(SWIG_This()); + swig_this = NULL; +} + +SWIGRUNTIME void +SWIG_Python_SetModule(swig_module_info *swig_module) { +#if PY_VERSION_HEX >= 0x03000000 + /* Add a placeholder module object into sys.modules */ + PyObject *module = PyImport_AddModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION); +#else + static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} }; /* Sentinel */ + PyObject *module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table); +#endif +#ifdef SWIGPY_USE_CAPSULE + PyObject *pointer = PyCapsule_New((void *) swig_module, SWIGPY_CAPSULE_NAME, SWIG_Python_DestroyModule); + if (pointer && module) { + PyModule_AddObject(module, (char*)"type_pointer_capsule" SWIG_TYPE_TABLE_NAME, pointer); + } else { + Py_XDECREF(pointer); + } +#else + PyObject *pointer = PyCObject_FromVoidPtr((void *) swig_module, SWIG_Python_DestroyModule); + if (pointer && module) { + PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer); + } else { + Py_XDECREF(pointer); + } +#endif +} + +/* The python cached type query */ +SWIGRUNTIME PyObject * +SWIG_Python_TypeCache(void) { + static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New(); + return cache; +} + +SWIGRUNTIME swig_type_info * +SWIG_Python_TypeQuery(const char *type) +{ + PyObject *cache = SWIG_Python_TypeCache(); + PyObject *key = SWIG_Python_str_FromChar(type); + PyObject *obj = PyDict_GetItem(cache, key); + swig_type_info *descriptor; + if (obj) { +#ifdef SWIGPY_USE_CAPSULE + descriptor = (swig_type_info *) PyCapsule_GetPointer(obj, NULL); +#else + descriptor = (swig_type_info *) PyCObject_AsVoidPtr(obj); +#endif + } else { + swig_module_info *swig_module = SWIG_GetModule(0); + descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type); + if (descriptor) { +#ifdef SWIGPY_USE_CAPSULE + obj = PyCapsule_New((void*) descriptor, NULL, NULL); +#else + obj = PyCObject_FromVoidPtr(descriptor, NULL); +#endif + PyDict_SetItem(cache, key, obj); + Py_DECREF(obj); + } + } + Py_DECREF(key); + return descriptor; +} + +/* + For backward compatibility only +*/ +#define SWIG_POINTER_EXCEPTION 0 +#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg) +#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags) + +SWIGRUNTIME int +SWIG_Python_AddErrMesg(const char* mesg, int infront) +{ + if (PyErr_Occurred()) { + PyObject *type = 0; + PyObject *value = 0; + PyObject *traceback = 0; + PyErr_Fetch(&type, &value, &traceback); + if (value) { + char *tmp; + PyObject *old_str = PyObject_Str(value); + Py_XINCREF(type); + PyErr_Clear(); + if (infront) { + PyErr_Format(type, "%s %s", mesg, tmp = SWIG_Python_str_AsChar(old_str)); + } else { + PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg); + } + SWIG_Python_str_DelForPy3(tmp); + Py_DECREF(old_str); + } + return 1; + } else { + return 0; + } +} + +SWIGRUNTIME int +SWIG_Python_ArgFail(int argnum) +{ + if (PyErr_Occurred()) { + /* add information about failing argument */ + char mesg[256]; + PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum); + return SWIG_Python_AddErrMesg(mesg, 1); + } else { + return 0; + } +} + +SWIGRUNTIMEINLINE const char * +SwigPyObject_GetDesc(PyObject *self) +{ + SwigPyObject *v = (SwigPyObject *)self; + swig_type_info *ty = v ? v->ty : 0; + return ty ? ty->str : ""; +} + +SWIGRUNTIME void +SWIG_Python_TypeError(const char *type, PyObject *obj) +{ + if (type) { +#if defined(SWIG_COBJECT_TYPES) + if (obj && SwigPyObject_Check(obj)) { + const char *otype = (const char *) SwigPyObject_GetDesc(obj); + if (otype) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received", + type, otype); + return; + } + } else +#endif + { + const char *otype = (obj ? obj->ob_type->tp_name : 0); + if (otype) { + PyObject *str = PyObject_Str(obj); + const char *cstr = str ? SWIG_Python_str_AsChar(str) : 0; + if (cstr) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received", + type, otype, cstr); + SWIG_Python_str_DelForPy3(cstr); + } else { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received", + type, otype); + } + Py_XDECREF(str); + return; + } + } + PyErr_Format(PyExc_TypeError, "a '%s' is expected", type); + } else { + PyErr_Format(PyExc_TypeError, "unexpected type is received"); + } +} + + +/* Convert a pointer value, signal an exception on a type mismatch */ +SWIGRUNTIME void * +SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) { + void *result; + if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) { + PyErr_Clear(); +#if SWIG_POINTER_EXCEPTION + if (flags) { + SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj); + SWIG_Python_ArgFail(argnum); + } +#endif + } + return result; +} + +#ifdef SWIGPYTHON_BUILTIN +SWIGRUNTIME int +SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) { + PyTypeObject *tp = obj->ob_type; + PyObject *descr; + PyObject *encoded_name; + descrsetfunc f; + int res = -1; + +# ifdef Py_USING_UNICODE + if (PyString_Check(name)) { + name = PyUnicode_Decode(PyString_AsString(name), PyString_Size(name), NULL, NULL); + if (!name) + return -1; + } else if (!PyUnicode_Check(name)) +# else + if (!PyString_Check(name)) +# endif + { + PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", name->ob_type->tp_name); + return -1; + } else { + Py_INCREF(name); + } + + if (!tp->tp_dict) { + if (PyType_Ready(tp) < 0) + goto done; + } + + descr = _PyType_Lookup(tp, name); + f = NULL; + if (descr != NULL) + f = descr->ob_type->tp_descr_set; + if (!f) { + if (PyString_Check(name)) { + encoded_name = name; + Py_INCREF(name); + } else { + encoded_name = PyUnicode_AsUTF8String(name); + } + PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name, PyString_AsString(encoded_name)); + Py_DECREF(encoded_name); + } else { + res = f(descr, obj, value); + } + + done: + Py_DECREF(name); + return res; +} +#endif + + +#ifdef __cplusplus +} +#endif + + + +#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0) + +#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else + + + + #define SWIG_exception(code, msg) do { SWIG_Error(code, msg); SWIG_fail;; } while(0) + + +/* -------- TYPES TABLE (BEGIN) -------- */ + +#define SWIGTYPE_p_char swig_types[0] +#define SWIGTYPE_p_int swig_types[1] +#define SWIGTYPE_p_uint8_t swig_types[2] +static swig_type_info *swig_types[4]; +static swig_module_info swig_module = {swig_types, 3, 0, 0, 0, 0}; +#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name) +#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name) + +/* -------- TYPES TABLE (END) -------- */ + +#if (PY_VERSION_HEX <= 0x02000000) +# if !defined(SWIG_PYTHON_CLASSIC) +# error "This python version requires swig to be run with the '-classic' option" +# endif +#endif + +/*----------------------------------------------- + @(target):= _libwebp.so + ------------------------------------------------*/ +#if PY_VERSION_HEX >= 0x03000000 +# define SWIG_init PyInit__libwebp + +#else +# define SWIG_init init_libwebp + +#endif +#define SWIG_name "_libwebp" + +#define SWIGVERSION 0x030012 +#define SWIG_VERSION SWIGVERSION + + +#define SWIG_as_voidptr(a) (void *)((const void *)(a)) +#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a)) + + +SWIGINTERNINLINE PyObject* + SWIG_From_int (int value) +{ + return PyInt_FromLong((long) value); +} + + +SWIGINTERN swig_type_info* +SWIG_pchar_descriptor(void) +{ + static int init = 0; + static swig_type_info* info = 0; + if (!init) { + info = SWIG_TypeQuery("_p_char"); + init = 1; + } + return info; +} + + +SWIGINTERN int +SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc) +{ +#if PY_VERSION_HEX>=0x03000000 +#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + if (PyBytes_Check(obj)) +#else + if (PyUnicode_Check(obj)) +#endif +#else + if (PyString_Check(obj)) +#endif + { + char *cstr; Py_ssize_t len; +#if PY_VERSION_HEX>=0x03000000 +#if !defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + if (!alloc && cptr) { + /* We can't allow converting without allocation, since the internal + representation of string in Python 3 is UCS-2/UCS-4 but we require + a UTF-8 representation. + TODO(bhy) More detailed explanation */ + return SWIG_RuntimeError; + } + obj = PyUnicode_AsUTF8String(obj); + if(alloc) *alloc = SWIG_NEWOBJ; +#endif + PyBytes_AsStringAndSize(obj, &cstr, &len); +#else + PyString_AsStringAndSize(obj, &cstr, &len); +#endif + if (cptr) { + if (alloc) { + /* + In python the user should not be able to modify the inner + string representation. To warranty that, if you define + SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string + buffer is always returned. + + The default behavior is just to return the pointer value, + so, be careful. + */ +#if defined(SWIG_PYTHON_SAFE_CSTRINGS) + if (*alloc != SWIG_OLDOBJ) +#else + if (*alloc == SWIG_NEWOBJ) +#endif + { + *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1)); + *alloc = SWIG_NEWOBJ; + } else { + *cptr = cstr; + *alloc = SWIG_OLDOBJ; + } + } else { +#if PY_VERSION_HEX>=0x03000000 +#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + *cptr = PyBytes_AsString(obj); +#else + assert(0); /* Should never reach here with Unicode strings in Python 3 */ +#endif +#else + *cptr = SWIG_Python_str_AsChar(obj); +#endif + } + } + if (psize) *psize = len + 1; +#if PY_VERSION_HEX>=0x03000000 && !defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + Py_XDECREF(obj); +#endif + return SWIG_OK; + } else { +#if defined(SWIG_PYTHON_2_UNICODE) +#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) +#error "Cannot use both SWIG_PYTHON_2_UNICODE and SWIG_PYTHON_STRICT_BYTE_CHAR at once" +#endif +#if PY_VERSION_HEX<0x03000000 + if (PyUnicode_Check(obj)) { + char *cstr; Py_ssize_t len; + if (!alloc && cptr) { + return SWIG_RuntimeError; + } + obj = PyUnicode_AsUTF8String(obj); + if (PyString_AsStringAndSize(obj, &cstr, &len) != -1) { + if (cptr) { + if (alloc) *alloc = SWIG_NEWOBJ; + *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1)); + } + if (psize) *psize = len + 1; + + Py_XDECREF(obj); + return SWIG_OK; + } else { + Py_XDECREF(obj); + } + } +#endif +#endif + + swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); + if (pchar_descriptor) { + void* vptr = 0; + if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) { + if (cptr) *cptr = (char *) vptr; + if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0; + if (alloc) *alloc = SWIG_OLDOBJ; + return SWIG_OK; + } + } + } + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_double (PyObject *obj, double *val) +{ + int res = SWIG_TypeError; + if (PyFloat_Check(obj)) { + if (val) *val = PyFloat_AsDouble(obj); + return SWIG_OK; +#if PY_VERSION_HEX < 0x03000000 + } else if (PyInt_Check(obj)) { + if (val) *val = (double) PyInt_AsLong(obj); + return SWIG_OK; +#endif + } else if (PyLong_Check(obj)) { + double v = PyLong_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + double d = PyFloat_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = d; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_AddCast(SWIG_OK)); + } else { + PyErr_Clear(); + } + } + } +#endif + return res; +} + + +#include + + +#include + + +SWIGINTERNINLINE int +SWIG_CanCastAsInteger(double *d, double min, double max) { + double x = *d; + if ((min <= x && x <= max)) { + double fx = floor(x); + double cx = ceil(x); + double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ + if ((errno == EDOM) || (errno == ERANGE)) { + errno = 0; + } else { + double summ, reps, diff; + if (rd < x) { + diff = x - rd; + } else if (rd > x) { + diff = rd - x; + } else { + return 1; + } + summ = rd + x; + reps = diff/summ; + if (reps < 8*DBL_EPSILON) { + *d = rd; + return 1; + } + } + } + return 0; +} + + +SWIGINTERN int +SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val) +{ +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check(obj)) { + long v = PyInt_AsLong(obj); + if (v >= 0) { + if (val) *val = v; + return SWIG_OK; + } else { + return SWIG_OverflowError; + } + } else +#endif + if (PyLong_Check(obj)) { + unsigned long v = PyLong_AsUnsignedLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + return SWIG_OverflowError; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + unsigned long v = PyLong_AsUnsignedLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) { + if (val) *val = (unsigned long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +#include +#if !defined(SWIG_NO_LLONG_MAX) +# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) +# define LLONG_MAX __LONG_LONG_MAX__ +# define LLONG_MIN (-LLONG_MAX - 1LL) +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +# endif +#endif + + +#if defined(LLONG_MAX) && !defined(SWIG_LONG_LONG_AVAILABLE) +# define SWIG_LONG_LONG_AVAILABLE +#endif + + +#ifdef SWIG_LONG_LONG_AVAILABLE +SWIGINTERN int +SWIG_AsVal_unsigned_SS_long_SS_long (PyObject *obj, unsigned long long *val) +{ + int res = SWIG_TypeError; + if (PyLong_Check(obj)) { + unsigned long long v = PyLong_AsUnsignedLongLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + res = SWIG_OverflowError; + } + } else { + unsigned long v; + res = SWIG_AsVal_unsigned_SS_long (obj,&v); + if (SWIG_IsOK(res)) { + if (val) *val = v; + return res; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + const double mant_max = 1LL << DBL_MANT_DIG; + double d; + res = SWIG_AsVal_double (obj,&d); + if (SWIG_IsOK(res) && !SWIG_CanCastAsInteger(&d, 0, mant_max)) + return SWIG_OverflowError; + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, mant_max)) { + if (val) *val = (unsigned long long)(d); + return SWIG_AddCast(res); + } + res = SWIG_TypeError; + } +#endif + return res; +} +#endif + + +SWIGINTERNINLINE int +SWIG_AsVal_size_t (PyObject * obj, size_t *val) +{ + int res = SWIG_TypeError; +#ifdef SWIG_LONG_LONG_AVAILABLE + if (sizeof(size_t) <= sizeof(unsigned long)) { +#endif + unsigned long v; + res = SWIG_AsVal_unsigned_SS_long (obj, val ? &v : 0); + if (SWIG_IsOK(res) && val) *val = (size_t)(v); +#ifdef SWIG_LONG_LONG_AVAILABLE + } else if (sizeof(size_t) <= sizeof(unsigned long long)) { + unsigned long long v; + res = SWIG_AsVal_unsigned_SS_long_SS_long (obj, val ? &v : 0); + if (SWIG_IsOK(res) && val) *val = (size_t)(v); + } +#endif + return res; +} + + +#include "webp/decode.h" +#include "webp/encode.h" + + +static size_t ReturnedBufferSize( + const char* function, int* width, int* height) { + static const struct sizemap { + const char* function; + int size_multiplier; + } size_map[] = { +#ifdef SWIGJAVA + { "Java_com_google_webp_libwebpJNI_WebPDecodeRGB", 3 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeRGBA", 4 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeARGB", 4 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeBGR", 3 }, + { "Java_com_google_webp_libwebpJNI_WebPDecodeBGRA", 4 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA", 1 }, + { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA", 1 }, +#endif +#ifdef SWIGPYTHON + { "WebPDecodeRGB", 3 }, + { "WebPDecodeRGBA", 4 }, + { "WebPDecodeARGB", 4 }, + { "WebPDecodeBGR", 3 }, + { "WebPDecodeBGRA", 4 }, + { "wrap_WebPEncodeRGB", 1 }, + { "wrap_WebPEncodeBGR", 1 }, + { "wrap_WebPEncodeRGBA", 1 }, + { "wrap_WebPEncodeBGRA", 1 }, + { "wrap_WebPEncodeLosslessRGB", 1 }, + { "wrap_WebPEncodeLosslessBGR", 1 }, + { "wrap_WebPEncodeLosslessRGBA", 1 }, + { "wrap_WebPEncodeLosslessBGRA", 1 }, +#endif + { NULL, 0 } + }; + const struct sizemap* p; + size_t size = 0; + + for (p = size_map; p->function; ++p) { + if (!strcmp(function, p->function)) { + size = *width * *height * p->size_multiplier; + break; + } + } + + return size; +} + + +typedef size_t (*WebPEncodeFunction)(const uint8_t* rgb, + int width, int height, int stride, + float quality_factor, uint8_t** output); +typedef size_t (*WebPEncodeLosslessFunction)(const uint8_t* rgb, + int width, int height, int stride, + uint8_t** output); + +static uint8_t* EncodeLossy(const uint8_t* rgb, + int width, int height, int stride, + float quality_factor, + WebPEncodeFunction encfn, + int* output_size, int* unused) { + uint8_t* output = NULL; + const size_t image_size = + encfn(rgb, width, height, stride, quality_factor, &output); + // the values of following two will be interpreted by ReturnedBufferSize() + // as 'width' and 'height' in the size calculation. + *output_size = image_size; + *unused = 1; + return image_size ? output : NULL; +} + +static uint8_t* EncodeLossless(const uint8_t* rgb, + int width, int height, int stride, + WebPEncodeLosslessFunction encfn, + int* output_size, int* unused) { + uint8_t* output = NULL; + const size_t image_size = encfn(rgb, width, height, stride, &output); + // the values of the following two will be interpreted by + // ReturnedBufferSize() as 'width' and 'height' in the size calculation. + *output_size = image_size; + *unused = 1; + return image_size ? output : NULL; +} + + +// Changes the return type of WebPEncode* to more closely match Decode*. +// This also makes it easier to wrap the output buffer in a native type rather +// than dealing with the return pointer. +// The additional parameters are to allow reuse of ReturnedBufferSize(), +// unused2 and output_size will be used in this case. +#define LOSSY_WRAPPER(FUNC) \ + static uint8_t* wrap_##FUNC( \ + const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \ + int width, int height, int stride, float quality_factor) { \ + return EncodeLossy(rgb, width, height, stride, quality_factor, \ + FUNC, output_size, unused2); \ + } \ + +LOSSY_WRAPPER(WebPEncodeRGB) +LOSSY_WRAPPER(WebPEncodeBGR) +LOSSY_WRAPPER(WebPEncodeRGBA) +LOSSY_WRAPPER(WebPEncodeBGRA) + +#undef LOSSY_WRAPPER + +#define LOSSLESS_WRAPPER(FUNC) \ + static uint8_t* wrap_##FUNC( \ + const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \ + int width, int height, int stride) { \ + return EncodeLossless(rgb, width, height, stride, \ + FUNC, output_size, unused2); \ + } \ + +LOSSLESS_WRAPPER(WebPEncodeLosslessRGB) +LOSSLESS_WRAPPER(WebPEncodeLosslessBGR) +LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA) +LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA) + +#undef LOSSLESS_WRAPPER + + + +SWIGINTERN int +SWIG_AsVal_long (PyObject *obj, long* val) +{ +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check(obj)) { + if (val) *val = PyInt_AsLong(obj); + return SWIG_OK; + } else +#endif + if (PyLong_Check(obj)) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + return SWIG_OverflowError; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + long v = PyInt_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) { + if (val) *val = (long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_int (PyObject * obj, int *val) +{ + long v; + int res = SWIG_AsVal_long (obj, &v); + if (SWIG_IsOK(res)) { + if ((v < INT_MIN || v > INT_MAX)) { + return SWIG_OverflowError; + } else { + if (val) *val = (int)(v); + } + } + return res; +} + + +/* Getting isfinite working pre C99 across multiple platforms is non-trivial. Users can provide SWIG_isfinite on older platforms. */ +#ifndef SWIG_isfinite +/* isfinite() is a macro for C99 */ +# if defined(isfinite) +# define SWIG_isfinite(X) (isfinite(X)) +# elif defined __cplusplus && __cplusplus >= 201103L +/* Use a template so that this works whether isfinite() is std::isfinite() or + * in the global namespace. The reality seems to vary between compiler + * versions. + * + * Make sure namespace std exists to avoid compiler warnings. + * + * extern "C++" is required as this fragment can end up inside an extern "C" { } block + */ +namespace std { } +extern "C++" template +inline int SWIG_isfinite_func(T x) { + using namespace std; + return isfinite(x); +} +# define SWIG_isfinite(X) (SWIG_isfinite_func(X)) +# elif defined(_MSC_VER) +# define SWIG_isfinite(X) (_finite(X)) +# elif defined(__sun) && defined(__SVR4) +# include +# define SWIG_isfinite(X) (finite(X)) +# endif +#endif + + +/* Accept infinite as a valid float value unless we are unable to check if a value is finite */ +#ifdef SWIG_isfinite +# define SWIG_Float_Overflow_Check(X) ((X < -FLT_MAX || X > FLT_MAX) && SWIG_isfinite(X)) +#else +# define SWIG_Float_Overflow_Check(X) ((X < -FLT_MAX || X > FLT_MAX)) +#endif + + +SWIGINTERN int +SWIG_AsVal_float (PyObject * obj, float *val) +{ + double v; + int res = SWIG_AsVal_double (obj, &v); + if (SWIG_IsOK(res)) { + if (SWIG_Float_Overflow_Check(v)) { + return SWIG_OverflowError; + } else { + if (val) *val = (float)(v); + } + } + return res; +} + +#ifdef __cplusplus +extern "C" { +#endif +SWIGINTERN PyObject *_wrap_WebPGetDecoderVersion(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + int result; + + if (!PyArg_ParseTuple(args,(char *)":WebPGetDecoderVersion")) SWIG_fail; + result = (int)WebPGetDecoderVersion(); + resultobj = SWIG_From_int((int)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_WebPGetInfo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int res1 ; + char *buf1 = 0 ; + size_t size1 = 0 ; + int alloc1 = 0 ; + int temp3 ; + int res3 = SWIG_TMPOBJ ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + PyObject * obj0 = 0 ; + int result; + + arg3 = &temp3; + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"O:WebPGetInfo",&obj0)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPGetInfo" "', argument " "1"" of type '" "uint8_t const *""'"); + } + arg1 = (uint8_t *)(buf1); + arg2 = (size_t)(size1 - 1); + result = (int)WebPGetInfo((uint8_t const *)arg1,arg2,arg3,arg4); + resultobj = SWIG_From_int((int)(result)); + if (SWIG_IsTmpObj(res3)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3))); + } else { + int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_WebPDecodeRGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int res1 ; + char *buf1 = 0 ; + size_t size1 = 0 ; + int alloc1 = 0 ; + int temp3 ; + int res3 = SWIG_TMPOBJ ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + PyObject * obj0 = 0 ; + uint8_t *result = 0 ; + + arg3 = &temp3; + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeRGB",&obj0)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeRGB" "', argument " "1"" of type '" "uint8_t const *""'"); + } + arg1 = (uint8_t *)(buf1); + arg2 = (size_t)(size1 - 1); + result = (uint8_t *)WebPDecodeRGB((uint8_t const *)arg1,arg2,arg3,arg4); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeRGB", arg3, arg4)); + } + if (SWIG_IsTmpObj(res3)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3))); + } else { + int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + free(result); + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_WebPDecodeRGBA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int res1 ; + char *buf1 = 0 ; + size_t size1 = 0 ; + int alloc1 = 0 ; + int temp3 ; + int res3 = SWIG_TMPOBJ ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + PyObject * obj0 = 0 ; + uint8_t *result = 0 ; + + arg3 = &temp3; + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeRGBA",&obj0)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeRGBA" "', argument " "1"" of type '" "uint8_t const *""'"); + } + arg1 = (uint8_t *)(buf1); + arg2 = (size_t)(size1 - 1); + result = (uint8_t *)WebPDecodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeRGBA", arg3, arg4)); + } + if (SWIG_IsTmpObj(res3)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3))); + } else { + int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + free(result); + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_WebPDecodeARGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int res1 ; + char *buf1 = 0 ; + size_t size1 = 0 ; + int alloc1 = 0 ; + int temp3 ; + int res3 = SWIG_TMPOBJ ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + PyObject * obj0 = 0 ; + uint8_t *result = 0 ; + + arg3 = &temp3; + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeARGB",&obj0)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeARGB" "', argument " "1"" of type '" "uint8_t const *""'"); + } + arg1 = (uint8_t *)(buf1); + arg2 = (size_t)(size1 - 1); + result = (uint8_t *)WebPDecodeARGB((uint8_t const *)arg1,arg2,arg3,arg4); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeARGB", arg3, arg4)); + } + if (SWIG_IsTmpObj(res3)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3))); + } else { + int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + free(result); + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_WebPDecodeBGR(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int res1 ; + char *buf1 = 0 ; + size_t size1 = 0 ; + int alloc1 = 0 ; + int temp3 ; + int res3 = SWIG_TMPOBJ ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + PyObject * obj0 = 0 ; + uint8_t *result = 0 ; + + arg3 = &temp3; + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeBGR",&obj0)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeBGR" "', argument " "1"" of type '" "uint8_t const *""'"); + } + arg1 = (uint8_t *)(buf1); + arg2 = (size_t)(size1 - 1); + result = (uint8_t *)WebPDecodeBGR((uint8_t const *)arg1,arg2,arg3,arg4); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeBGR", arg3, arg4)); + } + if (SWIG_IsTmpObj(res3)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3))); + } else { + int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + free(result); + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_WebPDecodeBGRA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + size_t arg2 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int res1 ; + char *buf1 = 0 ; + size_t size1 = 0 ; + int alloc1 = 0 ; + int temp3 ; + int res3 = SWIG_TMPOBJ ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + PyObject * obj0 = 0 ; + uint8_t *result = 0 ; + + arg3 = &temp3; + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeBGRA",&obj0)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeBGRA" "', argument " "1"" of type '" "uint8_t const *""'"); + } + arg1 = (uint8_t *)(buf1); + arg2 = (size_t)(size1 - 1); + result = (uint8_t *)WebPDecodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeBGRA", arg3, arg4)); + } + if (SWIG_IsTmpObj(res3)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3))); + } else { + int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + free(result); + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_WebPGetEncoderVersion(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + int result; + + if (!PyArg_ParseTuple(args,(char *)":WebPGetEncoderVersion")) SWIG_fail; + result = (int)WebPGetEncoderVersion(); + resultobj = SWIG_From_int((int)(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_wrap_WebPEncodeRGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + float arg8 ; + Py_buffer rgb_buffer1 ; + int temp2 ; + int res2 = 0 ; + int temp3 ; + int res3 = 0 ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + float val8 ; + int ecode8 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + PyObject * obj6 = 0 ; + uint8_t *result = 0 ; + + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeRGB",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail; + { + // NB: with Python < 2.6 the old style buffer protocol may be used: + // Py_ssize_t unused; + // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused); + if (!PyObject_CheckBuffer(obj0)) { + SWIG_exception_fail(SWIG_TypeError, + "in method 'wrap_WebPEncodeRGB', argument 1" + " does not support the buffer interface"); + } + if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) { + SWIG_exception_fail(SWIG_RuntimeError, + "in method 'wrap_WebPEncodeRGB', unable to get buffer view"); + } + arg1 = (uint8_t *)rgb_buffer1.buf; + } + if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj1, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGB" "', argument " "2"" of type '" "int""'"); + } + temp2 = (int)(val); + arg2 = &temp2; + res2 = SWIG_AddTmpMask(ecode); + } + if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj2, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGB" "', argument " "3"" of type '" "int""'"); + } + temp3 = (int)(val); + arg3 = &temp3; + res3 = SWIG_AddTmpMask(ecode); + } + ecode5 = SWIG_AsVal_int(obj3, &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeRGB" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + ecode6 = SWIG_AsVal_int(obj4, &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeRGB" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + ecode7 = SWIG_AsVal_int(obj5, &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeRGB" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + ecode8 = SWIG_AsVal_float(obj6, &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeRGB" "', argument " "8"" of type '" "float""'"); + } + arg8 = (float)(val8); + result = (uint8_t *)wrap_WebPEncodeRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeRGB", arg3, arg4)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + free(result); + return resultobj; +fail: + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_wrap_WebPEncodeBGR(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + float arg8 ; + Py_buffer rgb_buffer1 ; + int temp2 ; + int res2 = 0 ; + int temp3 ; + int res3 = 0 ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + float val8 ; + int ecode8 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + PyObject * obj6 = 0 ; + uint8_t *result = 0 ; + + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeBGR",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail; + { + // NB: with Python < 2.6 the old style buffer protocol may be used: + // Py_ssize_t unused; + // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused); + if (!PyObject_CheckBuffer(obj0)) { + SWIG_exception_fail(SWIG_TypeError, + "in method 'wrap_WebPEncodeBGR', argument 1" + " does not support the buffer interface"); + } + if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) { + SWIG_exception_fail(SWIG_RuntimeError, + "in method 'wrap_WebPEncodeBGR', unable to get buffer view"); + } + arg1 = (uint8_t *)rgb_buffer1.buf; + } + if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj1, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGR" "', argument " "2"" of type '" "int""'"); + } + temp2 = (int)(val); + arg2 = &temp2; + res2 = SWIG_AddTmpMask(ecode); + } + if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj2, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGR" "', argument " "3"" of type '" "int""'"); + } + temp3 = (int)(val); + arg3 = &temp3; + res3 = SWIG_AddTmpMask(ecode); + } + ecode5 = SWIG_AsVal_int(obj3, &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeBGR" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + ecode6 = SWIG_AsVal_int(obj4, &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeBGR" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + ecode7 = SWIG_AsVal_int(obj5, &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeBGR" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + ecode8 = SWIG_AsVal_float(obj6, &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeBGR" "', argument " "8"" of type '" "float""'"); + } + arg8 = (float)(val8); + result = (uint8_t *)wrap_WebPEncodeBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeBGR", arg3, arg4)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + free(result); + return resultobj; +fail: + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_wrap_WebPEncodeRGBA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + float arg8 ; + Py_buffer rgb_buffer1 ; + int temp2 ; + int res2 = 0 ; + int temp3 ; + int res3 = 0 ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + float val8 ; + int ecode8 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + PyObject * obj6 = 0 ; + uint8_t *result = 0 ; + + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeRGBA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail; + { + // NB: with Python < 2.6 the old style buffer protocol may be used: + // Py_ssize_t unused; + // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused); + if (!PyObject_CheckBuffer(obj0)) { + SWIG_exception_fail(SWIG_TypeError, + "in method 'wrap_WebPEncodeRGBA', argument 1" + " does not support the buffer interface"); + } + if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) { + SWIG_exception_fail(SWIG_RuntimeError, + "in method 'wrap_WebPEncodeRGBA', unable to get buffer view"); + } + arg1 = (uint8_t *)rgb_buffer1.buf; + } + if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj1, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGBA" "', argument " "2"" of type '" "int""'"); + } + temp2 = (int)(val); + arg2 = &temp2; + res2 = SWIG_AddTmpMask(ecode); + } + if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj2, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGBA" "', argument " "3"" of type '" "int""'"); + } + temp3 = (int)(val); + arg3 = &temp3; + res3 = SWIG_AddTmpMask(ecode); + } + ecode5 = SWIG_AsVal_int(obj3, &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeRGBA" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + ecode6 = SWIG_AsVal_int(obj4, &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeRGBA" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + ecode7 = SWIG_AsVal_int(obj5, &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeRGBA" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + ecode8 = SWIG_AsVal_float(obj6, &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeRGBA" "', argument " "8"" of type '" "float""'"); + } + arg8 = (float)(val8); + result = (uint8_t *)wrap_WebPEncodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeRGBA", arg3, arg4)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + free(result); + return resultobj; +fail: + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_wrap_WebPEncodeBGRA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + float arg8 ; + Py_buffer rgb_buffer1 ; + int temp2 ; + int res2 = 0 ; + int temp3 ; + int res3 = 0 ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + float val8 ; + int ecode8 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + PyObject * obj6 = 0 ; + uint8_t *result = 0 ; + + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeBGRA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail; + { + // NB: with Python < 2.6 the old style buffer protocol may be used: + // Py_ssize_t unused; + // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused); + if (!PyObject_CheckBuffer(obj0)) { + SWIG_exception_fail(SWIG_TypeError, + "in method 'wrap_WebPEncodeBGRA', argument 1" + " does not support the buffer interface"); + } + if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) { + SWIG_exception_fail(SWIG_RuntimeError, + "in method 'wrap_WebPEncodeBGRA', unable to get buffer view"); + } + arg1 = (uint8_t *)rgb_buffer1.buf; + } + if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj1, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGRA" "', argument " "2"" of type '" "int""'"); + } + temp2 = (int)(val); + arg2 = &temp2; + res2 = SWIG_AddTmpMask(ecode); + } + if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj2, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGRA" "', argument " "3"" of type '" "int""'"); + } + temp3 = (int)(val); + arg3 = &temp3; + res3 = SWIG_AddTmpMask(ecode); + } + ecode5 = SWIG_AsVal_int(obj3, &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeBGRA" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + ecode6 = SWIG_AsVal_int(obj4, &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeBGRA" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + ecode7 = SWIG_AsVal_int(obj5, &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeBGRA" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + ecode8 = SWIG_AsVal_float(obj6, &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeBGRA" "', argument " "8"" of type '" "float""'"); + } + arg8 = (float)(val8); + result = (uint8_t *)wrap_WebPEncodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeBGRA", arg3, arg4)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + free(result); + return resultobj; +fail: + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessRGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + Py_buffer rgb_buffer1 ; + int temp2 ; + int res2 = 0 ; + int temp3 ; + int res3 = 0 ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + uint8_t *result = 0 ; + + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessRGB",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail; + { + // NB: with Python < 2.6 the old style buffer protocol may be used: + // Py_ssize_t unused; + // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused); + if (!PyObject_CheckBuffer(obj0)) { + SWIG_exception_fail(SWIG_TypeError, + "in method 'wrap_WebPEncodeLosslessRGB', argument 1" + " does not support the buffer interface"); + } + if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) { + SWIG_exception_fail(SWIG_RuntimeError, + "in method 'wrap_WebPEncodeLosslessRGB', unable to get buffer view"); + } + arg1 = (uint8_t *)rgb_buffer1.buf; + } + if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj1, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "2"" of type '" "int""'"); + } + temp2 = (int)(val); + arg2 = &temp2; + res2 = SWIG_AddTmpMask(ecode); + } + if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj2, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "3"" of type '" "int""'"); + } + temp3 = (int)(val); + arg3 = &temp3; + res3 = SWIG_AddTmpMask(ecode); + } + ecode5 = SWIG_AsVal_int(obj3, &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + ecode6 = SWIG_AsVal_int(obj4, &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + ecode7 = SWIG_AsVal_int(obj5, &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + result = (uint8_t *)wrap_WebPEncodeLosslessRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessRGB", arg3, arg4)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + free(result); + return resultobj; +fail: + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessBGR(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + Py_buffer rgb_buffer1 ; + int temp2 ; + int res2 = 0 ; + int temp3 ; + int res3 = 0 ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + uint8_t *result = 0 ; + + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessBGR",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail; + { + // NB: with Python < 2.6 the old style buffer protocol may be used: + // Py_ssize_t unused; + // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused); + if (!PyObject_CheckBuffer(obj0)) { + SWIG_exception_fail(SWIG_TypeError, + "in method 'wrap_WebPEncodeLosslessBGR', argument 1" + " does not support the buffer interface"); + } + if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) { + SWIG_exception_fail(SWIG_RuntimeError, + "in method 'wrap_WebPEncodeLosslessBGR', unable to get buffer view"); + } + arg1 = (uint8_t *)rgb_buffer1.buf; + } + if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj1, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "2"" of type '" "int""'"); + } + temp2 = (int)(val); + arg2 = &temp2; + res2 = SWIG_AddTmpMask(ecode); + } + if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj2, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "3"" of type '" "int""'"); + } + temp3 = (int)(val); + arg3 = &temp3; + res3 = SWIG_AddTmpMask(ecode); + } + ecode5 = SWIG_AsVal_int(obj3, &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + ecode6 = SWIG_AsVal_int(obj4, &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + ecode7 = SWIG_AsVal_int(obj5, &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + result = (uint8_t *)wrap_WebPEncodeLosslessBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessBGR", arg3, arg4)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + free(result); + return resultobj; +fail: + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessRGBA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + Py_buffer rgb_buffer1 ; + int temp2 ; + int res2 = 0 ; + int temp3 ; + int res3 = 0 ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + uint8_t *result = 0 ; + + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessRGBA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail; + { + // NB: with Python < 2.6 the old style buffer protocol may be used: + // Py_ssize_t unused; + // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused); + if (!PyObject_CheckBuffer(obj0)) { + SWIG_exception_fail(SWIG_TypeError, + "in method 'wrap_WebPEncodeLosslessRGBA', argument 1" + " does not support the buffer interface"); + } + if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) { + SWIG_exception_fail(SWIG_RuntimeError, + "in method 'wrap_WebPEncodeLosslessRGBA', unable to get buffer view"); + } + arg1 = (uint8_t *)rgb_buffer1.buf; + } + if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj1, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "2"" of type '" "int""'"); + } + temp2 = (int)(val); + arg2 = &temp2; + res2 = SWIG_AddTmpMask(ecode); + } + if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj2, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "3"" of type '" "int""'"); + } + temp3 = (int)(val); + arg3 = &temp3; + res3 = SWIG_AddTmpMask(ecode); + } + ecode5 = SWIG_AsVal_int(obj3, &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + ecode6 = SWIG_AsVal_int(obj4, &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + ecode7 = SWIG_AsVal_int(obj5, &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + result = (uint8_t *)wrap_WebPEncodeLosslessRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessRGBA", arg3, arg4)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + free(result); + return resultobj; +fail: + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + return NULL; +} + + +SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessBGRA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + uint8_t *arg1 = (uint8_t *) 0 ; + int *arg2 = (int *) 0 ; + int *arg3 = (int *) 0 ; + int *arg4 = (int *) 0 ; + int arg5 ; + int arg6 ; + int arg7 ; + Py_buffer rgb_buffer1 ; + int temp2 ; + int res2 = 0 ; + int temp3 ; + int res3 = 0 ; + int temp4 ; + int res4 = SWIG_TMPOBJ ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + PyObject * obj4 = 0 ; + PyObject * obj5 = 0 ; + uint8_t *result = 0 ; + + arg4 = &temp4; + if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessBGRA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail; + { + // NB: with Python < 2.6 the old style buffer protocol may be used: + // Py_ssize_t unused; + // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused); + if (!PyObject_CheckBuffer(obj0)) { + SWIG_exception_fail(SWIG_TypeError, + "in method 'wrap_WebPEncodeLosslessBGRA', argument 1" + " does not support the buffer interface"); + } + if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) { + SWIG_exception_fail(SWIG_RuntimeError, + "in method 'wrap_WebPEncodeLosslessBGRA', unable to get buffer view"); + } + arg1 = (uint8_t *)rgb_buffer1.buf; + } + if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj1, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "2"" of type '" "int""'"); + } + temp2 = (int)(val); + arg2 = &temp2; + res2 = SWIG_AddTmpMask(ecode); + } + if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) { + int val; + int ecode = SWIG_AsVal_int(obj2, &val); + if (!SWIG_IsOK(ecode)) { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "3"" of type '" "int""'"); + } + temp3 = (int)(val); + arg3 = &temp3; + res3 = SWIG_AddTmpMask(ecode); + } + ecode5 = SWIG_AsVal_int(obj3, &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + ecode6 = SWIG_AsVal_int(obj4, &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + ecode7 = SWIG_AsVal_int(obj5, &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + result = (uint8_t *)wrap_WebPEncodeLosslessBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7); + { + resultobj = PyString_FromStringAndSize( + (const char*)result, + (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessBGRA", arg3, arg4)); + } + if (SWIG_IsTmpObj(res4)) { + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4))); + } else { + int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags)); + } + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + free(result); + return resultobj; +fail: + { + PyBuffer_Release(&rgb_buffer1); + } + if (SWIG_IsNewObj(res2)) free((char*)arg2); + if (SWIG_IsNewObj(res3)) free((char*)arg3); + return NULL; +} + + +static PyMethodDef SwigMethods[] = { + { "SWIG_PyInstanceMethod_New", SWIG_PyInstanceMethod_New, METH_O, NULL}, + { "WebPGetDecoderVersion", _wrap_WebPGetDecoderVersion, METH_VARARGS, (char *)"WebPGetDecoderVersion() -> int"}, + { "WebPGetInfo", _wrap_WebPGetInfo, METH_VARARGS, (char *)"WebPGetInfo(uint8_t data) -> (width, height)"}, + { "WebPDecodeRGB", _wrap_WebPDecodeRGB, METH_VARARGS, (char *)"WebPDecodeRGB(uint8_t data) -> (rgb, width, height)"}, + { "WebPDecodeRGBA", _wrap_WebPDecodeRGBA, METH_VARARGS, (char *)"WebPDecodeRGBA(uint8_t data) -> (rgb, width, height)"}, + { "WebPDecodeARGB", _wrap_WebPDecodeARGB, METH_VARARGS, (char *)"WebPDecodeARGB(uint8_t data) -> (rgb, width, height)"}, + { "WebPDecodeBGR", _wrap_WebPDecodeBGR, METH_VARARGS, (char *)"WebPDecodeBGR(uint8_t data) -> (rgb, width, height)"}, + { "WebPDecodeBGRA", _wrap_WebPDecodeBGRA, METH_VARARGS, (char *)"WebPDecodeBGRA(uint8_t data) -> (rgb, width, height)"}, + { "WebPGetEncoderVersion", _wrap_WebPGetEncoderVersion, METH_VARARGS, (char *)"WebPGetEncoderVersion() -> int"}, + { "wrap_WebPEncodeRGB", _wrap_wrap_WebPEncodeRGB, METH_VARARGS, (char *)"private, do not call directly."}, + { "wrap_WebPEncodeBGR", _wrap_wrap_WebPEncodeBGR, METH_VARARGS, (char *)"private, do not call directly."}, + { "wrap_WebPEncodeRGBA", _wrap_wrap_WebPEncodeRGBA, METH_VARARGS, (char *)"private, do not call directly."}, + { "wrap_WebPEncodeBGRA", _wrap_wrap_WebPEncodeBGRA, METH_VARARGS, (char *)"private, do not call directly."}, + { "wrap_WebPEncodeLosslessRGB", _wrap_wrap_WebPEncodeLosslessRGB, METH_VARARGS, (char *)"private, do not call directly."}, + { "wrap_WebPEncodeLosslessBGR", _wrap_wrap_WebPEncodeLosslessBGR, METH_VARARGS, (char *)"private, do not call directly."}, + { "wrap_WebPEncodeLosslessRGBA", _wrap_wrap_WebPEncodeLosslessRGBA, METH_VARARGS, (char *)"private, do not call directly."}, + { "wrap_WebPEncodeLosslessBGRA", _wrap_wrap_WebPEncodeLosslessBGRA, METH_VARARGS, (char *)"private, do not call directly."}, + { NULL, NULL, 0, NULL } +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ + +static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_int = {"_p_int", "int *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_uint8_t = {"_p_uint8_t", "uint8_t *", 0, 0, (void*)0, 0}; + +static swig_type_info *swig_type_initial[] = { + &_swigt__p_char, + &_swigt__p_int, + &_swigt__p_uint8_t, +}; + +static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_int[] = { {&_swigt__p_int, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_uint8_t[] = { {&_swigt__p_uint8_t, 0, 0, 0},{0, 0, 0, 0}}; + +static swig_cast_info *swig_cast_initial[] = { + _swigc__p_char, + _swigc__p_int, + _swigc__p_uint8_t, +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ + +static swig_const_info swig_const_table[] = { +{0, 0, 0, 0.0, 0, 0}}; + +#ifdef __cplusplus +} +#endif +/* ----------------------------------------------------------------------------- + * Type initialization: + * This problem is tough by the requirement that no dynamic + * memory is used. Also, since swig_type_info structures store pointers to + * swig_cast_info structures and swig_cast_info structures store pointers back + * to swig_type_info structures, we need some lookup code at initialization. + * The idea is that swig generates all the structures that are needed. + * The runtime then collects these partially filled structures. + * The SWIG_InitializeModule function takes these initial arrays out of + * swig_module, and does all the lookup, filling in the swig_module.types + * array with the correct data and linking the correct swig_cast_info + * structures together. + * + * The generated swig_type_info structures are assigned statically to an initial + * array. We just loop through that array, and handle each type individually. + * First we lookup if this type has been already loaded, and if so, use the + * loaded structure instead of the generated one. Then we have to fill in the + * cast linked list. The cast data is initially stored in something like a + * two-dimensional array. Each row corresponds to a type (there are the same + * number of rows as there are in the swig_type_initial array). Each entry in + * a column is one of the swig_cast_info structures for that type. + * The cast_initial array is actually an array of arrays, because each row has + * a variable number of columns. So to actually build the cast linked list, + * we find the array of casts associated with the type, and loop through it + * adding the casts to the list. The one last trick we need to do is making + * sure the type pointer in the swig_cast_info struct is correct. + * + * First off, we lookup the cast->type name to see if it is already loaded. + * There are three cases to handle: + * 1) If the cast->type has already been loaded AND the type we are adding + * casting info to has not been loaded (it is in this module), THEN we + * replace the cast->type pointer with the type pointer that has already + * been loaded. + * 2) If BOTH types (the one we are adding casting info to, and the + * cast->type) are loaded, THEN the cast info has already been loaded by + * the previous module so we just ignore it. + * 3) Finally, if cast->type has not already been loaded, then we add that + * swig_cast_info to the linked list (because the cast->type) pointer will + * be correct. + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* c-mode */ +#endif +#endif + +#if 0 +#define SWIGRUNTIME_DEBUG +#endif + + +SWIGRUNTIME void +SWIG_InitializeModule(void *clientdata) { + size_t i; + swig_module_info *module_head, *iter; + int init; + + /* check to see if the circular list has been setup, if not, set it up */ + if (swig_module.next==0) { + /* Initialize the swig_module */ + swig_module.type_initial = swig_type_initial; + swig_module.cast_initial = swig_cast_initial; + swig_module.next = &swig_module; + init = 1; + } else { + init = 0; + } + + /* Try and load any already created modules */ + module_head = SWIG_GetModule(clientdata); + if (!module_head) { + /* This is the first module loaded for this interpreter */ + /* so set the swig module into the interpreter */ + SWIG_SetModule(clientdata, &swig_module); + } else { + /* the interpreter has loaded a SWIG module, but has it loaded this one? */ + iter=module_head; + do { + if (iter==&swig_module) { + /* Our module is already in the list, so there's nothing more to do. */ + return; + } + iter=iter->next; + } while (iter!= module_head); + + /* otherwise we must add our module into the list */ + swig_module.next = module_head->next; + module_head->next = &swig_module; + } + + /* When multiple interpreters are used, a module could have already been initialized in + a different interpreter, but not yet have a pointer in this interpreter. + In this case, we do not want to continue adding types... everything should be + set up already */ + if (init == 0) return; + + /* Now work on filling in swig_module.types */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: size %d\n", swig_module.size); +#endif + for (i = 0; i < swig_module.size; ++i) { + swig_type_info *type = 0; + swig_type_info *ret; + swig_cast_info *cast; + +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); +#endif + + /* if there is another module already loaded */ + if (swig_module.next != &swig_module) { + type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name); + } + if (type) { + /* Overwrite clientdata field */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found type %s\n", type->name); +#endif + if (swig_module.type_initial[i]->clientdata) { + type->clientdata = swig_module.type_initial[i]->clientdata; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name); +#endif + } + } else { + type = swig_module.type_initial[i]; + } + + /* Insert casting types */ + cast = swig_module.cast_initial[i]; + while (cast->type) { + /* Don't need to add information already in the list */ + ret = 0; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: look cast %s\n", cast->type->name); +#endif + if (swig_module.next != &swig_module) { + ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name); +#ifdef SWIGRUNTIME_DEBUG + if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name); +#endif + } + if (ret) { + if (type == swig_module.type_initial[i]) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: skip old type %s\n", ret->name); +#endif + cast->type = ret; + ret = 0; + } else { + /* Check for casting already in the list */ + swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type); +#ifdef SWIGRUNTIME_DEBUG + if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name); +#endif + if (!ocast) ret = 0; + } + } + + if (!ret) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name); +#endif + if (type->cast) { + type->cast->prev = cast; + cast->next = type->cast; + } + type->cast = cast; + } + cast++; + } + /* Set entry in modules->types array equal to the type */ + swig_module.types[i] = type; + } + swig_module.types[i] = 0; + +#ifdef SWIGRUNTIME_DEBUG + printf("**** SWIG_InitializeModule: Cast List ******\n"); + for (i = 0; i < swig_module.size; ++i) { + int j = 0; + swig_cast_info *cast = swig_module.cast_initial[i]; + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); + while (cast->type) { + printf("SWIG_InitializeModule: cast type %s\n", cast->type->name); + cast++; + ++j; + } + printf("---- Total casts: %d\n",j); + } + printf("**** SWIG_InitializeModule: Cast List ******\n"); +#endif +} + +/* This function will propagate the clientdata field of type to +* any new swig_type_info structures that have been added into the list +* of equivalent types. It is like calling +* SWIG_TypeClientData(type, clientdata) a second time. +*/ +SWIGRUNTIME void +SWIG_PropagateClientData(void) { + size_t i; + swig_cast_info *equiv; + static int init_run = 0; + + if (init_run) return; + init_run = 1; + + for (i = 0; i < swig_module.size; i++) { + if (swig_module.types[i]->clientdata) { + equiv = swig_module.types[i]->cast; + while (equiv) { + if (!equiv->converter) { + if (equiv->type && !equiv->type->clientdata) + SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata); + } + equiv = equiv->next; + } + } + } +} + +#ifdef __cplusplus +#if 0 +{ + /* c-mode */ +#endif +} +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + /* Python-specific SWIG API */ +#define SWIG_newvarlink() SWIG_Python_newvarlink() +#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr) +#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants) + + /* ----------------------------------------------------------------------------- + * global variable support code. + * ----------------------------------------------------------------------------- */ + + typedef struct swig_globalvar { + char *name; /* Name of global variable */ + PyObject *(*get_attr)(void); /* Return the current value */ + int (*set_attr)(PyObject *); /* Set the value */ + struct swig_globalvar *next; + } swig_globalvar; + + typedef struct swig_varlinkobject { + PyObject_HEAD + swig_globalvar *vars; + } swig_varlinkobject; + + SWIGINTERN PyObject * + swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) { +#if PY_VERSION_HEX >= 0x03000000 + return PyUnicode_InternFromString(""); +#else + return PyString_FromString(""); +#endif + } + + SWIGINTERN PyObject * + swig_varlink_str(swig_varlinkobject *v) { +#if PY_VERSION_HEX >= 0x03000000 + PyObject *str = PyUnicode_InternFromString("("); + PyObject *tail; + PyObject *joined; + swig_globalvar *var; + for (var = v->vars; var; var=var->next) { + tail = PyUnicode_FromString(var->name); + joined = PyUnicode_Concat(str, tail); + Py_DecRef(str); + Py_DecRef(tail); + str = joined; + if (var->next) { + tail = PyUnicode_InternFromString(", "); + joined = PyUnicode_Concat(str, tail); + Py_DecRef(str); + Py_DecRef(tail); + str = joined; + } + } + tail = PyUnicode_InternFromString(")"); + joined = PyUnicode_Concat(str, tail); + Py_DecRef(str); + Py_DecRef(tail); + str = joined; +#else + PyObject *str = PyString_FromString("("); + swig_globalvar *var; + for (var = v->vars; var; var=var->next) { + PyString_ConcatAndDel(&str,PyString_FromString(var->name)); + if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", ")); + } + PyString_ConcatAndDel(&str,PyString_FromString(")")); +#endif + return str; + } + + SWIGINTERN int + swig_varlink_print(swig_varlinkobject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) { + char *tmp; + PyObject *str = swig_varlink_str(v); + fprintf(fp,"Swig global variables "); + fprintf(fp,"%s\n", tmp = SWIG_Python_str_AsChar(str)); + SWIG_Python_str_DelForPy3(tmp); + Py_DECREF(str); + return 0; + } + + SWIGINTERN void + swig_varlink_dealloc(swig_varlinkobject *v) { + swig_globalvar *var = v->vars; + while (var) { + swig_globalvar *n = var->next; + free(var->name); + free(var); + var = n; + } + } + + SWIGINTERN PyObject * + swig_varlink_getattr(swig_varlinkobject *v, char *n) { + PyObject *res = NULL; + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + res = (*var->get_attr)(); + break; + } + var = var->next; + } + if (res == NULL && !PyErr_Occurred()) { + PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); + } + return res; + } + + SWIGINTERN int + swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) { + int res = 1; + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + res = (*var->set_attr)(p); + break; + } + var = var->next; + } + if (res == 1 && !PyErr_Occurred()) { + PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); + } + return res; + } + + SWIGINTERN PyTypeObject* + swig_varlink_type(void) { + static char varlink__doc__[] = "Swig var link object"; + static PyTypeObject varlink_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp = { +#if PY_VERSION_HEX >= 0x03000000 + PyVarObject_HEAD_INIT(NULL, 0) +#else + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ +#endif + (char *)"swigvarlink", /* tp_name */ + sizeof(swig_varlinkobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) swig_varlink_dealloc, /* tp_dealloc */ + (printfunc) swig_varlink_print, /* tp_print */ + (getattrfunc) swig_varlink_getattr, /* tp_getattr */ + (setattrfunc) swig_varlink_setattr, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc) swig_varlink_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc) swig_varlink_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0, /* tp_flags */ + varlink__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#if PY_VERSION_HEX >= 0x02060000 + 0, /* tp_version_tag */ +#endif +#if PY_VERSION_HEX >= 0x03040000 + 0, /* tp_finalize */ +#endif +#ifdef COUNT_ALLOCS + 0, /* tp_allocs */ + 0, /* tp_frees */ + 0, /* tp_maxalloc */ +#if PY_VERSION_HEX >= 0x02050000 + 0, /* tp_prev */ +#endif + 0 /* tp_next */ +#endif + }; + varlink_type = tmp; + type_init = 1; +#if PY_VERSION_HEX < 0x02020000 + varlink_type.ob_type = &PyType_Type; +#else + if (PyType_Ready(&varlink_type) < 0) + return NULL; +#endif + } + return &varlink_type; + } + + /* Create a variable linking object for use later */ + SWIGINTERN PyObject * + SWIG_Python_newvarlink(void) { + swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type()); + if (result) { + result->vars = 0; + } + return ((PyObject*) result); + } + + SWIGINTERN void + SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) { + swig_varlinkobject *v = (swig_varlinkobject *) p; + swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar)); + if (gv) { + size_t size = strlen(name)+1; + gv->name = (char *)malloc(size); + if (gv->name) { + strncpy(gv->name,name,size); + gv->get_attr = get_attr; + gv->set_attr = set_attr; + gv->next = v->vars; + } + } + v->vars = gv; + } + + SWIGINTERN PyObject * + SWIG_globals(void) { + static PyObject *_SWIG_globals = 0; + if (!_SWIG_globals) _SWIG_globals = SWIG_newvarlink(); + return _SWIG_globals; + } + + /* ----------------------------------------------------------------------------- + * constants/methods manipulation + * ----------------------------------------------------------------------------- */ + + /* Install Constants */ + SWIGINTERN void + SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) { + PyObject *obj = 0; + size_t i; + for (i = 0; constants[i].type; ++i) { + switch(constants[i].type) { + case SWIG_PY_POINTER: + obj = SWIG_InternalNewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0); + break; + case SWIG_PY_BINARY: + obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype)); + break; + default: + obj = 0; + break; + } + if (obj) { + PyDict_SetItemString(d, constants[i].name, obj); + Py_DECREF(obj); + } + } + } + + /* -----------------------------------------------------------------------------*/ + /* Fix SwigMethods to carry the callback ptrs when needed */ + /* -----------------------------------------------------------------------------*/ + + SWIGINTERN void + SWIG_Python_FixMethods(PyMethodDef *methods, + swig_const_info *const_table, + swig_type_info **types, + swig_type_info **types_initial) { + size_t i; + for (i = 0; methods[i].ml_name; ++i) { + const char *c = methods[i].ml_doc; + if (!c) continue; + c = strstr(c, "swig_ptr: "); + if (c) { + int j; + swig_const_info *ci = 0; + const char *name = c + 10; + for (j = 0; const_table[j].type; ++j) { + if (strncmp(const_table[j].name, name, + strlen(const_table[j].name)) == 0) { + ci = &(const_table[j]); + break; + } + } + if (ci) { + void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0; + if (ptr) { + size_t shift = (ci->ptype) - types; + swig_type_info *ty = types_initial[shift]; + size_t ldoc = (c - methods[i].ml_doc); + size_t lptr = strlen(ty->name)+2*sizeof(void*)+2; + char *ndoc = (char*)malloc(ldoc + lptr + 10); + if (ndoc) { + char *buff = ndoc; + memcpy(buff, methods[i].ml_doc, ldoc); + buff += ldoc; + memcpy(buff, "swig_ptr: ", 10); + buff += 10; + SWIG_PackVoidPtr(buff, ptr, ty->name, lptr); + methods[i].ml_doc = ndoc; + } + } + } + } + } + } + +#ifdef __cplusplus +} +#endif + +/* -----------------------------------------------------------------------------* + * Partial Init method + * -----------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" +#endif + +SWIGEXPORT +#if PY_VERSION_HEX >= 0x03000000 +PyObject* +#else +void +#endif +SWIG_init(void) { + PyObject *m, *d, *md; +#if PY_VERSION_HEX >= 0x03000000 + static struct PyModuleDef SWIG_module = { +# if PY_VERSION_HEX >= 0x03020000 + PyModuleDef_HEAD_INIT, +# else + { + PyObject_HEAD_INIT(NULL) + NULL, /* m_init */ + 0, /* m_index */ + NULL, /* m_copy */ + }, +# endif + (char *) SWIG_name, + NULL, + -1, + SwigMethods, + NULL, + NULL, + NULL, + NULL + }; +#endif + +#if defined(SWIGPYTHON_BUILTIN) + static SwigPyClientData SwigPyObject_clientdata = { + 0, 0, 0, 0, 0, 0, 0 + }; + static PyGetSetDef this_getset_def = { + (char *)"this", &SwigPyBuiltin_ThisClosure, NULL, NULL, NULL + }; + static SwigPyGetSet thisown_getset_closure = { + SwigPyObject_own, + SwigPyObject_own + }; + static PyGetSetDef thisown_getset_def = { + (char *)"thisown", SwigPyBuiltin_GetterClosure, SwigPyBuiltin_SetterClosure, NULL, &thisown_getset_closure + }; + PyTypeObject *builtin_pytype; + int builtin_base_count; + swig_type_info *builtin_basetype; + PyObject *tuple; + PyGetSetDescrObject *static_getset; + PyTypeObject *metatype; + PyTypeObject *swigpyobject; + SwigPyClientData *cd; + PyObject *public_interface, *public_symbol; + PyObject *this_descr; + PyObject *thisown_descr; + PyObject *self = 0; + int i; + + (void)builtin_pytype; + (void)builtin_base_count; + (void)builtin_basetype; + (void)tuple; + (void)static_getset; + (void)self; + + /* Metaclass is used to implement static member variables */ + metatype = SwigPyObjectType(); + assert(metatype); +#endif + + /* Fix SwigMethods to carry the callback ptrs when needed */ + SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial); + +#if PY_VERSION_HEX >= 0x03000000 + m = PyModule_Create(&SWIG_module); +#else + m = Py_InitModule((char *) SWIG_name, SwigMethods); +#endif + + md = d = PyModule_GetDict(m); + (void)md; + + SWIG_InitializeModule(0); + +#ifdef SWIGPYTHON_BUILTIN + swigpyobject = SwigPyObject_TypeOnce(); + + SwigPyObject_stype = SWIG_MangledTypeQuery("_p_SwigPyObject"); + assert(SwigPyObject_stype); + cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; + if (!cd) { + SwigPyObject_stype->clientdata = &SwigPyObject_clientdata; + SwigPyObject_clientdata.pytype = swigpyobject; + } else if (swigpyobject->tp_basicsize != cd->pytype->tp_basicsize) { + PyErr_SetString(PyExc_RuntimeError, "Import error: attempted to load two incompatible swig-generated modules."); +# if PY_VERSION_HEX >= 0x03000000 + return NULL; +# else + return; +# endif + } + + /* All objects have a 'this' attribute */ + this_descr = PyDescr_NewGetSet(SwigPyObject_type(), &this_getset_def); + (void)this_descr; + + /* All objects have a 'thisown' attribute */ + thisown_descr = PyDescr_NewGetSet(SwigPyObject_type(), &thisown_getset_def); + (void)thisown_descr; + + public_interface = PyList_New(0); + public_symbol = 0; + (void)public_symbol; + + PyDict_SetItemString(md, "__all__", public_interface); + Py_DECREF(public_interface); + for (i = 0; SwigMethods[i].ml_name != NULL; ++i) + SwigPyBuiltin_AddPublicSymbol(public_interface, SwigMethods[i].ml_name); + for (i = 0; swig_const_table[i].name != 0; ++i) + SwigPyBuiltin_AddPublicSymbol(public_interface, swig_const_table[i].name); +#endif + + SWIG_InstallConstants(d,swig_const_table); + +#if PY_VERSION_HEX >= 0x03000000 + return m; +#else + return; +#endif +} + diff --git a/internal/libwebp-1.2.1/swig/setup.py b/internal/libwebp-1.2.1/swig/setup.py new file mode 100644 index 0000000..3a3bfe1 --- /dev/null +++ b/internal/libwebp-1.2.1/swig/setup.py @@ -0,0 +1,40 @@ +#!/usr/bin/python + +"""distutils script for libwebp python module.""" + +from distutils.core import setup +from distutils.extension import Extension +import os +import shutil +import tempfile + +tmpdir = tempfile.mkdtemp() +package = "com.google.webp" +package_path = os.path.join(tmpdir, *package.split(".")) +os.makedirs(package_path) + +# Create __init_.py files along the package path. +initpy_path = tmpdir +for d in package.split("."): + initpy_path = os.path.join(initpy_path, d) + open(os.path.join(initpy_path, "__init__.py"), "w").close() + +shutil.copy2("libwebp.py", package_path) +setup(name="libwebp", + version="0.0", + description="libwebp python wrapper", + long_description="Provides access to 'simple' libwebp decode interface", + license="BSD", + url="http://developers.google.com/speed/webp", + ext_package=package, + ext_modules=[Extension("_libwebp", + ["libwebp_python_wrap.c"], + libraries=["webp"], + ), + ], + package_dir={"": tmpdir}, + packages=["com", "com.google", "com.google.webp"], + py_modules=[package + ".libwebp"], + ) + +shutil.rmtree(tmpdir) diff --git a/internal/libwebp-1.2.1/tests/README b/internal/libwebp-1.2.1/tests/README new file mode 100644 index 0000000..2f80681 --- /dev/null +++ b/internal/libwebp-1.2.1/tests/README @@ -0,0 +1,19 @@ +Description: +============ + +This is a collection of tests for the libwebp libraries, currently covering +fuzzing through the APIs. Additional test vector coverage can be found at: +https://chromium.googlesource.com/webm/libwebp-test-data + +Building: +========= + +Fuzzers: +-------- + +Follow the build instructions in ../README for libwebp, optionally adding build +flags for various sanitizers (e.g., -fsanitize=address). + +fuzzer/makefile.unix can then be used to compile the fuzzer targets: + +$ make -C fuzzer -f makefile.unix diff --git a/internal/libwebp-1.2.1/tests/fuzzer/advanced_api_fuzzer.c b/internal/libwebp-1.2.1/tests/fuzzer/advanced_api_fuzzer.c new file mode 100644 index 0000000..a5323e4 --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/advanced_api_fuzzer.c @@ -0,0 +1,114 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#include + +#include "./fuzz_utils.h" +#include "src/webp/decode.h" + +int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { + int i; + WebPDecoderConfig config; + if (!WebPInitDecoderConfig(&config)) return 0; + if (WebPGetFeatures(data, size, &config.input) != VP8_STATUS_OK) return 0; + if ((size_t)config.input.width * config.input.height > kFuzzPxLimit) return 0; + + // Using two independent criteria ensures that all combinations of options + // can reach each path at the decoding stage, with meaningful differences. + + const uint8_t value = FuzzHash(data, size); + const float factor = value / 255.f; // 0-1 + + config.options.flip = value & 1; + config.options.bypass_filtering = value & 2; + config.options.no_fancy_upsampling = value & 4; + config.options.use_threads = value & 8; + if (size & 1) { + config.options.use_cropping = 1; + config.options.crop_width = (int)(config.input.width * (1 - factor)); + config.options.crop_height = (int)(config.input.height * (1 - factor)); + config.options.crop_left = config.input.width - config.options.crop_width; + config.options.crop_top = config.input.height - config.options.crop_height; + } + if (size & 2) { + int strength = (int)(factor * 100); + config.options.dithering_strength = strength; + config.options.alpha_dithering_strength = 100 - strength; + } + if (size & 4) { + config.options.use_scaling = 1; + config.options.scaled_width = (int)(config.input.width * factor * 2); + config.options.scaled_height = (int)(config.input.height * factor * 2); + } + +#if defined(WEBP_REDUCE_CSP) + config.output.colorspace = (value & 1) + ? ((value & 2) ? MODE_RGBA : MODE_BGRA) + : ((value & 2) ? MODE_rgbA : MODE_bgrA); +#else + config.output.colorspace = (WEBP_CSP_MODE)(value % MODE_LAST); +#endif // WEBP_REDUCE_CSP + + for (i = 0; i < 2; ++i) { + if (i == 1) { + // Use the bitstream data to generate extreme ranges for the options. An + // alternative approach would be to use a custom corpus containing webp + // files prepended with sizeof(config.options) zeroes to allow the fuzzer + // to modify these independently. + const int data_offset = 50; + if (size > data_offset + sizeof(config.options)) { + memcpy(&config.options, data + data_offset, sizeof(config.options)); + } else { + break; + } + } + if (size % 3) { + // Decodes incrementally in chunks of increasing size. + WebPIDecoder* idec = WebPIDecode(NULL, 0, &config); + if (!idec) return 0; + VP8StatusCode status; + if (size & 8) { + size_t available_size = value + 1; + while (1) { + if (available_size > size) available_size = size; + status = WebPIUpdate(idec, data, available_size); + if (status != VP8_STATUS_SUSPENDED || available_size == size) break; + available_size *= 2; + } + } else { + // WebPIAppend expects new data and its size with each call. + // Implemented here by simply advancing the pointer into data. + const uint8_t* new_data = data; + size_t new_size = value + 1; + while (1) { + if (new_data + new_size > data + size) { + new_size = data + size - new_data; + } + status = WebPIAppend(idec, new_data, new_size); + if (status != VP8_STATUS_SUSPENDED || new_size == 0) break; + new_data += new_size; + new_size *= 2; + } + } + WebPIDelete(idec); + } else { + WebPDecode(data, size, &config); + } + + WebPFreeDecBuffer(&config.output); + } + return 0; +} diff --git a/internal/libwebp-1.2.1/tests/fuzzer/animation_api_fuzzer.c b/internal/libwebp-1.2.1/tests/fuzzer/animation_api_fuzzer.c new file mode 100644 index 0000000..187ed24 --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/animation_api_fuzzer.c @@ -0,0 +1,78 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "./fuzz_utils.h" +#include "src/webp/decode.h" +#include "src/webp/demux.h" +#include "src/webp/mux_types.h" + +int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { + WebPData webp_data; + WebPDataInit(&webp_data); + webp_data.size = size; + webp_data.bytes = data; + + // WebPAnimDecoderNew uses WebPDemux internally to calloc canvas size. + WebPDemuxer* const demux = WebPDemux(&webp_data); + if (!demux) return 0; + const uint32_t cw = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); + const uint32_t ch = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); + if ((size_t)cw * ch > kFuzzPxLimit) { + WebPDemuxDelete(demux); + return 0; + } + + // In addition to canvas size, check each frame separately. + WebPIterator iter; + for (int i = 0; i < kFuzzFrameLimit; i++) { + if (!WebPDemuxGetFrame(demux, i + 1, &iter)) break; + int w, h; + if (WebPGetInfo(iter.fragment.bytes, iter.fragment.size, &w, &h)) { + if ((size_t)w * h > kFuzzPxLimit) { // image size of the frame payload + WebPDemuxReleaseIterator(&iter); + WebPDemuxDelete(demux); + return 0; + } + } + } + + WebPDemuxReleaseIterator(&iter); + WebPDemuxDelete(demux); + + WebPAnimDecoderOptions dec_options; + if (!WebPAnimDecoderOptionsInit(&dec_options)) return 0; + + dec_options.use_threads = size & 1; + // Animations only support 4 (of 12) modes. + dec_options.color_mode = (WEBP_CSP_MODE)(size % MODE_LAST); + if (dec_options.color_mode != MODE_BGRA && + dec_options.color_mode != MODE_rgbA && + dec_options.color_mode != MODE_bgrA) { + dec_options.color_mode = MODE_RGBA; + } + + WebPAnimDecoder* dec = WebPAnimDecoderNew(&webp_data, &dec_options); + if (!dec) return 0; + + for (int i = 0; i < kFuzzFrameLimit; i++) { + uint8_t* buf; + int timestamp; + if (!WebPAnimDecoderGetNext(dec, &buf, ×tamp)) break; + } + + WebPAnimDecoderDelete(dec); + return 0; +} diff --git a/internal/libwebp-1.2.1/tests/fuzzer/animdecoder_fuzzer.cc b/internal/libwebp-1.2.1/tests/fuzzer/animdecoder_fuzzer.cc new file mode 100644 index 0000000..a79712d --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/animdecoder_fuzzer.cc @@ -0,0 +1,52 @@ +// Copyright 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "examples/anim_util.h" +#include "imageio/imageio_util.h" +#include "src/webp/demux.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + // WebPAnimDecoderGetInfo() is too late to check the canvas size as + // WebPAnimDecoderNew() will handle the allocations. + WebPBitstreamFeatures features; + if (WebPGetFeatures(data, size, &features) == VP8_STATUS_OK) { + if (!ImgIoUtilCheckSizeArgumentsOverflow(features.width * 4, + features.height)) { + return 0; + } + } + + // decode everything as an animation + WebPData webp_data = { data, size }; + WebPAnimDecoder* const dec = WebPAnimDecoderNew(&webp_data, NULL); + if (dec == NULL) return 0; + + WebPAnimInfo info; + if (!WebPAnimDecoderGetInfo(dec, &info)) goto End; + if (!ImgIoUtilCheckSizeArgumentsOverflow(info.canvas_width * 4, + info.canvas_height)) { + goto End; + } + + while (WebPAnimDecoderHasMoreFrames(dec)) { + uint8_t* buf; + int timestamp; + if (!WebPAnimDecoderGetNext(dec, &buf, ×tamp)) break; + } + End: + WebPAnimDecoderDelete(dec); + return 0; +} diff --git a/internal/libwebp-1.2.1/tests/fuzzer/animencoder_fuzzer.cc b/internal/libwebp-1.2.1/tests/fuzzer/animencoder_fuzzer.cc new file mode 100644 index 0000000..1bd7871 --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/animencoder_fuzzer.cc @@ -0,0 +1,177 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "./fuzz_utils.h" +#include "src/webp/encode.h" +#include "src/webp/mux.h" + +namespace { + +const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo; + +int AddFrame(WebPAnimEncoder** const enc, + const WebPAnimEncoderOptions& anim_config, int* const width, + int* const height, int timestamp_ms, const uint8_t data[], + size_t size, uint32_t* const bit_pos) { + if (enc == nullptr || width == nullptr || height == nullptr) { + fprintf(stderr, "NULL parameters.\n"); + if (enc != nullptr) WebPAnimEncoderDelete(*enc); + abort(); + } + + // Init the source picture. + WebPPicture pic; + if (!WebPPictureInit(&pic)) { + fprintf(stderr, "WebPPictureInit failed.\n"); + WebPAnimEncoderDelete(*enc); + abort(); + } + pic.use_argb = Extract(1, data, size, bit_pos); + + // Read the source picture. + if (!ExtractSourcePicture(&pic, data, size, bit_pos)) { + const WebPEncodingError error_code = pic.error_code; + WebPPictureFree(&pic); + if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; + fprintf(stderr, "Can't read input image. Error code: %d\n", error_code); + abort(); + } + + // Crop and scale. + if (*enc == nullptr) { // First frame will set canvas width and height. + if (!ExtractAndCropOrScale(&pic, data, size, bit_pos)) { + const WebPEncodingError error_code = pic.error_code; + WebPPictureFree(&pic); + if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; + fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n", + error_code); + abort(); + } + } else { // Other frames will be resized to the first frame's dimensions. + if (!WebPPictureRescale(&pic, *width, *height)) { + const WebPEncodingError error_code = pic.error_code; + WebPAnimEncoderDelete(*enc); + WebPPictureFree(&pic); + if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; + fprintf(stderr, + "WebPPictureRescale failed. Size: %d,%d. Error code: %d\n", + *width, *height, error_code); + abort(); + } + } + + // Create encoder if it doesn't exist. + if (*enc == nullptr) { + *width = pic.width; + *height = pic.height; + *enc = WebPAnimEncoderNew(*width, *height, &anim_config); + if (*enc == nullptr) { + WebPPictureFree(&pic); + return 0; + } + } + + // Create frame encoding config. + WebPConfig config; + if (!ExtractWebPConfig(&config, data, size, bit_pos)) { + fprintf(stderr, "ExtractWebPConfig failed.\n"); + WebPAnimEncoderDelete(*enc); + WebPPictureFree(&pic); + abort(); + } + // Skip slow settings on big images, it's likely to timeout. + if (pic.width * pic.height > 32 * 32) { + config.method = (config.method > 4) ? 4 : config.method; + config.quality = (config.quality > 99.0f) ? 99.0f : config.quality; + config.alpha_quality = + (config.alpha_quality > 99) ? 99 : config.alpha_quality; + } + + // Encode. + if (!WebPAnimEncoderAdd(*enc, &pic, timestamp_ms, &config)) { + const WebPEncodingError error_code = pic.error_code; + WebPAnimEncoderDelete(*enc); + WebPPictureFree(&pic); + if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; + fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code); + abort(); + } + + WebPPictureFree(&pic); + return 1; +} + +} // namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { + WebPAnimEncoder* enc = nullptr; + int width = 0, height = 0, timestamp_ms = 0; + uint32_t bit_pos = 0; + + ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos); + + // Extract a configuration from the packed bits. + WebPAnimEncoderOptions anim_config; + if (!WebPAnimEncoderOptionsInit(&anim_config)) { + fprintf(stderr, "WebPAnimEncoderOptionsInit failed.\n"); + abort(); + } + anim_config.minimize_size = Extract(1, data, size, &bit_pos); + anim_config.kmax = Extract(15, data, size, &bit_pos); + const int min_kmin = (anim_config.kmax > 1) ? (anim_config.kmax / 2) : 0; + const int max_kmin = (anim_config.kmax > 1) ? (anim_config.kmax - 1) : 0; + anim_config.kmin = + min_kmin + Extract((uint32_t)(max_kmin - min_kmin), data, size, &bit_pos); + anim_config.allow_mixed = Extract(1, data, size, &bit_pos); + anim_config.verbose = 0; + + const int nb_frames = 1 + Extract(15, data, size, &bit_pos); + + // For each frame. + for (int i = 0; i < nb_frames; ++i) { + if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms, data, size, + &bit_pos)) { + return 0; + } + + timestamp_ms += (1 << (2 + Extract(15, data, size, &bit_pos))) + + Extract(1, data, size, &bit_pos); // [1..131073], arbitrary + } + + // Assemble. + if (!WebPAnimEncoderAdd(enc, nullptr, timestamp_ms, nullptr)) { + fprintf(stderr, "Last WebPAnimEncoderAdd failed: %s.\n", + WebPAnimEncoderGetError(enc)); + WebPAnimEncoderDelete(enc); + abort(); + } + WebPData webp_data; + WebPDataInit(&webp_data); + if (!WebPAnimEncoderAssemble(enc, &webp_data)) { + fprintf(stderr, "WebPAnimEncoderAssemble failed: %s.\n", + WebPAnimEncoderGetError(enc)); + WebPAnimEncoderDelete(enc); + WebPDataClear(&webp_data); + abort(); + } + + WebPAnimEncoderDelete(enc); + WebPDataClear(&webp_data); + return 0; +} diff --git a/internal/libwebp-1.2.1/tests/fuzzer/enc_dec_fuzzer.cc b/internal/libwebp-1.2.1/tests/fuzzer/enc_dec_fuzzer.cc new file mode 100644 index 0000000..187b516 --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/enc_dec_fuzzer.cc @@ -0,0 +1,141 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "./fuzz_utils.h" +#include "src/webp/decode.h" +#include "src/webp/encode.h" + +namespace { + +const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo; + +} // namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { + uint32_t bit_pos = 0; + + ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos); + + // Init the source picture. + WebPPicture pic; + if (!WebPPictureInit(&pic)) { + fprintf(stderr, "WebPPictureInit failed.\n"); + abort(); + } + pic.use_argb = Extract(1, data, size, &bit_pos); + + // Read the source picture. + if (!ExtractSourcePicture(&pic, data, size, &bit_pos)) { + const WebPEncodingError error_code = pic.error_code; + WebPPictureFree(&pic); + if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; + fprintf(stderr, "Can't read input image. Error code: %d\n", error_code); + abort(); + } + + // Crop and scale. + if (!ExtractAndCropOrScale(&pic, data, size, &bit_pos)) { + const WebPEncodingError error_code = pic.error_code; + WebPPictureFree(&pic); + if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; + fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n", + error_code); + abort(); + } + + // Extract a configuration from the packed bits. + WebPConfig config; + if (!ExtractWebPConfig(&config, data, size, &bit_pos)) { + fprintf(stderr, "ExtractWebPConfig failed.\n"); + abort(); + } + // Skip slow settings on big images, it's likely to timeout. + if (pic.width * pic.height > 32 * 32) { + if (config.lossless) { + if (config.quality > 99.0f && config.method >= 5) { + config.quality = 99.0f; + config.method = 5; + } + } else { + if (config.quality > 99.0f && config.method == 6) { + config.quality = 99.0f; + } + } + if (config.alpha_quality == 100 && config.method == 6) { + config.alpha_quality = 99; + } + } + + // Encode. + WebPMemoryWriter memory_writer; + WebPMemoryWriterInit(&memory_writer); + pic.writer = WebPMemoryWrite; + pic.custom_ptr = &memory_writer; + if (!WebPEncode(&config, &pic)) { + const WebPEncodingError error_code = pic.error_code; + WebPMemoryWriterClear(&memory_writer); + WebPPictureFree(&pic); + if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0; + fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code); + abort(); + } + + // Try decoding the result. + int w, h; + const uint8_t* const out_data = memory_writer.mem; + const size_t out_size = memory_writer.size; + uint8_t* const rgba = WebPDecodeBGRA(out_data, out_size, &w, &h); + if (rgba == nullptr || w != pic.width || h != pic.height) { + fprintf(stderr, "WebPDecodeBGRA failed.\n"); + WebPFree(rgba); + WebPMemoryWriterClear(&memory_writer); + WebPPictureFree(&pic); + abort(); + } + + // Compare the results if exact encoding. + if (pic.use_argb && config.lossless && config.near_lossless == 100) { + const uint32_t* src1 = (const uint32_t*)rgba; + const uint32_t* src2 = pic.argb; + for (int y = 0; y < h; ++y, src1 += w, src2 += pic.argb_stride) { + for (int x = 0; x < w; ++x) { + uint32_t v1 = src1[x], v2 = src2[x]; + if (!config.exact) { + if ((v1 & 0xff000000u) == 0 || (v2 & 0xff000000u) == 0) { + // Only keep alpha for comparison of fully transparent area. + v1 &= 0xff000000u; + v2 &= 0xff000000u; + } + } + if (v1 != v2) { + fprintf(stderr, "Lossless compression failed pixel-exactness.\n"); + WebPFree(rgba); + WebPMemoryWriterClear(&memory_writer); + WebPPictureFree(&pic); + abort(); + } + } + } + } + + WebPFree(rgba); + WebPMemoryWriterClear(&memory_writer); + WebPPictureFree(&pic); + return 0; +} diff --git a/internal/libwebp-1.2.1/tests/fuzzer/fuzz.dict b/internal/libwebp-1.2.1/tests/fuzzer/fuzz.dict new file mode 100644 index 0000000..627c72e --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/fuzz.dict @@ -0,0 +1,17 @@ +# https://developers.google.com/speed/webp/docs/riff_container + +# FourCC +"ALPH" +"ANIM" +"ANMF" +"EXIF" +"ICCP" +"RIFF" +"VP8 " +"VP8L" +"VP8X" +"WEBP" +"XMP " + +# VP8 signature +"\x9D\x01\x2A" diff --git a/internal/libwebp-1.2.1/tests/fuzzer/fuzz_utils.h b/internal/libwebp-1.2.1/tests/fuzzer/fuzz_utils.h new file mode 100644 index 0000000..713a5f4 --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/fuzz_utils.h @@ -0,0 +1,207 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WEBP_TESTS_FUZZER_FUZZ_UTILS_H_ +#define WEBP_TESTS_FUZZER_FUZZ_UTILS_H_ + +#include +#include + +#include "./img_alpha.h" +#include "./img_grid.h" +#include "./img_peak.h" +#include "src/dsp/dsp.h" +#include "src/webp/encode.h" + +//------------------------------------------------------------------------------ +// Arbitrary limits to prevent OOM, timeout, or slow execution. +// +// The decoded image size, and for animations additionally the canvas size. +static const size_t kFuzzPxLimit = 1024 * 1024; +// Demuxed or decoded animation frames. +static const int kFuzzFrameLimit = 3; + +// Reads and sums (up to) 128 spread-out bytes. +static WEBP_INLINE uint8_t FuzzHash(const uint8_t* const data, size_t size) { + uint8_t value = 0; + size_t incr = size / 128; + if (!incr) incr = 1; + for (size_t i = 0; i < size; i += incr) value += data[i]; + return value; +} + +//------------------------------------------------------------------------------ +// Extract an integer in [0, max_value]. + +static WEBP_INLINE uint32_t Extract(uint32_t max_value, + const uint8_t data[], size_t size, + uint32_t* const bit_pos) { + uint32_t v = 0; + uint32_t range = 1; + while (*bit_pos < 8 * size && range <= max_value) { + const uint8_t mask = 1u << (*bit_pos & 7); + v = (v << 1) | !!(data[*bit_pos >> 3] & mask); + range <<= 1; + ++*bit_pos; + } + return v % (max_value + 1); +} + +//------------------------------------------------------------------------------ +// Some functions to override VP8GetCPUInfo and disable some optimizations. + +static VP8CPUInfo GetCPUInfo; + +static WEBP_INLINE int GetCPUInfoNoSSE41(CPUFeature feature) { + if (feature == kSSE4_1 || feature == kAVX) return 0; + return GetCPUInfo(feature); +} + +static WEBP_INLINE int GetCPUInfoNoAVX(CPUFeature feature) { + if (feature == kAVX) return 0; + return GetCPUInfo(feature); +} + +static WEBP_INLINE int GetCPUInfoForceSlowSSSE3(CPUFeature feature) { + if (feature == kSlowSSSE3 && GetCPUInfo(kSSE3)) { + return 1; // we have SSE3 -> force SlowSSSE3 + } + return GetCPUInfo(feature); +} + +static WEBP_INLINE int GetCPUInfoOnlyC(CPUFeature feature) { + (void)feature; + return 0; +} + +static WEBP_INLINE void ExtractAndDisableOptimizations( + VP8CPUInfo default_VP8GetCPUInfo, const uint8_t data[], size_t size, + uint32_t* const bit_pos) { + GetCPUInfo = default_VP8GetCPUInfo; + const VP8CPUInfo kVP8CPUInfos[5] = {GetCPUInfoOnlyC, GetCPUInfoForceSlowSSSE3, + GetCPUInfoNoSSE41, GetCPUInfoNoAVX, + GetCPUInfo}; + int VP8GetCPUInfo_index = Extract(4, data, size, bit_pos); + VP8GetCPUInfo = kVP8CPUInfos[VP8GetCPUInfo_index]; +} + +//------------------------------------------------------------------------------ + +static WEBP_INLINE int ExtractWebPConfig(WebPConfig* const config, + const uint8_t data[], size_t size, + uint32_t* const bit_pos) { + if (config == NULL || !WebPConfigInit(config)) return 0; + config->lossless = Extract(1, data, size, bit_pos); + config->quality = Extract(100, data, size, bit_pos); + config->method = Extract(6, data, size, bit_pos); + config->image_hint = + (WebPImageHint)Extract(WEBP_HINT_LAST - 1, data, size, bit_pos); + config->segments = 1 + Extract(3, data, size, bit_pos); + config->sns_strength = Extract(100, data, size, bit_pos); + config->filter_strength = Extract(100, data, size, bit_pos); + config->filter_sharpness = Extract(7, data, size, bit_pos); + config->filter_type = Extract(1, data, size, bit_pos); + config->autofilter = Extract(1, data, size, bit_pos); + config->alpha_compression = Extract(1, data, size, bit_pos); + config->alpha_filtering = Extract(2, data, size, bit_pos); + config->alpha_quality = Extract(100, data, size, bit_pos); + config->pass = 1 + Extract(9, data, size, bit_pos); + config->show_compressed = 1; + config->preprocessing = Extract(2, data, size, bit_pos); + config->partitions = Extract(3, data, size, bit_pos); + config->partition_limit = 10 * Extract(10, data, size, bit_pos); + config->emulate_jpeg_size = Extract(1, data, size, bit_pos); + config->thread_level = Extract(1, data, size, bit_pos); + config->low_memory = Extract(1, data, size, bit_pos); + config->near_lossless = 20 * Extract(5, data, size, bit_pos); + config->exact = Extract(1, data, size, bit_pos); + config->use_delta_palette = Extract(1, data, size, bit_pos); + config->use_sharp_yuv = Extract(1, data, size, bit_pos); + return WebPValidateConfig(config); +} + +//------------------------------------------------------------------------------ + +static WEBP_INLINE int ExtractSourcePicture(WebPPicture* const pic, + const uint8_t data[], size_t size, + uint32_t* const bit_pos) { + if (pic == NULL) return 0; + + // Pick a source picture. + const uint8_t* kImagesData[] = { + kImgAlphaData, + kImgGridData, + kImgPeakData + }; + const int kImagesWidth[] = { + kImgAlphaWidth, + kImgGridWidth, + kImgPeakWidth + }; + const int kImagesHeight[] = { + kImgAlphaHeight, + kImgGridHeight, + kImgPeakHeight + }; + const size_t kNbImages = sizeof(kImagesData) / sizeof(kImagesData[0]); + const size_t image_index = Extract(kNbImages - 1, data, size, bit_pos); + const uint8_t* const image_data = kImagesData[image_index]; + pic->width = kImagesWidth[image_index]; + pic->height = kImagesHeight[image_index]; + pic->argb_stride = pic->width * 4 * sizeof(uint8_t); + + // Read the bytes. + return WebPPictureImportRGBA(pic, image_data, pic->argb_stride); +} + +//------------------------------------------------------------------------------ + +static WEBP_INLINE int Max(int a, int b) { return ((a < b) ? b : a); } + +static WEBP_INLINE int ExtractAndCropOrScale(WebPPicture* const pic, + const uint8_t data[], size_t size, + uint32_t* const bit_pos) { + if (pic == NULL) return 0; +#if !defined(WEBP_REDUCE_SIZE) + const int alter_input = Extract(1, data, size, bit_pos); + const int crop_or_scale = Extract(1, data, size, bit_pos); + const int width_ratio = 1 + Extract(7, data, size, bit_pos); + const int height_ratio = 1 + Extract(7, data, size, bit_pos); + if (alter_input) { + if (crop_or_scale) { + const uint32_t left_ratio = 1 + Extract(7, data, size, bit_pos); + const uint32_t top_ratio = 1 + Extract(7, data, size, bit_pos); + const int cropped_width = Max(1, pic->width / width_ratio); + const int cropped_height = Max(1, pic->height / height_ratio); + const int cropped_left = (pic->width - cropped_width) / left_ratio; + const int cropped_top = (pic->height - cropped_height) / top_ratio; + return WebPPictureCrop(pic, cropped_left, cropped_top, cropped_width, + cropped_height); + } else { + const int scaled_width = 1 + (pic->width * width_ratio) / 8; + const int scaled_height = 1 + (pic->height * height_ratio) / 8; + return WebPPictureRescale(pic, scaled_width, scaled_height); + } + } +#else // defined(WEBP_REDUCE_SIZE) + (void)data; + (void)size; + (void)bit_pos; +#endif // !defined(WEBP_REDUCE_SIZE) + return 1; +} + +#endif // WEBP_TESTS_FUZZER_FUZZ_UTILS_H_ diff --git a/internal/libwebp-1.2.1/tests/fuzzer/img_alpha.h b/internal/libwebp-1.2.1/tests/fuzzer/img_alpha.h new file mode 100644 index 0000000..fac63fe --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/img_alpha.h @@ -0,0 +1,381 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WEBP_TESTS_FUZZER_IMG_ALPHA_H_ +#define WEBP_TESTS_FUZZER_IMG_ALPHA_H_ + +#include + +static const int kImgAlphaWidth = 32; +static const int kImgAlphaHeight = 32; + +/*Pixel format: Red: 8 bit, Green: 8 bit, Blue: 8 bit, Fix 0xFF: 8 bit*/ +static const uint8_t kImgAlphaData[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xfe, 0xfd, 0xfc, 0xff, + 0xfe, 0xfd, 0xfb, 0xff, 0xfe, 0xfc, 0xfb, 0xff, 0xfe, 0xfc, 0xfa, 0xff, + 0xfe, 0xfc, 0xf9, 0xff, 0xfe, 0xfb, 0xf8, 0xff, 0xfe, 0xfb, 0xf7, 0xff, + 0xfe, 0xfb, 0xf7, 0xff, 0xfe, 0xfa, 0xf6, 0xff, 0xfe, 0xf9, 0xf5, 0xff, + 0xfd, 0xf9, 0xf3, 0xff, 0xfd, 0xf8, 0xf2, 0xff, 0xfd, 0xf7, 0xf1, 0xff, + 0xfc, 0xf7, 0xf0, 0xff, 0xfc, 0xf6, 0xef, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xfe, 0xfe, 0xfd, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0xfe, 0xfd, 0xfb, 0xff, + 0xfe, 0xfc, 0xfa, 0xff, 0xfe, 0xfc, 0xfa, 0xff, 0xfe, 0xfb, 0xf9, 0xff, + 0xfe, 0xfb, 0xf8, 0xff, 0xfe, 0xfb, 0xf7, 0xff, 0xfe, 0xfa, 0xf7, 0xff, + 0xfe, 0xfa, 0xf5, 0xff, 0xfe, 0xfa, 0xf5, 0xff, 0xfd, 0xf9, 0xf4, 0xff, + 0xfd, 0xf8, 0xf2, 0xff, 0xfc, 0xf7, 0xf1, 0xff, 0xfc, 0xf7, 0xf0, 0xff, + 0xfc, 0xf7, 0xef, 0xff, 0xfc, 0xf6, 0xee, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfd, 0xff, 0xfe, 0xfd, 0xfd, 0xff, + 0xfe, 0xfd, 0xfc, 0xff, 0xfe, 0xfd, 0xfb, 0xff, 0xfe, 0xfc, 0xfa, 0xff, + 0xfe, 0xfc, 0xfa, 0xff, 0xfe, 0xfc, 0xf9, 0xff, 0xfe, 0xfb, 0xf8, 0xff, + 0xfe, 0xfb, 0xf7, 0xff, 0xfe, 0xfb, 0xf6, 0xff, 0xfe, 0xfa, 0xf5, 0xff, + 0xfe, 0xfa, 0xf5, 0xff, 0xfe, 0xf9, 0xf4, 0xff, 0xfe, 0xf8, 0xf3, 0xff, + 0xfd, 0xf8, 0xf2, 0xff, 0xfc, 0xf7, 0xf1, 0xff, 0xfc, 0xf5, 0xf0, 0xff, + 0xfc, 0xf6, 0xee, 0xff, 0xfc, 0xf5, 0xed, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xfc, 0xfb, 0xfb, 0xff, 0xfd, 0xfc, 0xfc, 0xff, 0xfe, 0xfd, 0xfb, 0xff, + 0xfe, 0xfc, 0xfa, 0xff, 0xfe, 0xfc, 0xfa, 0xff, 0xfe, 0xfc, 0xf9, 0xff, + 0xfe, 0xfb, 0xf8, 0xff, 0xfe, 0xfb, 0xf8, 0xff, 0xfe, 0xfb, 0xf7, 0xff, + 0xfe, 0xfb, 0xf6, 0xff, 0xfe, 0xfa, 0xf5, 0xff, 0xfe, 0xfa, 0xf5, 0xff, + 0xfe, 0xf9, 0xf4, 0xff, 0xfe, 0xf9, 0xf3, 0xff, 0xfe, 0xf8, 0xf2, 0xff, + 0xfd, 0xf7, 0xf1, 0xff, 0xfc, 0xf6, 0xf0, 0xff, 0xfc, 0xf6, 0xee, 0xff, + 0xfc, 0xf5, 0xec, 0xff, 0xfb, 0xf5, 0xec, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xff, 0xfb, 0xfb, 0xfb, 0xff, + 0xfa, 0xf9, 0xf9, 0xff, 0xf7, 0xf6, 0xf6, 0xff, 0xfd, 0xfc, 0xfa, 0xff, + 0xfe, 0xfc, 0xfa, 0xff, 0xfe, 0xfc, 0xf9, 0xff, 0xfe, 0xfb, 0xf9, 0xff, + 0xfe, 0xfb, 0xf7, 0xff, 0xfe, 0xfb, 0xf7, 0xff, 0xfe, 0xfb, 0xf6, 0xff, + 0xfe, 0xfa, 0xf5, 0xff, 0xfe, 0xfa, 0xf5, 0xff, 0xfe, 0xfa, 0xf3, 0xff, + 0xfe, 0xfa, 0xf4, 0xfb, 0xfe, 0xf9, 0xf4, 0xeb, 0xfe, 0xfb, 0xf6, 0xdc, + 0xfd, 0xfa, 0xf6, 0xcf, 0xfd, 0xfa, 0xf5, 0xcc, 0xfc, 0xf9, 0xf4, 0xcb, + 0xfd, 0xf8, 0xf3, 0xc7, 0xfc, 0xf8, 0xf2, 0xc7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xff, + 0xfb, 0xfa, 0xfa, 0xff, 0xf8, 0xf7, 0xf7, 0xff, 0xf6, 0xf4, 0xf4, 0xff, + 0xf7, 0xf5, 0xf5, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf8, 0xf7, 0xf7, 0xff, + 0xfd, 0xfb, 0xf8, 0xff, 0xfe, 0xfb, 0xf8, 0xff, 0xfe, 0xfb, 0xf7, 0xff, + 0xfe, 0xfb, 0xf7, 0xff, 0xfe, 0xfa, 0xf6, 0xff, 0xfe, 0xfa, 0xf5, 0xff, + 0xfe, 0xfa, 0xf4, 0xff, 0xfd, 0xfa, 0xf4, 0xfc, 0xfe, 0xfc, 0xf9, 0xcf, + 0xff, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xff, 0xa8, 0xff, 0xff, 0xff, 0xa4, + 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0x94, + 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x8b, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfc, 0xff, 0xfa, 0xf9, 0xf9, 0xff, + 0xf6, 0xf5, 0xf4, 0xff, 0xf5, 0xf3, 0xf3, 0xff, 0xf4, 0xf2, 0xf1, 0xff, + 0xf2, 0xf1, 0xf0, 0xff, 0xf2, 0xf0, 0xf0, 0xff, 0xf0, 0xee, 0xee, 0xff, + 0xf1, 0xf0, 0xef, 0xff, 0xfe, 0xfb, 0xf8, 0xff, 0xfe, 0xfb, 0xf6, 0xff, + 0xfe, 0xfa, 0xf6, 0xff, 0xfe, 0xfa, 0xf5, 0xff, 0xfe, 0xfb, 0xf4, 0xff, + 0xfe, 0xfb, 0xf5, 0xf3, 0xff, 0xfe, 0xfe, 0xb4, 0xff, 0xff, 0xff, 0xac, + 0xff, 0xff, 0xff, 0xa7, 0xff, 0xff, 0xff, 0xa3, 0xff, 0xff, 0xff, 0x9c, + 0xff, 0xff, 0xff, 0x98, 0xff, 0xff, 0xff, 0x93, 0xff, 0xff, 0xff, 0x8f, + 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff, 0x84, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xfc, 0xfb, 0xfb, 0xff, 0xf9, 0xf8, 0xf8, 0xff, 0xf6, 0xf5, 0xf4, 0xff, + 0xf3, 0xf1, 0xf1, 0xff, 0xf0, 0xed, 0xed, 0xff, 0xef, 0xec, 0xeb, 0xff, + 0xee, 0xeb, 0xeb, 0xff, 0xf0, 0xee, 0xee, 0xff, 0xea, 0xe7, 0xe7, 0xff, + 0xec, 0xe9, 0xe9, 0xff, 0xf5, 0xf2, 0xf0, 0xff, 0xfe, 0xfb, 0xf6, 0xff, + 0xfe, 0xfb, 0xf5, 0xff, 0xfe, 0xfa, 0xf4, 0xff, 0xfe, 0xfb, 0xf6, 0xeb, + 0xff, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xff, 0xab, 0xff, 0xff, 0xff, 0xa4, + 0xff, 0xff, 0xff, 0xa0, 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0x97, + 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x87, + 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0x7c, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfc, 0xff, 0xfb, 0xfb, 0xfa, 0xff, + 0xf8, 0xf6, 0xf6, 0xff, 0xf4, 0xf3, 0xf2, 0xff, 0xf2, 0xf0, 0xf0, 0xff, + 0xef, 0xec, 0xeb, 0xff, 0xef, 0xec, 0xeb, 0xff, 0xeb, 0xe7, 0xe6, 0xff, + 0xea, 0xe6, 0xe5, 0xff, 0xeb, 0xe8, 0xe8, 0xff, 0xe9, 0xe6, 0xe7, 0xff, + 0xe5, 0xe2, 0xe2, 0xff, 0xed, 0xeb, 0xeb, 0xff, 0xf7, 0xf5, 0xf2, 0xff, + 0xfe, 0xfa, 0xf5, 0xff, 0xfe, 0xfb, 0xf5, 0xf8, 0xff, 0xff, 0xff, 0xac, + 0xff, 0xff, 0xff, 0xa8, 0xff, 0xff, 0xff, 0xa3, 0xff, 0xff, 0xff, 0x9f, + 0xff, 0xff, 0xff, 0x98, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x8f, + 0xff, 0xff, 0xff, 0x8b, 0xff, 0xff, 0xff, 0x84, 0xff, 0xff, 0xff, 0x80, + 0xff, 0xff, 0xff, 0x7c, 0xff, 0xff, 0xff, 0x77, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfc, 0xfc, 0xff, 0xf8, 0xf7, 0xf7, 0xff, 0xf6, 0xf5, 0xf4, 0xff, + 0xf3, 0xf1, 0xf0, 0xff, 0xf1, 0xef, 0xef, 0xff, 0xef, 0xed, 0xec, 0xff, + 0xec, 0xe8, 0xe7, 0xff, 0xe9, 0xe6, 0xe6, 0xff, 0xec, 0xe9, 0xe9, 0xff, + 0xe4, 0xe0, 0xdf, 0xff, 0xec, 0xe9, 0xe9, 0xff, 0xea, 0xe6, 0xe6, 0xff, + 0xe6, 0xe2, 0xe2, 0xff, 0xea, 0xe8, 0xe8, 0xff, 0xec, 0xe8, 0xe7, 0xff, + 0xfc, 0xf9, 0xf4, 0xff, 0xfe, 0xfd, 0xfa, 0xc3, 0xff, 0xff, 0xff, 0xa7, + 0xff, 0xff, 0xff, 0xa0, 0xff, 0xff, 0xff, 0x9c, 0xff, 0xff, 0xff, 0x97, + 0xff, 0xff, 0xff, 0x93, 0xff, 0xff, 0xff, 0x8c, 0xfe, 0xfc, 0xf9, 0xa3, + 0xfc, 0xfb, 0xf5, 0xb0, 0xfc, 0xf9, 0xf1, 0xbb, 0xfb, 0xf8, 0xf1, 0xb8, + 0xfb, 0xf8, 0xef, 0xb4, 0xfb, 0xf9, 0xf2, 0xa4, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xf8, 0xf7, 0xf7, 0xff, 0xf5, 0xf3, 0xf3, 0xff, 0xf1, 0xef, 0xef, 0xff, + 0xef, 0xec, 0xeb, 0xff, 0xeb, 0xe7, 0xe7, 0xff, 0xea, 0xe7, 0xe6, 0xff, + 0xe7, 0xe3, 0xe2, 0xff, 0xe4, 0xdf, 0xde, 0xff, 0xe6, 0xe2, 0xe2, 0xff, + 0xe4, 0xe1, 0xe0, 0xff, 0xe6, 0xe3, 0xe2, 0xff, 0xe0, 0xdc, 0xdc, 0xff, + 0xe3, 0xdf, 0xdf, 0xff, 0xe8, 0xe5, 0xe5, 0xff, 0xe4, 0xe1, 0xe2, 0xff, + 0xec, 0xe8, 0xe5, 0xf8, 0xff, 0xff, 0xff, 0xa4, 0xff, 0xff, 0xff, 0xa0, + 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0x97, 0xff, 0xff, 0xff, 0x90, + 0xfe, 0xfe, 0xfb, 0x9c, 0xfc, 0xf9, 0xef, 0xf7, 0xfc, 0xf8, 0xed, 0xff, + 0xfc, 0xf7, 0xeb, 0xff, 0xfb, 0xf7, 0xeb, 0xff, 0xfa, 0xf6, 0xe9, 0xff, + 0xfa, 0xf5, 0xe7, 0xff, 0xf8, 0xf4, 0xe5, 0xff, + 0xfd, 0xfd, 0xfd, 0xff, 0xfa, 0xfa, 0xfa, 0xff, 0xfc, 0xfc, 0xfc, 0xff, + 0xf8, 0xf7, 0xf7, 0xff, 0xf1, 0xef, 0xee, 0xff, 0xed, 0xea, 0xe9, 0xff, + 0xea, 0xe6, 0xe5, 0xff, 0xe7, 0xe3, 0xe2, 0xff, 0xe4, 0xe0, 0xdf, 0xff, + 0xe3, 0xde, 0xdd, 0xff, 0xe2, 0xde, 0xdd, 0xff, 0xe5, 0xe1, 0xe1, 0xff, + 0xde, 0xda, 0xda, 0xff, 0xe0, 0xdd, 0xdc, 0xff, 0xe1, 0xdd, 0xdd, 0xff, + 0xe3, 0xe0, 0xdf, 0xff, 0xe4, 0xe1, 0xe1, 0xff, 0xe3, 0xe0, 0xe0, 0xff, + 0xec, 0xea, 0xea, 0xe8, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x98, + 0xff, 0xff, 0xff, 0x93, 0xff, 0xff, 0xff, 0x8f, 0xfe, 0xfe, 0xfe, 0x8c, + 0xfd, 0xfb, 0xef, 0xff, 0xfd, 0xfb, 0xed, 0xff, 0xfd, 0xf8, 0xec, 0xff, + 0xfc, 0xf8, 0xeb, 0xff, 0xfc, 0xf7, 0xea, 0xff, 0xfb, 0xf6, 0xe9, 0xff, + 0xfa, 0xf6, 0xe7, 0xff, 0xfa, 0xf5, 0xe5, 0xff, + 0xfa, 0xfa, 0xfa, 0xff, 0xf8, 0xf7, 0xf8, 0xff, 0xf6, 0xf6, 0xf6, 0xff, + 0xf1, 0xf0, 0xf0, 0xff, 0xed, 0xec, 0xec, 0xff, 0xe9, 0xe6, 0xe5, 0xff, + 0xe6, 0xe1, 0xe0, 0xff, 0xe2, 0xde, 0xdc, 0xff, 0xe0, 0xda, 0xd8, 0xff, + 0xdd, 0xd7, 0xd5, 0xff, 0xde, 0xda, 0xd9, 0xff, 0xde, 0xda, 0xda, 0xff, + 0xdc, 0xd7, 0xd8, 0xff, 0xdf, 0xdc, 0xdc, 0xff, 0xe5, 0xe3, 0xe4, 0xff, + 0xe1, 0xde, 0xde, 0xff, 0xde, 0xdc, 0xdc, 0xff, 0xe9, 0xe7, 0xe8, 0xff, + 0xe8, 0xe7, 0xe6, 0xe7, 0xff, 0xff, 0xff, 0x97, 0xff, 0xff, 0xff, 0x93, + 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x88, 0xfe, 0xfe, 0xfa, 0x9b, + 0xfc, 0xfb, 0xee, 0xff, 0xfc, 0xfa, 0xed, 0xff, 0xfb, 0xfa, 0xeb, 0xff, + 0xfb, 0xf8, 0xeb, 0xff, 0xfb, 0xf7, 0xea, 0xff, 0xfa, 0xf6, 0xe8, 0xff, + 0xf8, 0xf6, 0xe6, 0xff, 0xf7, 0xf5, 0xe3, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xf7, 0xf6, 0xf7, 0xff, 0xf5, 0xf4, 0xf4, 0xff, + 0xf0, 0xee, 0xef, 0xff, 0xe8, 0xe7, 0xe7, 0xff, 0xe7, 0xe4, 0xe3, 0xff, + 0xe2, 0xdd, 0xdb, 0xff, 0xdf, 0xd9, 0xd7, 0xff, 0xdc, 0xd6, 0xd4, 0xff, + 0xd8, 0xd2, 0xcf, 0xff, 0xd8, 0xd2, 0xd1, 0xff, 0xe0, 0xdc, 0xdc, 0xff, + 0xdb, 0xd7, 0xd7, 0xff, 0xdb, 0xd7, 0xd7, 0xff, 0xe7, 0xe5, 0xe5, 0xff, + 0xde, 0xda, 0xdb, 0xff, 0xdb, 0xd8, 0xd9, 0xff, 0xe0, 0xde, 0xde, 0xff, + 0xed, 0xeb, 0xec, 0xe4, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0x8b, + 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0x80, 0xfe, 0xfe, 0xfa, 0x94, + 0xfd, 0xfb, 0xee, 0xff, 0xfd, 0xfb, 0xed, 0xff, 0xfc, 0xfb, 0xec, 0xff, + 0xfc, 0xfa, 0xea, 0xff, 0xf8, 0xf7, 0xe9, 0xff, 0xf8, 0xf6, 0xe7, 0xff, + 0xf7, 0xf7, 0xe5, 0xff, 0xf7, 0xf5, 0xe3, 0xff, + 0xf9, 0xf9, 0xf9, 0xff, 0xf5, 0xf4, 0xf4, 0xff, 0xf2, 0xf2, 0xf2, 0xff, + 0xe9, 0xe6, 0xe6, 0xff, 0xe9, 0xe7, 0xe6, 0xff, 0xe9, 0xe7, 0xe7, 0xff, + 0xe0, 0xdb, 0xda, 0xff, 0xda, 0xd3, 0xd2, 0xff, 0xd6, 0xd0, 0xce, 0xff, + 0xd4, 0xcd, 0xcb, 0xff, 0xd0, 0xc8, 0xc7, 0xff, 0xd7, 0xd2, 0xd1, 0xff, + 0xd8, 0xd3, 0xd3, 0xff, 0xd4, 0xd1, 0xd1, 0xff, 0xe6, 0xe3, 0xe2, 0xff, + 0xdd, 0xda, 0xda, 0xff, 0xe3, 0xe1, 0xe1, 0xff, 0xe0, 0xde, 0xde, 0xff, + 0xe7, 0xe5, 0xe5, 0xe3, 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff, 0x84, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7b, 0xfe, 0xfe, 0xfb, 0x88, + 0xfc, 0xfc, 0xef, 0xff, 0xfb, 0xfc, 0xee, 0xff, 0xfb, 0xfa, 0xeb, 0xff, + 0xfa, 0xfa, 0xea, 0xff, 0xf9, 0xf9, 0xe7, 0xff, 0xf7, 0xf7, 0xe6, 0xff, + 0xf6, 0xf5, 0xe5, 0xff, 0xf7, 0xf5, 0xe3, 0xff, + 0xf8, 0xf7, 0xf7, 0xff, 0xf4, 0xf4, 0xf4, 0xff, 0xf2, 0xf2, 0xf1, 0xff, + 0xe8, 0xe6, 0xe6, 0xff, 0xe4, 0xe1, 0xe1, 0xff, 0xdd, 0xda, 0xda, 0xff, + 0xe7, 0xe3, 0xe3, 0xff, 0xd6, 0xcf, 0xcd, 0xff, 0xd4, 0xcc, 0xca, 0xff, + 0xcf, 0xc7, 0xc5, 0xff, 0xcc, 0xc3, 0xc1, 0xff, 0xd3, 0xce, 0xcc, 0xff, + 0xd9, 0xd4, 0xd4, 0xff, 0xda, 0xd6, 0xd6, 0xff, 0xe3, 0xe1, 0xe0, 0xff, + 0xdb, 0xd8, 0xd7, 0xff, 0xe4, 0xe2, 0xe2, 0xff, 0xe1, 0xde, 0xdf, 0xff, + 0xe3, 0xe1, 0xe1, 0xf0, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0x7c, + 0xff, 0xff, 0xff, 0x78, 0xff, 0xff, 0xff, 0x73, 0xff, 0xff, 0xff, 0x6f, + 0xfb, 0xfc, 0xed, 0xff, 0xfa, 0xfb, 0xed, 0xff, 0xfb, 0xfa, 0xeb, 0xff, + 0xfa, 0xf9, 0xe9, 0xff, 0xfa, 0xf9, 0xe7, 0xff, 0xf8, 0xf9, 0xe7, 0xff, + 0xf7, 0xf5, 0xe3, 0xff, 0xf7, 0xf4, 0xe1, 0xff, + 0xf2, 0xf2, 0xf2, 0xff, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, 0xef, 0xff, + 0xe5, 0xe3, 0xe3, 0xff, 0xdd, 0xd9, 0xd9, 0xff, 0xdf, 0xdc, 0xdc, 0xff, + 0xe6, 0xe3, 0xe3, 0xff, 0xe1, 0xda, 0xdb, 0xff, 0xd2, 0xca, 0xc7, 0xff, + 0xca, 0xc2, 0xc0, 0xff, 0xc7, 0xbe, 0xbc, 0xff, 0xc8, 0xc2, 0xc0, 0xff, + 0xd2, 0xcf, 0xce, 0xff, 0xd5, 0xcf, 0xd0, 0xff, 0xd5, 0xd1, 0xd1, 0xff, + 0xda, 0xd6, 0xd7, 0xff, 0xde, 0xdc, 0xdd, 0xff, 0xde, 0xdd, 0xdc, 0xff, + 0xe2, 0xe0, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x7c, 0xff, 0xff, 0xff, 0x77, + 0xff, 0xff, 0xff, 0x70, 0xff, 0xff, 0xff, 0x6c, 0xff, 0xff, 0xff, 0x67, + 0xfa, 0xfd, 0xed, 0xf8, 0xf9, 0xfc, 0xeb, 0xff, 0xf9, 0xfb, 0xea, 0xff, + 0xf8, 0xf9, 0xe8, 0xff, 0xf7, 0xf9, 0xe6, 0xff, 0xf7, 0xf8, 0xe6, 0xff, + 0xf6, 0xf5, 0xe4, 0xff, 0xf6, 0xf4, 0xe1, 0xff, + 0xe7, 0xe7, 0xe7, 0xff, 0xe3, 0xe4, 0xe3, 0xff, 0xe1, 0xe2, 0xe2, 0xff, + 0xe4, 0xe4, 0xe4, 0xff, 0xd7, 0xd6, 0xd5, 0xff, 0xd5, 0xd4, 0xd4, 0xff, + 0xdd, 0xdc, 0xdc, 0xff, 0xdc, 0xda, 0xda, 0xff, 0xd0, 0xc9, 0xc9, 0xff, + 0xc9, 0xbf, 0xbe, 0xff, 0xc2, 0xba, 0xb8, 0xff, 0xc0, 0xb8, 0xb6, 0xff, + 0xcc, 0xc7, 0xc7, 0xff, 0xd2, 0xcd, 0xcd, 0xff, 0xd2, 0xce, 0xcf, 0xff, + 0xd1, 0xcc, 0xcd, 0xff, 0xdf, 0xde, 0xde, 0xff, 0xe7, 0xe6, 0xe6, 0xff, + 0xe4, 0xe3, 0xe3, 0xff, 0xfd, 0xfd, 0xfd, 0x78, 0xff, 0xff, 0xff, 0x70, + 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x67, 0xff, 0xff, 0xff, 0x60, + 0xfc, 0xfe, 0xef, 0xcf, 0xf9, 0xfc, 0xec, 0xff, 0xfa, 0xfa, 0xe9, 0xff, + 0xf8, 0xfa, 0xe8, 0xff, 0xf7, 0xf7, 0xe5, 0xff, 0xf6, 0xf7, 0xe5, 0xff, + 0xf5, 0xf5, 0xe3, 0xff, 0xf6, 0xf4, 0xe0, 0xff, + 0xdc, 0xdd, 0xdd, 0xff, 0xdb, 0xdc, 0xdc, 0xff, 0xd3, 0xd4, 0xd4, 0xff, + 0xd8, 0xd8, 0xd8, 0xff, 0xd1, 0xd1, 0xd1, 0xff, 0xd0, 0xd0, 0xd0, 0xff, + 0xca, 0xcb, 0xcb, 0xff, 0xcc, 0xcc, 0xcd, 0xff, 0xcb, 0xcb, 0xcb, 0xff, + 0xc6, 0xc1, 0xc1, 0xff, 0xc0, 0xbc, 0xba, 0xff, 0xba, 0xb1, 0xaf, 0xff, + 0xc5, 0xbf, 0xc0, 0xff, 0xd6, 0xd2, 0xd2, 0xff, 0xc4, 0xbf, 0xbf, 0xff, + 0xd3, 0xce, 0xcd, 0xff, 0xd3, 0xd1, 0xd2, 0xff, 0xe3, 0xe2, 0xe2, 0xff, + 0xe6, 0xe4, 0xe4, 0xff, 0xee, 0xee, 0xed, 0x9b, 0xff, 0xff, 0xff, 0x68, + 0xff, 0xff, 0xff, 0x63, 0xff, 0xff, 0xff, 0x5f, 0xff, 0xff, 0xff, 0x5b, + 0xfd, 0xfe, 0xf6, 0x80, 0xf9, 0xfc, 0xeb, 0xff, 0xf7, 0xfa, 0xeb, 0xff, + 0xf7, 0xf9, 0xe9, 0xff, 0xf6, 0xf8, 0xe8, 0xff, 0xf6, 0xf7, 0xe5, 0xff, + 0xf5, 0xf4, 0xe4, 0xff, 0xf5, 0xf4, 0xe3, 0xff, + 0xd7, 0xd8, 0xd8, 0xff, 0xd4, 0xd5, 0xd4, 0xff, 0xd2, 0xd3, 0xd3, 0xff, + 0xcf, 0xd0, 0xd0, 0xff, 0xcc, 0xce, 0xcd, 0xff, 0xc9, 0xca, 0xca, 0xff, + 0xc5, 0xc6, 0xc6, 0xff, 0xc3, 0xc4, 0xc4, 0xff, 0xbf, 0xbf, 0xbd, 0xff, + 0xbe, 0xbf, 0xbf, 0xff, 0xba, 0xba, 0xba, 0xff, 0xb4, 0xb1, 0xb1, 0xff, + 0xbb, 0xba, 0xb9, 0xff, 0xc9, 0xc8, 0xc7, 0xff, 0xe2, 0xde, 0xde, 0xff, + 0xc1, 0xbd, 0xbd, 0xff, 0xd6, 0xd5, 0xd5, 0xff, 0xde, 0xdd, 0xdc, 0xff, + 0xdc, 0xda, 0xda, 0xff, 0xe6, 0xe5, 0xe5, 0xcf, 0xff, 0xff, 0xff, 0x63, + 0xff, 0xff, 0xff, 0x5c, 0xff, 0xff, 0xff, 0x58, 0xff, 0xff, 0xff, 0x53, + 0xff, 0xff, 0xff, 0x4f, 0xf9, 0xfc, 0xeb, 0xfb, 0xf6, 0xf9, 0xeb, 0xff, + 0xf6, 0xf9, 0xea, 0xff, 0xf6, 0xf9, 0xe8, 0xff, 0xf5, 0xf5, 0xe4, 0xff, + 0xf6, 0xf6, 0xe4, 0xff, 0xf5, 0xf4, 0xe2, 0xff, + 0xd3, 0xd4, 0xd4, 0xff, 0xcc, 0xcd, 0xcd, 0xff, 0xce, 0xcf, 0xcf, 0xff, + 0xc9, 0xcb, 0xcb, 0xff, 0xc6, 0xc7, 0xc7, 0xff, 0xc3, 0xc5, 0xc4, 0xff, + 0xc0, 0xc1, 0xc1, 0xff, 0xbb, 0xbd, 0xbc, 0xff, 0xb7, 0xb9, 0xb8, 0xff, + 0xb6, 0xb7, 0xb7, 0xff, 0xb4, 0xb7, 0xb6, 0xff, 0xaf, 0xaf, 0xaf, 0xff, + 0xb0, 0xb2, 0xb2, 0xff, 0xb5, 0xb5, 0xb5, 0xff, 0xc1, 0xc0, 0xc0, 0xff, + 0xc9, 0xc6, 0xc7, 0xff, 0xb3, 0xad, 0xae, 0xff, 0xcf, 0xcd, 0xce, 0xff, + 0xe3, 0xe2, 0xe2, 0xff, 0xe4, 0xe2, 0xe2, 0xff, 0xfd, 0xfd, 0xfd, 0x60, + 0xff, 0xff, 0xff, 0x57, 0xff, 0xff, 0xff, 0x50, 0xff, 0xff, 0xff, 0x4c, + 0xff, 0xff, 0xff, 0x47, 0xfa, 0xfb, 0xef, 0xa8, 0xf8, 0xfc, 0xed, 0xff, + 0xf7, 0xf9, 0xea, 0xff, 0xf6, 0xf7, 0xe7, 0xff, 0xf6, 0xf4, 0xe7, 0xff, + 0xf5, 0xf4, 0xe5, 0xff, 0xf4, 0xf3, 0xe3, 0xff, + 0xce, 0xcf, 0xcf, 0xff, 0xc8, 0xc9, 0xc9, 0xff, 0xc6, 0xc8, 0xc8, 0xff, + 0xc4, 0xc6, 0xc6, 0xff, 0xc2, 0xc3, 0xc3, 0xff, 0xba, 0xbd, 0xbc, 0xff, + 0xbc, 0xbe, 0xbe, 0xff, 0xb5, 0xb8, 0xb7, 0xff, 0xb4, 0xb6, 0xb5, 0xff, + 0xb0, 0xb2, 0xb2, 0xff, 0xaf, 0xb1, 0xb0, 0xff, 0xaa, 0xad, 0xac, 0xff, + 0xaa, 0xad, 0xac, 0xff, 0xa7, 0xa9, 0xa8, 0xff, 0xa4, 0xa5, 0xa5, 0xff, + 0xa7, 0xa8, 0xa8, 0xff, 0xc4, 0xc3, 0xc3, 0xff, 0xb8, 0xb7, 0xb7, 0xff, + 0xc0, 0xbf, 0xbe, 0xff, 0xd0, 0xce, 0xce, 0xff, 0xe3, 0xe2, 0xe2, 0x94, + 0xff, 0xff, 0xff, 0x4f, 0xff, 0xff, 0xff, 0x4b, 0xff, 0xff, 0xff, 0x44, + 0xff, 0xff, 0xff, 0x40, 0xed, 0xe7, 0xe6, 0x48, 0xba, 0xad, 0xa1, 0xff, + 0xd6, 0xcf, 0xc1, 0xff, 0xf4, 0xf3, 0xe4, 0xff, 0xf5, 0xf4, 0xe5, 0xff, + 0xf5, 0xf3, 0xe5, 0xff, 0xf5, 0xf5, 0xe5, 0xff, + 0xc7, 0xc9, 0xc8, 0xff, 0xc5, 0xc6, 0xc6, 0xff, 0xc1, 0xc2, 0xc2, 0xff, + 0xbd, 0xbe, 0xbe, 0xff, 0xba, 0xbc, 0xbb, 0xff, 0xb3, 0xb4, 0xb4, 0xff, + 0xb2, 0xb5, 0xb4, 0xff, 0xb1, 0xb4, 0xb3, 0xff, 0xac, 0xaf, 0xae, 0xff, + 0xa8, 0xaa, 0xa9, 0xff, 0xa8, 0xab, 0xaa, 0xff, 0xa1, 0xa4, 0xa3, 0xff, + 0xa1, 0xa4, 0xa3, 0xff, 0xa3, 0xa5, 0xa4, 0xff, 0x9f, 0xa1, 0xa0, 0xff, + 0x99, 0x9b, 0x9a, 0xff, 0x98, 0x9a, 0x9a, 0xff, 0x97, 0x9a, 0x99, 0xff, + 0xa0, 0xa0, 0xa0, 0xff, 0x9b, 0x9b, 0x9b, 0xff, 0x9f, 0xa0, 0x9f, 0xe8, + 0xff, 0xff, 0xff, 0x48, 0xff, 0xff, 0xff, 0x44, 0xff, 0xff, 0xff, 0x3f, + 0xff, 0xff, 0xff, 0x3b, 0xff, 0xff, 0xff, 0x34, 0xbb, 0xa7, 0xa5, 0xd8, + 0xb9, 0xa9, 0xa6, 0xff, 0xb9, 0xac, 0xa0, 0xff, 0xf8, 0xfa, 0xe7, 0xff, + 0xf5, 0xf4, 0xe6, 0xff, 0xf4, 0xf3, 0xe4, 0xff, + 0xc0, 0xc0, 0xbf, 0xff, 0xbb, 0xbb, 0xbb, 0xff, 0xbb, 0xbc, 0xbb, 0xff, + 0xb6, 0xb6, 0xb6, 0xff, 0xb0, 0xb1, 0xb1, 0xff, 0xad, 0xad, 0xad, 0xff, + 0xa8, 0xa7, 0xa7, 0xff, 0xa8, 0xa9, 0xa9, 0xff, 0xa5, 0xa6, 0xa6, 0xff, + 0xa4, 0xa6, 0xa5, 0xff, 0xa3, 0xa6, 0xa6, 0xff, 0x9b, 0x9d, 0x9c, 0xff, + 0x96, 0x99, 0x99, 0xff, 0x9b, 0x9d, 0x9d, 0xff, 0x97, 0x99, 0x98, 0xff, + 0x8e, 0x91, 0x91, 0xff, 0x98, 0x9b, 0x9b, 0xff, 0x89, 0x8c, 0x8b, 0xff, + 0x87, 0x8b, 0x89, 0xff, 0x84, 0x7f, 0x81, 0xff, 0x82, 0x82, 0x83, 0xff, + 0xc0, 0xbe, 0xbf, 0x67, 0xff, 0xff, 0xff, 0x3c, 0xff, 0xff, 0xff, 0x37, + 0xff, 0xff, 0xff, 0x33, 0xff, 0xff, 0xff, 0x2f, 0xc6, 0xbb, 0xb9, 0x50, + 0xac, 0x9c, 0x99, 0xff, 0xa8, 0x98, 0x91, 0xff, 0xbd, 0xb0, 0xa5, 0xff, + 0xf6, 0xf5, 0xe6, 0xff, 0xf4, 0xf3, 0xe4, 0xff, + 0xbb, 0xbb, 0xba, 0xff, 0xb3, 0xb3, 0xb3, 0xff, 0xb3, 0xb4, 0xb4, 0xff, + 0xae, 0xaf, 0xae, 0xff, 0xad, 0xae, 0xae, 0xff, 0xb2, 0xb4, 0xb4, 0xfc, + 0xa8, 0xa8, 0xa9, 0xf3, 0xa7, 0xa7, 0xa7, 0xf3, 0x9e, 0x9f, 0x9d, 0xfc, + 0x9b, 0x9c, 0x9a, 0xff, 0x9c, 0x9e, 0x9e, 0xff, 0x94, 0x96, 0x94, 0xff, + 0x90, 0x90, 0x90, 0xff, 0x96, 0x98, 0x99, 0xff, 0x8d, 0x8f, 0x8e, 0xff, + 0x83, 0x83, 0x84, 0xff, 0x8f, 0x92, 0x91, 0xff, 0x83, 0x85, 0x85, 0xff, + 0x87, 0x87, 0x87, 0xff, 0x7c, 0x7d, 0x7d, 0xff, 0x78, 0x74, 0x72, 0xff, + 0x95, 0x93, 0x92, 0x97, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0x30, + 0xff, 0xff, 0xff, 0x2c, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, 0x23, + 0xa2, 0x93, 0x94, 0xe7, 0x96, 0x83, 0x81, 0xff, 0xa8, 0x91, 0x8b, 0xff, + 0xc4, 0xbc, 0xb2, 0xff, 0xf5, 0xf5, 0xe4, 0xff, + 0xb7, 0xb9, 0xb8, 0xff, 0xaf, 0xaf, 0xae, 0xff, 0xaf, 0xaf, 0xaf, 0xff, + 0xac, 0xad, 0xae, 0xff, 0xa8, 0xaa, 0xa9, 0xff, 0xae, 0xaf, 0xaf, 0xec, + 0xc6, 0xc7, 0xc7, 0xb4, 0xc5, 0xc6, 0xc6, 0xb3, 0x9a, 0x99, 0x99, 0xeb, + 0x91, 0x91, 0x90, 0xff, 0x94, 0x95, 0x95, 0xff, 0x95, 0x96, 0x96, 0xff, + 0x8c, 0x8e, 0x8e, 0xff, 0x86, 0x88, 0x87, 0xff, 0x83, 0x82, 0x82, 0xff, + 0x80, 0x81, 0x81, 0xff, 0x82, 0x84, 0x84, 0xff, 0x7b, 0x79, 0x7a, 0xff, + 0x73, 0x74, 0x73, 0xff, 0x79, 0x7a, 0x7b, 0xff, 0x75, 0x73, 0x72, 0xff, + 0x6f, 0x70, 0x6e, 0xfc, 0xe8, 0xe7, 0xe7, 0x37, 0xff, 0xff, 0xff, 0x2b, + 0xff, 0xff, 0xff, 0x24, 0xff, 0xff, 0xff, 0x20, 0xff, 0xff, 0xff, 0x1c, + 0x70, 0x6e, 0x6d, 0xeb, 0x6c, 0x69, 0x66, 0xff, 0x83, 0x78, 0x7b, 0xff, + 0x83, 0x7f, 0x78, 0xff, 0xdc, 0xd5, 0xcd, 0xff, + 0xb3, 0xb5, 0xb4, 0xff, 0xa9, 0xa9, 0xaa, 0xff, 0xa7, 0xa8, 0xa7, 0xff, + 0xa7, 0xa8, 0xa7, 0xff, 0xa6, 0xa8, 0xa8, 0xff, 0xab, 0xac, 0xab, 0xe3, + 0xdb, 0xdc, 0xdc, 0x97, 0xd9, 0xda, 0xda, 0x93, 0x99, 0x99, 0x98, 0xe0, + 0x94, 0x95, 0x95, 0xff, 0x8e, 0x8f, 0x8e, 0xff, 0x8f, 0x93, 0x92, 0xff, + 0x88, 0x8a, 0x89, 0xff, 0x79, 0x78, 0x78, 0xff, 0x80, 0x82, 0x82, 0xff, + 0x7f, 0x81, 0x81, 0xff, 0x7f, 0x7d, 0x7e, 0xff, 0x76, 0x72, 0x72, 0xff, + 0x70, 0x70, 0x70, 0xff, 0x73, 0x73, 0x73, 0xff, 0x67, 0x66, 0x64, 0xff, + 0x6b, 0x6b, 0x6a, 0xff, 0x70, 0x73, 0x72, 0xd4, 0xff, 0xff, 0xff, 0x23, + 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x1b, 0x79, 0x74, 0x72, 0x67, + 0x63, 0x61, 0x61, 0xff, 0x63, 0x62, 0x60, 0xff, 0x5e, 0x5c, 0x5a, 0xff, + 0x61, 0x5f, 0x5c, 0xff, 0x6b, 0x63, 0x5e, 0xff, + 0xae, 0xaf, 0xaf, 0xff, 0xa9, 0xa9, 0xa9, 0xff, 0x9c, 0x9b, 0x99, 0xff, + 0x9d, 0x9c, 0x9b, 0xff, 0x9c, 0x9c, 0x9c, 0xff, 0x9c, 0x9c, 0x9d, 0xf7, + 0xaa, 0xac, 0xac, 0xd4, 0xa5, 0xa7, 0xa6, 0xd3, 0x98, 0x9a, 0x9a, 0xf4, + 0x8d, 0x8e, 0x8e, 0xff, 0x85, 0x85, 0x85, 0xff, 0x85, 0x86, 0x86, 0xff, + 0x82, 0x84, 0x84, 0xff, 0x7b, 0x7b, 0x7b, 0xff, 0x7e, 0x82, 0x81, 0xff, + 0x79, 0x7b, 0x7b, 0xff, 0x79, 0x7b, 0x7b, 0xff, 0x72, 0x73, 0x73, 0xff, + 0x70, 0x6e, 0x6e, 0xff, 0x6d, 0x6d, 0x6d, 0xff, 0x61, 0x5f, 0x5d, 0xff, + 0x5d, 0x5d, 0x5b, 0xff, 0x64, 0x60, 0x61, 0xff, 0x5e, 0x5d, 0x5d, 0xec, + 0x6a, 0x69, 0x67, 0xa0, 0x67, 0x66, 0x64, 0xbc, 0x59, 0x56, 0x55, 0xff, + 0x54, 0x51, 0x4e, 0xff, 0x56, 0x52, 0x51, 0xff, 0x55, 0x52, 0x4f, 0xff, + 0x52, 0x4e, 0x49, 0xff, 0x54, 0x54, 0x52, 0xff, + 0xa9, 0xac, 0xab, 0xff, 0xa1, 0xa0, 0xa0, 0xff, 0x9a, 0x99, 0x98, 0xff, + 0x97, 0x97, 0x96, 0xff, 0x95, 0x94, 0x94, 0xff, 0x8e, 0x8c, 0x8c, 0xff, + 0x95, 0x98, 0x97, 0xfc, 0x90, 0x91, 0x91, 0xfc, 0x95, 0x96, 0x96, 0xff, + 0x86, 0x86, 0x86, 0xff, 0x81, 0x82, 0x80, 0xff, 0x7d, 0x7e, 0x7b, 0xff, + 0x78, 0x79, 0x78, 0xff, 0x71, 0x70, 0x6e, 0xff, 0x72, 0x71, 0x70, 0xff, + 0x72, 0x73, 0x72, 0xff, 0x70, 0x70, 0x6e, 0xff, 0x6d, 0x6a, 0x6a, 0xff, + 0x64, 0x64, 0x63, 0xff, 0x69, 0x68, 0x67, 0xff, 0x61, 0x5f, 0x5e, 0xff, + 0x59, 0x57, 0x57, 0xff, 0x5f, 0x5f, 0x5e, 0xff, 0x55, 0x55, 0x53, 0xff, + 0x57, 0x58, 0x57, 0xff, 0x51, 0x53, 0x50, 0xff, 0x54, 0x51, 0x51, 0xff, + 0x4c, 0x4b, 0x4a, 0xff, 0x49, 0x4a, 0x47, 0xff, 0x4f, 0x51, 0x4e, 0xff, + 0x4f, 0x4d, 0x49, 0xff, 0x4c, 0x4b, 0x46, 0xff, + 0xa7, 0xaa, 0xaa, 0xff, 0x99, 0x98, 0x98, 0xff, 0x95, 0x92, 0x92, 0xff, + 0x94, 0x93, 0x92, 0xff, 0x8a, 0x87, 0x84, 0xff, 0x85, 0x80, 0x7f, 0xff, + 0x8d, 0x90, 0x8f, 0xff, 0x85, 0x85, 0x85, 0xff, 0x8e, 0x8e, 0x8e, 0xff, + 0x80, 0x80, 0x81, 0xff, 0x7c, 0x7d, 0x7b, 0xff, 0x79, 0x79, 0x78, 0xff, + 0x78, 0x79, 0x78, 0xff, 0x6e, 0x6c, 0x6b, 0xff, 0x68, 0x66, 0x66, 0xff, + 0x69, 0x6a, 0x67, 0xff, 0x60, 0x5e, 0x5d, 0xff, 0x5b, 0x58, 0x57, 0xff, + 0x58, 0x56, 0x55, 0xff, 0x61, 0x5e, 0x5d, 0xff, 0x54, 0x53, 0x52, 0xff, + 0x60, 0x5d, 0x5c, 0xff, 0x5b, 0x5b, 0x5a, 0xff, 0x59, 0x5a, 0x58, 0xff, + 0x48, 0x43, 0x42, 0xff, 0x44, 0x40, 0x40, 0xff, 0x48, 0x46, 0x44, 0xff, + 0x4a, 0x46, 0x46, 0xff, 0x4c, 0x4f, 0x4c, 0xff, 0x49, 0x4a, 0x48, 0xff, + 0x47, 0x46, 0x41, 0xff, 0x44, 0x43, 0x3f, 0xff, + 0xa2, 0xa4, 0xa4, 0xff, 0x96, 0x95, 0x96, 0xff, 0x8e, 0x8c, 0x8a, 0xff, + 0x89, 0x86, 0x86, 0xff, 0x82, 0x7c, 0x7c, 0xff, 0x83, 0x80, 0x7f, 0xff, + 0x82, 0x82, 0x80, 0xff, 0x82, 0x83, 0x81, 0xff, 0x82, 0x85, 0x85, 0xff, + 0x7c, 0x7c, 0x7c, 0xff, 0x77, 0x78, 0x75, 0xff, 0x75, 0x77, 0x76, 0xff, + 0x71, 0x72, 0x70, 0xff, 0x68, 0x66, 0x65, 0xff, 0x62, 0x60, 0x5f, 0xff, + 0x5f, 0x5e, 0x5c, 0xff, 0x5a, 0x52, 0x4f, 0xff, 0x5b, 0x53, 0x4f, 0xff, + 0x50, 0x49, 0x49, 0xff, 0x5c, 0x58, 0x57, 0xff, 0x50, 0x4b, 0x49, 0xff, + 0x57, 0x54, 0x53, 0xff, 0x4f, 0x4f, 0x4e, 0xff, 0x53, 0x57, 0x55, 0xff, + 0x48, 0x45, 0x44, 0xff, 0x3c, 0x33, 0x33, 0xff, 0x49, 0x45, 0x43, 0xff, + 0x41, 0x3f, 0x3d, 0xff, 0x49, 0x46, 0x44, 0xff, 0x4a, 0x48, 0x47, 0xff, + 0x3f, 0x3c, 0x3a, 0xff, 0x43, 0x41, 0x3e, 0xff, + 0x99, 0x9c, 0x9a, 0xff, 0x90, 0x8e, 0x8f, 0xff, 0x8c, 0x8b, 0x89, 0xff, + 0x81, 0x7c, 0x7c, 0xff, 0x7d, 0x78, 0x78, 0xff, 0x82, 0x82, 0x80, 0xff, + 0x77, 0x74, 0x73, 0xff, 0x75, 0x73, 0x73, 0xff, 0x78, 0x76, 0x76, 0xff, + 0x70, 0x6f, 0x6f, 0xff, 0x6d, 0x6c, 0x6b, 0xff, 0x6e, 0x6f, 0x6d, 0xff, + 0x68, 0x68, 0x66, 0xff, 0x5f, 0x5b, 0x5b, 0xff, 0x5b, 0x56, 0x56, 0xff, + 0x5a, 0x58, 0x57, 0xff, 0x53, 0x49, 0x47, 0xff, 0x4e, 0x45, 0x43, 0xff, + 0x49, 0x43, 0x41, 0xff, 0x5f, 0x5b, 0x5a, 0xff, 0x46, 0x3f, 0x3e, 0xff, + 0x4b, 0x48, 0x47, 0xff, 0x42, 0x3f, 0x3f, 0xff, 0x4d, 0x50, 0x50, 0xff, + 0x52, 0x52, 0x51, 0xff, 0x37, 0x31, 0x30, 0xff, 0x3d, 0x38, 0x37, 0xff, + 0x3f, 0x39, 0x38, 0xff, 0x40, 0x3c, 0x3b, 0xff, 0x4a, 0x48, 0x47, 0xff, + 0x3f, 0x3d, 0x3b, 0xff, 0x46, 0x45, 0x43, 0xff, +}; + +#endif // WEBP_TESTS_FUZZER_IMG_ALPHA_H_ diff --git a/internal/libwebp-1.2.1/tests/fuzzer/img_grid.h b/internal/libwebp-1.2.1/tests/fuzzer/img_grid.h new file mode 100644 index 0000000..f269bbd --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/img_grid.h @@ -0,0 +1,125 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WEBP_TESTS_FUZZER_IMG_GRID_H_ +#define WEBP_TESTS_FUZZER_IMG_GRID_H_ + +#include + +static const int kImgGridWidth = 16; +static const int kImgGridHeight = 16; + +/*Pixel format: Red: 8 bit, Green: 8 bit, Blue: 8 bit, Fix 0xFF: 8 bit*/ +static const uint8_t kImgGridData[] = { + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, +}; + +#endif // WEBP_TESTS_FUZZER_IMG_GRID_H_ diff --git a/internal/libwebp-1.2.1/tests/fuzzer/img_peak.h b/internal/libwebp-1.2.1/tests/fuzzer/img_peak.h new file mode 100644 index 0000000..d17ba1e --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/img_peak.h @@ -0,0 +1,5533 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WEBP_TESTS_FUZZER_IMG_PEAK_H_ +#define WEBP_TESTS_FUZZER_IMG_PEAK_H_ + +#include + +static const int kImgPeakWidth = 128; +static const int kImgPeakHeight = 128; + +/*Pixel format: Red: 8 bit, Green: 8 bit, Blue: 8 bit, Fix 0xFF: 8 bit*/ +static const uint8_t kImgPeakData[] = { + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf7, 0xdd, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf7, 0xdd, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf7, 0xdd, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xe4, 0xe2, 0xd8, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xe4, 0xe2, 0xd8, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xec, 0xdb, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf7, 0xdd, 0xcc, 0xff, 0xec, 0xd6, 0xc4, 0xff, 0xd9, 0xc6, 0xb4, 0xff, + 0xa7, 0x94, 0x84, 0xff, 0xa9, 0x9b, 0x8c, 0xff, 0xa9, 0x9b, 0x8c, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0xc0, 0xaa, 0x9c, 0xff, 0xc4, 0xb2, 0xac, 0xff, + 0xe6, 0xd6, 0xcc, 0xff, 0xee, 0xdd, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xde, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf7, 0xdd, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0xc0, 0xaa, 0x9c, 0xff, + 0x9c, 0x7e, 0x6c, 0xff, 0x8c, 0x6e, 0x64, 0xff, 0x94, 0x79, 0x6c, 0xff, + 0x94, 0x79, 0x6c, 0xff, 0x8e, 0x79, 0x74, 0xff, 0x9e, 0x8b, 0x84, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x9e, 0x8b, 0x84, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xbf, 0xaa, 0xa4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0xd6, 0xc5, 0xbc, 0xff, + 0xec, 0xdb, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xde, 0xd4, 0xff, + 0xd4, 0xb8, 0xac, 0xff, 0x94, 0x79, 0x6c, 0xff, 0x84, 0x6e, 0x5c, 0xff, + 0x94, 0x79, 0x6c, 0xff, 0x8d, 0x72, 0x6b, 0xff, 0x8a, 0x76, 0x6c, 0xff, + 0x9e, 0x8b, 0x84, 0xff, 0x9e, 0x8b, 0x84, 0xff, 0xa8, 0x93, 0x8c, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x8f, 0x86, 0x7c, 0xff, + 0xc4, 0xb2, 0xac, 0xff, 0xcc, 0xbd, 0xbc, 0xff, 0xa8, 0x9b, 0x94, 0xff, + 0xd9, 0xcc, 0xbc, 0xff, 0xe4, 0xe2, 0xd8, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xdc, 0xbe, 0xb4, 0xff, 0xac, 0x8e, 0x7c, 0xff, 0x9c, 0x81, 0x74, 0xff, + 0x7c, 0x62, 0x54, 0xff, 0x80, 0x67, 0x5c, 0xff, 0xa4, 0x89, 0x7c, 0xff, + 0xbc, 0xa4, 0x9c, 0xff, 0x8d, 0x72, 0x6b, 0xff, 0xac, 0x8d, 0x84, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x96, 0x85, 0x84, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x96, 0x85, 0x84, 0xff, 0x96, 0x85, 0x84, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xc7, 0xb8, 0xac, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0xc9, 0xc0, 0xb4, 0xff, 0xe4, 0xd2, 0xc4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xe4, 0xe2, 0xd8, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xec, 0xd6, 0xc4, 0xff, 0xa4, 0x89, 0x7c, 0xff, + 0x7c, 0x5c, 0x4c, 0xff, 0x7e, 0x67, 0x54, 0xff, 0x7c, 0x62, 0x54, 0xff, + 0x80, 0x67, 0x5c, 0xff, 0x8c, 0x6e, 0x64, 0xff, 0x8c, 0x6d, 0x6c, 0xff, + 0xac, 0x8d, 0x84, 0xff, 0x8c, 0x6e, 0x64, 0xff, 0xac, 0x8d, 0x8c, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x9c, 0x7a, 0x7c, 0xff, + 0x8e, 0x72, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0x96, 0x86, 0x8c, 0xff, 0xa8, 0x9b, 0x94, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xd7, 0xcc, 0xc4, 0xff, 0xd6, 0xc5, 0xbc, 0xff, 0xb0, 0xa3, 0x9c, 0xff, + 0xc7, 0xb8, 0xac, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xec, 0xd6, 0xc4, 0xff, 0x94, 0x76, 0x64, 0xff, 0x7c, 0x5c, 0x4c, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x80, 0x67, 0x5c, 0xff, 0x8d, 0x72, 0x6b, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x8e, 0x72, 0x74, 0xff, 0xa4, 0x85, 0x84, 0xff, + 0xa4, 0x85, 0x84, 0xff, 0x8e, 0x72, 0x74, 0xff, 0x8e, 0x72, 0x74, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0x9c, 0x93, 0x8c, 0xff, 0xa8, 0x93, 0x8c, 0xff, + 0xa8, 0x93, 0x8c, 0xff, 0xc4, 0xb2, 0xac, 0xff, 0xee, 0xdd, 0xd4, 0xff, + 0xef, 0xe2, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xe7, 0xce, 0xbc, 0xff, + 0x84, 0x6e, 0x5c, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0xa8, 0x93, 0x8c, 0xff, 0xa4, 0x85, 0x84, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0xa4, 0x85, 0x84, 0xff, 0xcc, 0xaa, 0xac, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x8e, 0x72, 0x74, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0x96, 0x85, 0x84, 0xff, 0xbc, 0xb6, 0xac, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x8f, 0x86, 0x7c, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0xa8, 0x9b, 0x94, 0xff, 0xdc, 0xbe, 0xb4, 0xff, + 0xee, 0xdd, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0x84, 0x6e, 0x5c, 0xff, + 0x6f, 0x56, 0x44, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x94, 0x79, 0x6c, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xbd, 0xa6, 0xa4, 0xff, 0xb4, 0x96, 0x94, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x8e, 0x72, 0x74, 0xff, 0xbd, 0xa6, 0xa4, 0xff, 0xdc, 0xba, 0xbc, 0xff, + 0xc6, 0xb1, 0xb4, 0xff, 0xac, 0x8d, 0x8c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x9c, 0x7a, 0x7c, 0xff, 0x84, 0x77, 0x74, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xd5, 0xcd, 0xcc, 0xff, 0x9c, 0x93, 0x8c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0x8f, 0x86, 0x7c, 0xff, 0xa8, 0x93, 0x8c, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xee, 0xdd, 0xd4, 0xff, 0xee, 0xdd, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xe7, 0xce, 0xbc, 0xff, 0x9c, 0x7e, 0x6c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x8d, 0x72, 0x6b, 0xff, 0xd4, 0xc6, 0xc4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xef, 0xe2, 0xdc, 0xff, 0xc4, 0xba, 0xbc, 0xff, + 0x96, 0x85, 0x84, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xe4, 0xce, 0xcc, 0xff, + 0xec, 0xde, 0xdc, 0xff, 0xf8, 0xea, 0xec, 0xff, 0xc5, 0xb7, 0xb4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0x84, 0x77, 0x74, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0x89, 0x86, 0x84, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xee, 0xdd, 0xd4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xd4, 0xc2, 0xac, 0xff, + 0x7e, 0x67, 0x54, 0xff, 0x7c, 0x5c, 0x4c, 0xff, 0x7c, 0x5c, 0x4c, 0xff, + 0x74, 0x4e, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x4e, 0x44, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0xac, 0x8d, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xc6, 0xb1, 0xb4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xdc, 0xba, 0xbc, 0xff, + 0xcf, 0xc6, 0xcc, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xba, 0xb0, 0xa4, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0xbd, 0xa6, 0xa4, 0xff, 0xec, 0xdb, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xe7, 0xce, 0xbc, 0xff, 0x7c, 0x62, 0x54, 0xff, + 0x6f, 0x56, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x8d, 0x72, 0x6b, 0xff, 0xa6, 0x95, 0x94, 0xff, 0x9e, 0x8b, 0x84, 0xff, + 0xb0, 0xa3, 0x9c, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x77, 0x66, 0x6c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x9e, 0x8b, 0x84, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0x94, 0x8b, 0x84, 0xff, 0x84, 0x77, 0x74, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xba, 0xb0, 0xa4, 0xff, 0x94, 0x8b, 0x84, 0xff, 0xba, 0xb0, 0xa4, 0xff, + 0xec, 0xd6, 0xc4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xc7, 0xb2, 0xa4, 0xff, 0x7e, 0x67, 0x54, 0xff, 0x6f, 0x56, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5c, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x4e, 0x44, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x7f, 0x6a, 0x64, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x84, 0x77, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x9e, 0x8b, 0x84, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x94, 0x8b, 0x84, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0xe6, 0xd6, 0xcc, 0xff, 0xef, 0xe2, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xec, 0xdb, 0xcc, 0xff, 0x9c, 0x7e, 0x6c, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x7c, 0x5c, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x9c, 0x7a, 0x74, 0xff, 0x8d, 0x72, 0x6b, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x8a, 0x76, 0x6c, 0xff, 0x71, 0x6a, 0x64, 0xff, 0x96, 0x85, 0x84, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x94, 0x8b, 0x84, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x9e, 0x8b, 0x84, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x9e, 0x8b, 0x84, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x8c, 0x81, 0x84, 0xff, 0xc4, 0xb2, 0xac, 0xff, + 0xcc, 0xbd, 0xbc, 0xff, 0xbc, 0xb6, 0xac, 0xff, 0xd7, 0xcc, 0xc4, 0xff, + 0xc9, 0xc0, 0xb4, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0xd9, 0xcc, 0xbc, 0xff, 0xef, 0xe2, 0xdc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xec, 0xd6, 0xc4, 0xff, 0x84, 0x6e, 0x5c, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x80, 0x67, 0x5c, 0xff, + 0x8d, 0x72, 0x6b, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x7f, 0x6a, 0x64, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x7f, 0x72, 0x6c, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x7f, 0x6a, 0x64, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xbc, 0xb6, 0xac, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xd9, 0xd4, 0xcc, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xd6, 0xc5, 0xbc, 0xff, + 0xef, 0xe2, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xd9, 0xc6, 0xb4, 0xff, + 0x7e, 0x67, 0x54, 0xff, 0x6f, 0x56, 0x44, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x7e, 0x67, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x4e, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x80, 0x67, 0x5c, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x78, 0x66, 0x64, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x7f, 0x72, 0x6c, 0xff, + 0x7c, 0x72, 0x64, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x94, 0x8b, 0x84, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xe4, 0xde, 0xdc, 0xff, 0xcc, 0xc2, 0xbc, 0xff, + 0xcc, 0xc2, 0xbc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xd9, 0xc6, 0xb4, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x6f, 0x56, 0x44, 0xff, + 0x7c, 0x62, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x7c, 0x5c, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x8d, 0x72, 0x6b, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x8d, 0x72, 0x6b, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x75, 0x62, 0x64, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x94, 0x8b, 0x84, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xd5, 0xcd, 0xcc, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0xc7, 0xb8, 0xac, 0xff, 0xda, 0xd4, 0xc4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xd4, 0xb8, 0xac, 0xff, 0x80, 0x67, 0x5c, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x7c, 0x5c, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x7c, 0x5c, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x7c, 0x5c, 0x4c, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x4e, 0x44, 0xff, 0x7c, 0x5c, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5e, 0x5c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x7c, 0x5e, 0x5c, 0xff, 0x8c, 0x6e, 0x64, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x94, 0x8b, 0x84, 0xff, + 0x8d, 0x72, 0x6b, 0xff, 0x75, 0x56, 0x54, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x75, 0x56, 0x54, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x84, 0x77, 0x74, 0xff, 0xb1, 0xaa, 0xa4, 0xff, 0x94, 0x8b, 0x84, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x81, 0x72, 0x74, 0xff, 0x96, 0x85, 0x84, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x96, 0x85, 0x84, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0xd4, 0xc6, 0xc4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xd7, 0xcc, 0xc4, 0xff, + 0xef, 0xe2, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xac, 0x8e, 0x7c, 0xff, + 0x7c, 0x5c, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x6f, 0x56, 0x44, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x6f, 0x56, 0x44, 0xff, 0x7c, 0x62, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x8c, 0x6e, 0x64, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x7d, 0x62, 0x5c, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0xb4, 0x9b, 0x94, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x7c, 0x72, 0x64, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0x84, 0x77, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xcc, 0xbd, 0xbc, 0xff, + 0x88, 0x8a, 0x84, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xc2, 0xc2, 0xbc, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xa4, 0x86, 0x74, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x7c, 0x5c, 0x4c, 0xff, 0x7c, 0x62, 0x54, 0xff, + 0x7d, 0x62, 0x5c, 0xff, 0x8d, 0x72, 0x6b, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x8e, 0x72, 0x74, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x58, 0x49, 0x4c, 0xff, 0x74, 0x4e, 0x4c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x50, 0x4e, 0x44, 0xff, 0x7c, 0x5e, 0x5c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0xbc, 0xa4, 0x9c, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x84, 0x77, 0x74, 0xff, 0x9e, 0x8b, 0x84, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xbc, 0xaa, 0xac, 0xff, 0xc4, 0xba, 0xbc, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xb1, 0xaa, 0xa4, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0xc9, 0xc0, 0xb4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xac, 0x8e, 0x7c, 0xff, 0x6f, 0x56, 0x44, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x80, 0x67, 0x5c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x75, 0x62, 0x64, 0xff, + 0x9c, 0x7a, 0x74, 0xff, 0xa4, 0x85, 0x84, 0xff, 0x9c, 0x7a, 0x7c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0xbc, 0xa4, 0x9c, 0xff, + 0x7f, 0x72, 0x6c, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x7c, 0x5e, 0x5c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0xa8, 0x9b, 0x94, 0xff, + 0xc6, 0xb1, 0xb4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x84, 0x77, 0x74, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0xa8, 0x9b, 0x94, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xb0, 0xa3, 0x9c, 0xff, 0xd7, 0xcc, 0xc4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xf7, 0xdd, 0xcc, 0xff, 0xf7, 0xdd, 0xcc, 0xff, 0x94, 0x79, 0x6c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x6f, 0x56, 0x44, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x64, 0x5a, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x8d, 0x72, 0x6b, 0xff, 0x8c, 0x6d, 0x6c, 0xff, + 0x8c, 0x6d, 0x6c, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x8c, 0x6d, 0x6c, 0xff, + 0x7c, 0x5e, 0x5c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x78, 0x66, 0x64, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x78, 0x66, 0x64, 0xff, 0xa4, 0x9e, 0xa4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xec, 0xdb, 0xcc, 0xff, + 0xf2, 0xe9, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xfc, 0xe2, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xf7, 0xdd, 0xcc, 0xff, 0x94, 0x76, 0x64, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x6f, 0x56, 0x44, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x74, 0x4e, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x7c, 0x5e, 0x5c, 0xff, 0x72, 0x62, 0x5c, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x8c, 0x6e, 0x64, 0xff, 0x78, 0x66, 0x64, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x7c, 0x5e, 0x5c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x80, 0x67, 0x5c, 0xff, 0x75, 0x56, 0x54, 0xff, 0x50, 0x4e, 0x44, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x96, 0x85, 0x84, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x9e, 0x8e, 0x94, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xbf, 0xaa, 0xa4, 0xff, 0xa8, 0x9b, 0x94, 0xff, 0xd9, 0xcc, 0xbc, 0xff, + 0xef, 0xe2, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x6f, 0x56, 0x44, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x7c, 0x5c, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x7c, 0x5e, 0x5c, 0xff, 0x8c, 0x6d, 0x6c, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x8d, 0x72, 0x6b, 0xff, 0x69, 0x55, 0x54, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x80, 0x67, 0x5c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x90, 0x7f, 0x74, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x90, 0x7f, 0x74, 0xff, 0x9c, 0x88, 0x7c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x8e, 0x79, 0x74, 0xff, 0xac, 0x8d, 0x8c, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x96, 0x85, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xc6, 0xb1, 0xb4, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xba, 0xb0, 0xa4, 0xff, + 0xe6, 0xd6, 0xcc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe2, 0xcc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xac, 0x8e, 0x7c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x4e, 0x44, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x78, 0x66, 0x64, 0xff, 0xac, 0x8d, 0x8c, 0xff, 0xbf, 0xaa, 0xa4, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x80, 0x67, 0x5c, 0xff, 0x8a, 0x76, 0x6c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x80, 0x67, 0x5c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x9c, 0x7a, 0x74, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x96, 0x85, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x94, 0x8b, 0x84, 0xff, 0x96, 0x85, 0x84, 0xff, 0x84, 0x79, 0x7c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0xa8, 0x9b, 0x94, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xe2, 0xdc, 0xcc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe6, 0xcc, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0x94, 0x79, 0x6c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x7c, 0x62, 0x54, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x74, 0x4e, 0x44, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0xbc, 0xa4, 0x9c, 0xff, 0xc5, 0xb7, 0xb4, 0xff, + 0xac, 0x8d, 0x8c, 0xff, 0x7f, 0x6a, 0x64, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x7d, 0x62, 0x5c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x78, 0x66, 0x64, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x75, 0x62, 0x64, 0xff, 0x78, 0x66, 0x64, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0x9e, 0x8e, 0x94, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x96, 0x86, 0x8c, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xd4, 0xc6, 0xc4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xba, 0xb0, 0xa4, 0xff, 0xd9, 0xd4, 0xcc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0x9c, 0x7e, 0x6c, 0xff, 0x7c, 0x62, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x94, 0x79, 0x6c, 0xff, 0xbc, 0xa4, 0x9c, 0xff, 0xc7, 0xb8, 0xac, 0xff, + 0x70, 0x66, 0x5c, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x7c, 0x72, 0x64, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x8c, 0x6e, 0x64, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x74, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x7c, 0x5e, 0x5c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x75, 0x56, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x75, 0x62, 0x64, 0xff, 0xac, 0x8d, 0x8c, 0xff, 0x7c, 0x5e, 0x5c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0x6b, 0x56, 0x5c, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xba, 0xb0, 0xa4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, + 0xe2, 0xdc, 0xcc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf9, 0xe2, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xac, 0x8d, 0x84, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5c, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, 0x74, 0x4e, 0x44, 0xff, + 0x74, 0x4e, 0x44, 0xff, 0x75, 0x56, 0x54, 0xff, 0x74, 0x4e, 0x4c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x8a, 0x76, 0x6c, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0x8a, 0x76, 0x6c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x7c, 0x5e, 0x5c, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x84, 0x77, 0x74, 0xff, + 0xbd, 0xa6, 0xa4, 0xff, 0x84, 0x77, 0x74, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x8d, 0x72, 0x6b, 0xff, 0x96, 0x85, 0x84, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x84, 0x77, 0x74, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0x96, 0x86, 0x8c, 0xff, 0x81, 0x72, 0x74, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x9e, 0x8b, 0x84, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xcc, 0xbe, 0xc4, 0xff, 0xa1, 0xa2, 0x9c, 0xff, 0xc7, 0xb8, 0xac, 0xff, + 0xd7, 0xcc, 0xc4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf9, 0xe2, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xb4, 0x9a, 0x8c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x4e, 0x44, 0xff, 0x75, 0x56, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x84, 0x77, 0x74, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x69, 0x55, 0x54, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x58, 0x49, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x7f, 0x6a, 0x64, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x74, 0x4e, 0x4c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x8c, 0x6d, 0x6c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x7d, 0x62, 0x5c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0xad, 0x9a, 0x9c, 0xff, 0x9e, 0x8e, 0x94, 0xff, 0xa4, 0x9e, 0xa4, 0xff, + 0x9e, 0x8e, 0x94, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xc4, 0xba, 0xbc, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0xb0, 0xa3, 0x9c, 0xff, + 0xbc, 0xb6, 0xac, 0xff, 0xd9, 0xd4, 0xcc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xb1, 0xa3, 0x94, 0xff, 0xc9, 0xc0, 0xb4, 0xff, 0xd9, 0xcc, 0xbc, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x7c, 0x72, 0x64, 0xff, + 0x8f, 0x86, 0x7c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x8e, 0x72, 0x74, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x8e, 0x72, 0x74, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0xbc, 0xaa, 0xac, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0xbd, 0xa6, 0xa4, 0xff, 0xbd, 0xa6, 0xa4, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0x8e, 0x72, 0x74, 0xff, 0x7f, 0x72, 0x6c, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0xb0, 0xa3, 0x9c, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xea, 0xe4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf2, 0xe9, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0x7c, 0x72, 0x64, 0xff, 0x7f, 0x72, 0x6c, 0xff, 0x64, 0x5a, 0x4c, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x7c, 0x5e, 0x5c, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5c, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x75, 0x56, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x50, 0x4e, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x8c, 0x6d, 0x6c, 0xff, 0x84, 0x77, 0x74, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x81, 0x72, 0x74, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x9e, 0x8e, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xad, 0x9a, 0x9c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0xc6, 0xb1, 0xb4, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0x9e, 0x8e, 0x94, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x9e, 0x8e, 0x94, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xbc, 0xb6, 0xac, 0xff, 0xbf, 0xaa, 0xa4, 0xff, 0xc9, 0xc0, 0xb4, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf2, 0xe9, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x94, 0x8b, 0x84, 0xff, 0x90, 0x7f, 0x74, 0xff, 0x71, 0x6a, 0x64, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x7c, 0x5e, 0x5c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x50, 0x4e, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x4e, 0x4c, 0xff, 0x5c, 0x3e, 0x34, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x50, 0x4e, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x80, 0x67, 0x5c, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x77, 0x66, 0x6c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x78, 0x66, 0x64, 0xff, 0x77, 0x66, 0x6c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xad, 0x9a, 0x9c, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0xbc, 0xaa, 0xac, 0xff, + 0x9e, 0x8e, 0x94, 0xff, 0x9c, 0x7a, 0x7c, 0xff, 0x84, 0x79, 0x7c, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x96, 0x85, 0x84, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xcc, 0xc2, 0xbc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0xb0, 0xa3, 0x9c, 0xff, + 0xd7, 0xcc, 0xc4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0x94, 0x8b, 0x84, 0xff, 0x90, 0x7f, 0x74, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x7f, 0x72, 0x6c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x72, 0x62, 0x5c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x7c, 0x5d, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x7f, 0x6a, 0x64, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x77, 0x66, 0x6c, 0xff, 0x75, 0x62, 0x64, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x96, 0x86, 0x8c, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x8e, 0x72, 0x74, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x8e, 0x7a, 0x7c, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0x96, 0x86, 0x8c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x96, 0x86, 0x8c, 0xff, 0x96, 0x86, 0x8c, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xc2, 0xbc, 0xff, + 0xc4, 0xbe, 0xc4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xa9, 0x9b, 0x8c, 0xff, 0xf9, 0xe2, 0xd4, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xea, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x9c, 0x93, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0x94, 0x8b, 0x84, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0xd7, 0xcc, 0xc4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x81, 0x72, 0x74, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x70, 0x66, 0x5c, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x75, 0x62, 0x64, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0x96, 0x85, 0x84, 0xff, + 0x7f, 0x72, 0x6c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xb0, 0xa3, 0x9c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0x96, 0x85, 0x84, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x78, 0x77, 0x74, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x8c, 0x81, 0x84, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xad, 0x9a, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xbc, 0xaa, 0xac, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0xb0, 0xaa, 0x9c, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xea, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x92, 0x92, 0x8c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xc8, 0xca, 0xc4, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0xb1, 0xaa, 0xa4, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x78, 0x66, 0x64, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x74, 0x4e, 0x4c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x50, 0x4e, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x75, 0x56, 0x54, 0xff, 0x7c, 0x5e, 0x5c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x7f, 0x72, 0x6c, 0xff, 0x96, 0x85, 0x84, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x94, 0x8b, 0x84, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x84, 0x77, 0x74, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x81, 0x72, 0x74, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0x94, 0x8b, 0x84, 0xff, 0xe4, 0xde, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x9c, 0x9a, 0x94, 0xff, 0xe4, 0xde, 0xdc, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x8c, 0x6e, 0x64, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x74, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x70, 0x66, 0x5c, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x50, 0x4e, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x96, 0x85, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x94, 0x8b, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x75, 0x62, 0x64, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0x94, 0x8e, 0x94, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0xa8, 0x9b, 0x94, 0xff, 0xc7, 0xb8, 0xac, 0xff, + 0xf4, 0xea, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xe8, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0x9c, 0x93, 0x8c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xe4, 0xde, 0xdc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xbd, 0xa6, 0xa4, 0xff, + 0x9c, 0x7a, 0x7c, 0xff, 0x75, 0x62, 0x64, 0xff, 0x7c, 0x5e, 0x5c, 0xff, + 0x74, 0x4e, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x8e, 0x72, 0x74, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x94, 0x8b, 0x84, 0xff, + 0x96, 0x85, 0x84, 0xff, 0xa6, 0x95, 0x94, 0xff, 0x9c, 0x93, 0x8c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x84, 0x77, 0x74, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x96, 0x86, 0x8c, 0xff, 0x96, 0x85, 0x84, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0x96, 0x85, 0x84, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0xa1, 0xa2, 0x9c, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0x9c, 0x93, 0x8c, 0xff, + 0xef, 0xe2, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xe8, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xc2, 0xc2, 0xbc, 0xff, 0x94, 0x8e, 0x94, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0x71, 0x72, 0x6c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xe4, 0xe2, 0xd8, 0xff, 0xdc, 0xd6, 0xdc, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xbe, 0xc4, 0xff, + 0xc6, 0xb1, 0xb4, 0xff, 0xb4, 0x96, 0x94, 0xff, 0x75, 0x62, 0x64, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x7f, 0x72, 0x6c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xb0, 0xa3, 0x9c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xec, 0xde, 0xdc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xd6, 0xc5, 0xbc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0xbc, 0xb6, 0xac, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x92, 0x92, 0x8c, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xbc, 0xaa, 0xac, 0xff, 0xbc, 0xaa, 0xac, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x50, 0x4e, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x7c, 0x5d, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0x94, 0x8b, 0x84, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0x9c, 0x93, 0x8c, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x89, 0x86, 0x84, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x89, 0x86, 0x84, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xdc, 0xd6, 0xdc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x7f, 0x6a, 0x64, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x71, 0x6a, 0x64, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x8e, 0x7a, 0x7c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x8f, 0x86, 0x7c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x78, 0x66, 0x64, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x84, 0x77, 0x74, 0xff, 0x96, 0x85, 0x84, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xcc, 0xbe, 0xc4, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xad, 0x9a, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xc4, 0xbe, 0xc4, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xd5, 0xcd, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xb0, 0xa3, 0x9c, 0xff, 0xbc, 0xb6, 0xac, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xee, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xee, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x71, 0x72, 0x6c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0x94, 0x8e, 0x94, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x75, 0x62, 0x64, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x53, 0x41, 0x3c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x53, 0x41, 0x3c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x7f, 0x6a, 0x64, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x72, 0x62, 0x5c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x7f, 0x72, 0x6c, 0xff, 0x84, 0x79, 0x7c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x9c, 0x93, 0x8c, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xa4, 0x9e, 0xa4, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xb1, 0xaa, 0xa4, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xba, 0xb0, 0xa4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xee, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xac, 0xa9, 0xac, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x7b, 0x7a, 0x7c, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0x71, 0x72, 0x74, 0xff, 0x71, 0x72, 0x74, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x84, 0x77, 0x74, 0xff, 0x94, 0x8b, 0x84, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x84, 0x77, 0x74, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x84, 0x79, 0x7c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0xb0, 0xa3, 0x9c, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xbc, 0xb6, 0xac, 0xff, 0xc5, 0xb7, 0xb4, 0xff, + 0xe4, 0xe2, 0xd8, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x71, 0x72, 0x74, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0x94, 0x8e, 0x94, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x81, 0x72, 0x74, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x72, 0x5b, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x8f, 0x86, 0x7c, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x77, 0x66, 0x6c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x75, 0x62, 0x64, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x96, 0x86, 0x8c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xb1, 0xaa, 0xa4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xc2, 0xc2, 0xbc, 0xff, 0xb1, 0xaa, 0xa4, 0xff, 0xd6, 0xc5, 0xbc, 0xff, + 0xb0, 0xa3, 0x9c, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xfc, 0xf2, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x78, 0x77, 0x74, 0xff, 0x71, 0x72, 0x74, 0xff, + 0x78, 0x77, 0x74, 0xff, 0x83, 0x82, 0x84, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x8c, 0x86, 0x8c, 0xff, 0x96, 0x85, 0x84, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x96, 0x86, 0x8c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x7d, 0x62, 0x5c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x96, 0x85, 0x84, 0xff, 0x7c, 0x72, 0x64, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0x9e, 0x8e, 0x94, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xf8, 0xea, 0xec, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xc2, 0xc2, 0xbc, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xd7, 0xcc, 0xc4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x8c, 0x81, 0x84, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0xcc, 0xbd, 0xbc, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x58, 0x49, 0x4c, 0xff, 0x7c, 0x5d, 0x54, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x71, 0x6a, 0x64, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x81, 0x72, 0x74, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x84, 0x79, 0x7c, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xc4, 0xbe, 0xc4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xc8, 0xca, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xc2, 0xbc, 0xff, + 0xcc, 0xc2, 0xbc, 0xff, 0x9c, 0x93, 0x8c, 0xff, 0xee, 0xdd, 0xd4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xac, 0xa9, 0xac, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0x77, 0x66, 0x6c, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x7f, 0x72, 0x6c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x3e, 0x34, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x81, 0x72, 0x74, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x81, 0x72, 0x74, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xc4, 0xbe, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xcc, 0xc2, 0xbc, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0x9c, 0x92, 0x84, 0xff, + 0xe2, 0xdc, 0xcc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0xac, 0xa9, 0xac, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x6b, 0x56, 0x5c, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x72, 0x62, 0x5c, 0xff, 0x7f, 0x72, 0x6c, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x7f, 0x72, 0x6c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x75, 0x62, 0x64, 0xff, 0x75, 0x62, 0x64, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x84, 0x77, 0x74, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x96, 0x85, 0x84, 0xff, 0xac, 0xa9, 0xac, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0xc4, 0xba, 0xbc, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x83, 0x82, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x94, 0x8e, 0x94, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xcc, 0xbe, 0xc4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xcc, 0xbd, 0xbc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0x8f, 0x86, 0x7c, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0x94, 0x8e, 0x94, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x96, 0x86, 0x8c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xc4, 0xba, 0xbc, 0xff, + 0xd4, 0xc6, 0xc4, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x55, 0x55, 0x54, 0xff, 0x6b, 0x56, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x71, 0x6a, 0x64, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x72, 0x62, 0x5c, 0xff, 0x89, 0x86, 0x84, 0xff, + 0x9c, 0x93, 0x8c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x9e, 0x8e, 0x94, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xe4, 0xe1, 0xe4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xc4, 0xbe, 0xc4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xd5, 0xcd, 0xcc, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xb1, 0xaa, 0xa4, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0xbc, 0xb6, 0xac, 0xff, 0xfb, 0xf9, 0xec, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf1, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xcf, 0xc6, 0xcc, 0xff, 0xac, 0xa9, 0xac, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xac, 0xa9, 0xac, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0x7b, 0x7a, 0x7c, 0xff, 0x96, 0x85, 0x84, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x78, 0x66, 0x64, 0xff, 0x71, 0x6a, 0x64, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x84, 0x77, 0x74, 0xff, 0x50, 0x4e, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x3e, 0x34, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x3e, 0x34, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x81, 0x72, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0xa6, 0x95, 0x94, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x96, 0x85, 0x84, 0xff, 0x96, 0x85, 0x84, 0xff, + 0x78, 0x77, 0x74, 0xff, 0x96, 0x85, 0x84, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xac, 0xa9, 0xac, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xcf, 0xc6, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xd4, 0xc6, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xc2, 0xbc, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0x9c, 0x88, 0x7c, 0xff, 0xe6, 0xd6, 0xcc, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfb, 0xf9, 0xec, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0x83, 0x82, 0x84, 0xff, 0x78, 0x77, 0x74, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x7f, 0x6a, 0x64, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x7f, 0x72, 0x6c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x84, 0x77, 0x74, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x8c, 0x81, 0x84, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xbd, 0xbc, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0x9c, 0x93, 0x8c, 0xff, 0xa8, 0x9b, 0x94, 0xff, + 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x94, 0x8e, 0x94, 0xff, + 0xc4, 0xbe, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0xc6, 0xb1, 0xb4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x74, 0x4e, 0x4c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x8f, 0x86, 0x7c, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0x96, 0x85, 0x84, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x77, 0x66, 0x6c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x89, 0x86, 0x84, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xc4, 0xbe, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xdc, 0xd6, 0xdc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xd4, 0xc6, 0xc4, 0xff, 0xcc, 0xbe, 0xc4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0x94, 0x8b, 0x84, 0xff, + 0xba, 0xb0, 0xa4, 0xff, 0xf4, 0xea, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xe4, 0xe2, 0xd8, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0x9c, 0x9a, 0x94, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x8e, 0x79, 0x74, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xd7, 0xcc, 0xc4, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x3e, 0x34, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x84, 0x79, 0x7c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xb1, 0xaa, 0xa4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xc4, 0xbe, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xc4, 0xbe, 0xc4, 0xff, 0xd5, 0xcd, 0xcc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0xcc, 0xbe, 0xac, 0xff, 0xfb, 0xf9, 0xec, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfa, 0xfe, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0x94, 0x8e, 0x94, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0x89, 0x86, 0x84, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x81, 0x72, 0x74, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, + 0xbd, 0xa6, 0xa4, 0xff, 0x84, 0x77, 0x74, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x74, 0x4e, 0x4c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x96, 0x85, 0x84, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0x83, 0x82, 0x84, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xf8, 0xea, 0xec, 0xff, 0xec, 0xe2, 0xe4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xcf, 0xc6, 0xcc, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0x90, 0x7f, 0x74, 0xff, 0x9c, 0x93, 0x8c, 0xff, 0xfb, 0xf9, 0xec, 0xff, + 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xc2, 0xc2, 0xbc, 0xff, 0xb3, 0xae, 0xac, 0xff, 0x9c, 0x9a, 0x94, 0xff, + 0x94, 0x8e, 0x94, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x9c, 0x9a, 0x94, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0x94, 0x8b, 0x84, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x7f, 0x72, 0x6c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x96, 0x85, 0x84, 0xff, 0x7d, 0x62, 0x5c, 0xff, + 0x53, 0x41, 0x3c, 0xff, 0x75, 0x56, 0x54, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x8f, 0x86, 0x7c, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x96, 0x85, 0x84, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x96, 0x85, 0x84, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0x96, 0x85, 0x84, 0xff, 0xa4, 0x9e, 0xa4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xac, 0xa9, 0xac, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xe4, 0xe1, 0xe4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xe4, 0xd2, 0xd4, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xd9, 0xd4, 0xcc, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xd7, 0xcc, 0xc4, 0xff, + 0xfb, 0xf9, 0xec, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xc4, 0xbe, 0xc4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xac, 0xa9, 0xac, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xa1, 0xa2, 0x9c, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x9c, 0x93, 0x8c, 0xff, 0xc4, 0xb2, 0xac, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x84, 0x77, 0x74, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x96, 0x85, 0x84, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x89, 0x86, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xb0, 0xa3, 0x9c, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0x89, 0x86, 0x84, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xc4, 0xba, 0xbc, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xe4, 0xde, 0xdc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xf2, 0xf0, 0xec, 0xff, + 0xcc, 0xbe, 0xc4, 0xff, 0xd4, 0xc6, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xd4, 0xc6, 0xc4, 0xff, 0xa8, 0x93, 0x8c, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xfb, 0xf9, 0xec, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfa, 0xfe, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0x92, 0x92, 0x8c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xa6, 0x95, 0x94, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x75, 0x62, 0x64, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x78, 0x66, 0x64, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0x80, 0x67, 0x5c, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x61, 0x48, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x89, 0x86, 0x84, 0xff, 0x96, 0x85, 0x84, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x9e, 0x8e, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xa1, 0xa2, 0x9c, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xfc, 0xf6, 0xf4, 0xff, 0xdc, 0xd6, 0xdc, 0xff, + 0xd4, 0xc6, 0xc4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xd4, 0xc6, 0xc4, 0xff, + 0xdc, 0xd6, 0xdc, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xa8, 0x93, 0x8c, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfa, 0xfe, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xe9, 0xd4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x81, 0x72, 0x74, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x70, 0x5e, 0x5c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xbc, 0xaa, 0xac, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x3e, 0x34, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x5c, 0x3e, 0x34, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0x89, 0x86, 0x84, 0xff, 0x9c, 0x9a, 0x94, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xc2, 0xc2, 0xbc, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xc4, 0xbe, 0xc4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xc4, 0xbe, 0xc4, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xec, 0xe2, 0xe4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xcc, 0xc2, 0xbc, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0xb0, 0xa3, 0x9c, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x94, 0x8e, 0x94, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xc8, 0xca, 0xc4, 0xff, + 0xc2, 0xc2, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0x64, 0x4e, 0x54, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0xa8, 0x9b, 0x94, 0xff, + 0xbc, 0xaa, 0xac, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, + 0x69, 0x4e, 0x44, 0xff, 0x69, 0x4e, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x53, 0x41, 0x3c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x8f, 0x86, 0x7c, 0xff, + 0x78, 0x77, 0x74, 0xff, 0x78, 0x77, 0x74, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0x96, 0x86, 0x8c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xc8, 0xca, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xd5, 0xcd, 0xcc, 0xff, 0xf4, 0xf2, 0xf4, 0xff, + 0xf4, 0xf2, 0xf4, 0xff, 0xd4, 0xc6, 0xc4, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xc4, 0xbe, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0xc9, 0xc0, 0xb4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfb, 0xfe, 0xec, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x78, 0x77, 0x74, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0x88, 0x8a, 0x84, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x78, 0x66, 0x64, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x75, 0x62, 0x64, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x6c, 0x66, 0x6c, 0xff, 0x83, 0x82, 0x84, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xc6, 0xb1, 0xb4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x75, 0x56, 0x54, 0xff, 0x69, 0x4e, 0x44, 0xff, + 0x74, 0x5b, 0x4c, 0xff, 0x74, 0x4e, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x72, 0x62, 0x5c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x81, 0x72, 0x74, 0xff, 0x96, 0x85, 0x84, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x92, 0x92, 0x8c, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0xc6, 0xb1, 0xb4, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xc2, 0xc2, 0xbc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc2, 0xc2, 0xbc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xec, 0xe2, 0xe4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xc4, 0xbe, 0xc4, 0xff, 0xcc, 0xbd, 0xbc, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xf4, 0xea, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0x7b, 0x7a, 0x7c, 0xff, + 0x83, 0x82, 0x84, 0xff, 0xc2, 0xc2, 0xbc, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xbc, 0xaa, 0xac, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x71, 0x6a, 0x64, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xdc, 0xba, 0xbc, 0xff, 0xc6, 0xb1, 0xb4, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x75, 0x56, 0x54, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x72, 0x5b, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x84, 0x77, 0x74, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x84, 0x79, 0x7c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x89, 0x86, 0x84, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x84, 0x79, 0x7c, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xc5, 0xb7, 0xb4, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x9c, 0x93, 0x8c, 0xff, 0xd7, 0xcc, 0xc4, 0xff, + 0xfb, 0xf9, 0xec, 0xff, 0xfb, 0xf9, 0xec, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0x71, 0x72, 0x74, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xa1, 0xa2, 0x9c, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xc4, 0xba, 0xbc, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xcc, 0xbe, 0xc4, 0xff, 0xc4, 0xba, 0xbc, 0xff, + 0x8e, 0x72, 0x74, 0xff, 0x69, 0x55, 0x54, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x74, 0x55, 0x4c, 0xff, 0x74, 0x55, 0x4c, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x81, 0x72, 0x74, 0xff, 0x84, 0x79, 0x7c, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0x89, 0x86, 0x84, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0x92, 0x92, 0x8c, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xec, 0xea, 0xec, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0x89, 0x86, 0x84, 0xff, 0xa8, 0x9b, 0x94, 0xff, + 0xf2, 0xe9, 0xdc, 0xff, 0xfc, 0xf2, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x71, 0x72, 0x6c, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0x92, 0x92, 0x8c, 0xff, 0x71, 0x72, 0x74, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x92, 0x92, 0x8c, 0xff, 0x83, 0x82, 0x84, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0x83, 0x82, 0x84, 0xff, + 0xa1, 0xa2, 0x9c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x75, 0x62, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x75, 0x62, 0x64, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x74, 0x55, 0x4c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x3e, 0x34, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x54, 0x46, 0x3c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x89, 0x86, 0x84, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x94, 0x8e, 0x94, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xad, 0x9a, 0x9c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x94, 0x8e, 0x94, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xc8, 0xca, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xf4, 0xea, 0xe4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xc4, 0xba, 0xbc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0xd4, 0xc6, 0xc4, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x90, 0x7f, 0x74, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xfb, 0xf9, 0xec, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x88, 0x8a, 0x84, 0xff, 0x71, 0x72, 0x74, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x89, 0x86, 0x84, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x78, 0x77, 0x74, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0x96, 0x85, 0x84, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x6a, 0x53, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x6c, 0x4a, 0x44, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x4e, 0x42, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x7f, 0x72, 0x6c, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xa4, 0x9e, 0xa4, 0xff, + 0x9e, 0x8e, 0x94, 0xff, 0x81, 0x72, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x89, 0x86, 0x84, 0xff, 0xb1, 0xaa, 0xa4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xe4, 0xe1, 0xe4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc2, 0xc2, 0xbc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xb0, 0xa3, 0x9c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0x94, 0x8b, 0x84, 0xff, 0xe4, 0xe2, 0xd8, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfc, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf0, 0xd4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0x88, 0x8a, 0x84, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x63, 0x6a, 0x64, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x89, 0x86, 0x84, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0x9c, 0x9a, 0x94, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x6a, 0x53, 0x4c, 0xff, 0x75, 0x56, 0x54, 0xff, 0x6c, 0x4a, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x90, 0x7f, 0x74, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x96, 0x85, 0x84, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x96, 0x85, 0x84, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcf, 0xc6, 0xcc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xc8, 0xca, 0xc4, 0xff, + 0xe4, 0xe1, 0xe4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xb4, 0xaa, 0xac, 0xff, 0xc4, 0xba, 0xbc, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x94, 0x8b, 0x84, 0xff, 0xb4, 0xae, 0x9c, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x6a, 0x64, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x63, 0x6a, 0x64, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x71, 0x6a, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x78, 0x77, 0x74, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x75, 0x62, 0x64, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x69, 0x4e, 0x4c, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x96, 0x85, 0x84, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0x84, 0x79, 0x7c, 0xff, 0x96, 0x85, 0x84, 0xff, 0x9e, 0x8e, 0x94, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xd9, 0xd4, 0xcc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xc8, 0xca, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xd9, 0xd4, 0xcc, 0xff, 0x9c, 0x93, 0x8c, 0xff, + 0x88, 0x8a, 0x84, 0xff, 0x8f, 0x86, 0x7c, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfb, 0xfe, 0xec, 0xff, 0xfb, 0xfe, 0xec, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfc, 0xf9, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xfc, 0xf9, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfc, 0xf9, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf0, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x88, 0x8a, 0x84, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x71, 0x72, 0x74, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x71, 0x6a, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x6c, 0x66, 0x6c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x83, 0x82, 0x84, 0xff, + 0x7b, 0x7a, 0x7c, 0xff, 0x71, 0x72, 0x6c, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x69, 0x4e, 0x4c, 0xff, 0x5c, 0x42, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x4e, 0x42, 0x44, 0xff, 0x60, 0x46, 0x3c, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x96, 0x85, 0x84, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x94, 0x8b, 0x84, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0x89, 0x86, 0x84, 0xff, 0x9e, 0x8e, 0x94, 0xff, 0x9c, 0x9a, 0x94, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0xa4, 0x9e, 0xa4, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xc2, 0xc2, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xcf, 0xc6, 0xcc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xc8, 0xca, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xd4, 0xc6, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xc6, 0xb1, 0xb4, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x9c, 0x9a, 0x94, 0xff, 0xfb, 0xf9, 0xec, 0xff, + 0xfb, 0xf9, 0xec, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x71, 0x72, 0x74, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x6c, 0x66, 0x6c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x71, 0x72, 0x74, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x69, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x61, 0x48, 0x44, 0xff, 0x5c, 0x42, 0x44, 0xff, + 0x5c, 0x42, 0x3c, 0xff, 0x53, 0x41, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x96, 0x85, 0x84, 0xff, + 0x8e, 0x7a, 0x7c, 0xff, 0x8c, 0x81, 0x84, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0x8c, 0x81, 0x84, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0x94, 0x8e, 0x94, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xd7, 0xd6, 0xd4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0xcc, 0xbe, 0xc4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0x92, 0x92, 0x8c, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xfb, 0xfe, 0xec, 0xff, 0xfb, 0xfe, 0xec, 0xff, 0xfb, 0xfe, 0xec, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfa, 0xfe, 0xdc, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x6a, 0x64, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x83, 0x82, 0x84, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x71, 0x72, 0x6c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x53, 0x41, 0x3c, 0xff, 0x61, 0x48, 0x44, 0xff, 0x61, 0x48, 0x44, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x8e, 0x7a, 0x7c, 0xff, + 0x78, 0x77, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xae, 0xa4, 0xa4, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x8c, 0x81, 0x84, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x94, 0x8c, 0x8c, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xd7, 0xd6, 0xd4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xcf, 0xc6, 0xcc, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xcc, 0xbd, 0xbc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0x9c, 0x93, 0x8c, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x94, 0x8e, 0x94, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x88, 0x8a, 0x84, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x61, 0x48, 0x44, 0xff, 0x58, 0x49, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x89, 0x86, 0x84, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xd4, 0xc6, 0xc4, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x69, 0x55, 0x54, 0xff, 0x75, 0x62, 0x64, 0xff, + 0x94, 0x8c, 0x8c, 0xff, 0x89, 0x86, 0x84, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xb1, 0xaa, 0xa4, 0xff, 0xc4, 0xbe, 0xc4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xbe, 0xc4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xb4, 0xaa, 0xac, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xd7, 0xd6, 0xd4, 0xff, 0x9c, 0x9a, 0x94, 0xff, 0xe2, 0xdc, 0xcc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfb, 0xfe, 0xec, 0xff, 0xfb, 0xfe, 0xec, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x69, 0x4e, 0x4c, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0x96, 0x86, 0x8c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x9c, 0x9a, 0x94, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x94, 0x8e, 0x94, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xd7, 0xd6, 0xd4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xc5, 0xb7, 0xb4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0x9e, 0x8e, 0x94, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xd9, 0xd4, 0xcc, 0xff, 0xe4, 0xe2, 0xd8, 0xff, + 0xfb, 0xfe, 0xec, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x6b, 0x56, 0x5c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0xb1, 0xaa, 0xa4, 0xff, 0xbc, 0xb0, 0xac, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xe4, 0xce, 0xcc, 0xff, 0xd5, 0xcd, 0xcc, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xc4, 0xbe, 0xc4, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xe4, 0xe1, 0xe4, 0xff, + 0xec, 0xea, 0xec, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xc4, 0xbe, 0xc4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xbc, 0xb1, 0xb4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xb4, 0xaa, 0xac, 0xff, 0xbc, 0xaa, 0xac, 0xff, 0xcc, 0xbe, 0xc4, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xd8, 0xdc, 0xd4, 0xff, 0xe8, 0xe7, 0xe4, 0xff, + 0xfb, 0xf9, 0xec, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfb, 0xfe, 0xec, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x78, 0x77, 0x74, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x78, 0x77, 0x74, 0xff, 0x78, 0x77, 0x74, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xe6, 0xd6, 0xd6, 0xff, 0xcc, 0xbd, 0xbc, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x84, 0x77, 0x74, 0xff, 0x9e, 0x8b, 0x84, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x89, 0x86, 0x84, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xd9, 0xd4, 0xcc, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xd7, 0xd6, 0xd4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xe8, 0xe7, 0xe4, 0xff, + 0xe8, 0xe7, 0xe4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xd5, 0xcd, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0xcf, 0xc6, 0xcc, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfb, 0xfe, 0xec, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x71, 0x6a, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0x83, 0x82, 0x84, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xe4, 0xe1, 0xe4, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xe8, 0xe7, 0xe4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xe4, 0xde, 0xdc, 0xff, 0xe4, 0xe1, 0xe4, 0xff, + 0xf4, 0xf2, 0xf4, 0xff, 0xe8, 0xe7, 0xe4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xba, 0xbc, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xd9, 0xd4, 0xcc, 0xff, 0xe8, 0xe7, 0xe4, 0xff, + 0xfb, 0xf9, 0xec, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfb, 0xfe, 0xec, 0xff, + 0xfb, 0xfe, 0xec, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x6c, 0x66, 0x6c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x7b, 0x7a, 0x7c, 0xff, + 0x89, 0x86, 0x84, 0xff, 0x83, 0x82, 0x84, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xbc, 0xb1, 0xb4, 0xff, 0xae, 0xa4, 0xa4, 0xff, 0x84, 0x79, 0x7c, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x81, 0x72, 0x74, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc8, 0xca, 0xc4, 0xff, 0xe4, 0xe1, 0xe4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xec, 0xea, 0xec, 0xff, 0xec, 0xea, 0xec, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xd9, 0xd4, 0xcc, 0xff, + 0xd8, 0xdc, 0xd4, 0xff, 0xfb, 0xf9, 0xec, 0xff, 0xfb, 0xf9, 0xec, 0xff, + 0xfb, 0xfe, 0xec, 0xff, 0xfb, 0xfe, 0xec, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfb, 0xfe, 0xec, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x71, 0x72, 0x74, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x71, 0x72, 0x6c, 0xff, 0x7b, 0x7a, 0x7c, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0xa4, 0x9e, 0xa4, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x84, 0x79, 0x7c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xd8, 0xdc, 0xd4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xcc, 0xc5, 0xc4, 0xff, 0xc2, 0xc2, 0xbc, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xbc, 0xb6, 0xac, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc8, 0xca, 0xc4, 0xff, + 0xc8, 0xca, 0xc4, 0xff, 0xf2, 0xf0, 0xec, 0xff, 0xf2, 0xf0, 0xec, 0xff, + 0xfb, 0xfe, 0xec, 0xff, 0xfb, 0xfe, 0xec, 0xff, 0xfb, 0xfe, 0xec, 0xff, + 0xfb, 0xfe, 0xec, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xfa, 0xfe, 0xe4, 0xff, + 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x7b, 0x7a, 0x7c, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x78, 0x77, 0x74, 0xff, + 0x83, 0x82, 0x84, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0xc6, 0xb1, 0xb4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xb3, 0xae, 0xac, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xcf, 0xc6, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xcf, 0xc6, 0xcc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xd9, 0xd4, 0xcc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xd8, 0xdc, 0xd4, 0xff, 0xfc, 0xfe, 0xf4, 0xff, + 0xfb, 0xfe, 0xec, 0xff, 0xfa, 0xfe, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfb, 0xfe, 0xec, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x63, 0x6a, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x7b, 0x7a, 0x7c, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x8c, 0x86, 0x8c, 0xff, 0xac, 0xa9, 0xac, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xc5, 0xc4, 0xff, + 0xe4, 0xe1, 0xe4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xd7, 0xd6, 0xd4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xe4, 0xe1, 0xe4, 0xff, + 0xd7, 0xd6, 0xd4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xa1, 0xa2, 0x9c, 0xff, 0xbc, 0xb6, 0xac, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xd8, 0xdc, 0xd4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xf2, 0xf0, 0xec, 0xff, + 0xfb, 0xf9, 0xec, 0xff, 0xfb, 0xf9, 0xec, 0xff, 0xfb, 0xfe, 0xec, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xfb, 0xf9, 0xec, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xfb, 0xf9, 0xec, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x6a, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x71, 0x6a, 0x64, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0x94, 0x8c, 0x8c, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x63, 0x66, 0x64, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xc4, 0xbe, 0xc4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xd6, 0xd2, 0xd4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xe4, 0xe1, 0xe4, 0xff, 0xe4, 0xe2, 0xd8, 0xff, 0xe4, 0xe1, 0xe4, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xc8, 0xca, 0xc4, 0xff, 0xe4, 0xe2, 0xd8, 0xff, 0xe8, 0xe7, 0xe4, 0xff, + 0xfb, 0xf9, 0xec, 0xff, 0xfb, 0xf9, 0xec, 0xff, 0xe4, 0xe2, 0xd8, 0xff, + 0xc9, 0xc0, 0xb4, 0xff, 0xb0, 0xa3, 0x9c, 0xff, 0xb1, 0xa3, 0x94, 0xff, + 0xbc, 0xa4, 0x9c, 0xff, 0xc0, 0xaa, 0x9c, 0xff, 0xd6, 0xc5, 0xbc, 0xff, + 0xcc, 0xbe, 0xac, 0xff, 0xd9, 0xcc, 0xbc, 0xff, 0xe2, 0xdc, 0xcc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf7, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0xbc, 0xb1, 0xb4, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0xa1, 0x99, 0x9c, 0xff, + 0xac, 0xa9, 0xac, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xec, 0xea, 0xec, 0xff, + 0xe8, 0xe7, 0xe4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xc2, 0xc2, 0xbc, 0xff, + 0xa3, 0xa3, 0xa4, 0xff, 0xb1, 0xaa, 0xa4, 0xff, 0xb3, 0xae, 0xac, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xd9, 0xd4, 0xcc, 0xff, 0xe8, 0xe7, 0xe4, 0xff, + 0xd9, 0xd4, 0xcc, 0xff, 0xd7, 0xcc, 0xc4, 0xff, 0xbc, 0xb6, 0xac, 0xff, + 0xb0, 0xa3, 0x9c, 0xff, 0xc4, 0xb2, 0xac, 0xff, 0xbc, 0xa4, 0x9c, 0xff, + 0xa4, 0x85, 0x84, 0xff, 0x9e, 0x8b, 0x84, 0xff, 0xa7, 0x94, 0x84, 0xff, + 0x9c, 0x8e, 0x7c, 0xff, 0xa9, 0x9b, 0x8c, 0xff, 0xb4, 0xaa, 0x94, 0xff, + 0xbc, 0xb6, 0xac, 0xff, 0xda, 0xd4, 0xc4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0xba, 0xb6, 0xb4, 0xff, 0xa4, 0x9e, 0xa4, 0xff, 0x89, 0x86, 0x84, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x88, 0x8a, 0x84, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0x89, 0x86, 0x84, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0x83, 0x82, 0x84, 0xff, 0x94, 0x8e, 0x94, 0xff, + 0xd7, 0xd6, 0xd4, 0xff, 0xf4, 0xf2, 0xf4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xe4, 0xe1, 0xe4, 0xff, 0xd8, 0xdc, 0xd4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xd6, 0xd2, 0xd4, 0xff, + 0xa1, 0x99, 0x9c, 0xff, 0xb3, 0xae, 0xac, 0xff, 0xbc, 0xb6, 0xac, 0xff, + 0xb1, 0xaa, 0xa4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xe4, 0xe2, 0xd8, 0xff, + 0xf2, 0xf0, 0xec, 0xff, 0xd8, 0xdc, 0xd4, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0xcc, 0xbd, 0xbc, 0xff, 0xef, 0xe2, 0xdc, 0xff, 0xc6, 0xb1, 0xb4, 0xff, + 0xac, 0x8d, 0x8c, 0xff, 0xac, 0x8d, 0x84, 0xff, 0xa4, 0x89, 0x7c, 0xff, + 0x9c, 0x88, 0x7c, 0xff, 0x9c, 0x8e, 0x7c, 0xff, 0x9c, 0x8e, 0x7c, 0xff, + 0xb1, 0xa3, 0x94, 0xff, 0xb1, 0xa3, 0x94, 0xff, 0xb4, 0xae, 0x9c, 0xff, + 0xd9, 0xcc, 0xbc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x81, 0x7e, 0x7c, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xa1, 0x99, 0x9c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x7b, 0x7a, 0x7c, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0x88, 0x8a, 0x84, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x83, 0x82, 0x84, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0x9c, 0x9a, 0x94, 0xff, 0x89, 0x86, 0x84, 0xff, 0x71, 0x72, 0x6c, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xe8, 0xe7, 0xe4, 0xff, 0xec, 0xea, 0xec, 0xff, + 0xe8, 0xe7, 0xe4, 0xff, 0xe4, 0xe1, 0xe4, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xc4, 0xbe, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xc4, 0xc3, 0xc4, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0x9c, 0x9a, 0x94, 0xff, 0xac, 0xa9, 0xac, 0xff, 0xd7, 0xd6, 0xd4, 0xff, + 0xf2, 0xf0, 0xec, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xcc, 0xc2, 0xbc, 0xff, 0xdc, 0xba, 0xbc, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0xa4, 0x85, 0x84, 0xff, 0x9c, 0x88, 0x7c, 0xff, + 0x9c, 0x88, 0x7c, 0xff, 0x9c, 0x88, 0x7c, 0xff, 0x8f, 0x86, 0x7c, 0xff, + 0xa4, 0x86, 0x74, 0xff, 0x9c, 0x88, 0x7c, 0xff, 0xa8, 0x93, 0x8c, 0xff, + 0xb1, 0xa3, 0x94, 0xff, 0xb4, 0xae, 0x9c, 0xff, 0xe2, 0xdc, 0xcc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x83, 0x82, 0x84, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0x9a, 0x94, 0x94, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0x7b, 0x7a, 0x7c, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x94, 0x8e, 0x94, 0xff, 0xba, 0xb6, 0xb4, 0xff, 0xcc, 0xcc, 0xcc, 0xff, + 0xe8, 0xe7, 0xe4, 0xff, 0xd7, 0xd6, 0xd4, 0xff, 0xdc, 0xd6, 0xdc, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xba, 0xb6, 0xb4, 0xff, + 0xd9, 0xd4, 0xcc, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0xa1, 0x9e, 0x9c, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xf4, 0xea, 0xe4, 0xff, 0xbc, 0xb0, 0xac, 0xff, + 0xbc, 0xaa, 0xac, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xa8, 0x93, 0x8c, 0xff, + 0xa4, 0x85, 0x84, 0xff, 0xa4, 0x85, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0xbc, 0xa4, 0x9c, 0xff, 0xbf, 0xaa, 0xa4, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xac, 0x8d, 0x84, 0xff, 0x9c, 0x88, 0x7c, 0xff, + 0x90, 0x7f, 0x74, 0xff, 0xac, 0x8d, 0x84, 0xff, 0xba, 0xb0, 0xa4, 0xff, + 0xf4, 0xe9, 0xd4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf7, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x7b, 0x7a, 0x7c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x71, 0x72, 0x74, 0xff, + 0x88, 0x8a, 0x84, 0xff, 0x89, 0x86, 0x84, 0xff, 0x7b, 0x7a, 0x7c, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x78, 0x77, 0x74, 0xff, 0xa3, 0xa3, 0xa4, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xac, 0xa9, 0xac, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0x8b, 0x8b, 0x8c, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, + 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xdc, 0xdc, 0xdc, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xbc, 0xb9, 0xbc, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0xdc, 0xdc, 0xdc, 0xff, 0xd6, 0xd2, 0xd4, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xbc, 0xb0, 0xac, 0xff, 0xa6, 0x95, 0x94, 0xff, 0x96, 0x86, 0x8c, 0xff, + 0x9e, 0x8b, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xa4, 0x85, 0x84, 0xff, + 0xa4, 0x85, 0x84, 0xff, 0xa8, 0x93, 0x8c, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xbd, 0xa6, 0xa4, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xa6, 0x95, 0x94, 0xff, + 0xa4, 0x85, 0x84, 0xff, 0x8f, 0x86, 0x7c, 0xff, 0xa7, 0x94, 0x84, 0xff, + 0xba, 0xb0, 0xa4, 0xff, 0xe4, 0xe2, 0xd8, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf7, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0x63, 0x6a, 0x64, 0xff, + 0x71, 0x72, 0x74, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x6a, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x7b, 0x7a, 0x7c, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0x9c, 0x9a, 0x94, 0xff, + 0x71, 0x72, 0x74, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xc8, 0xca, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0xc2, 0xbe, 0xbc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xe4, 0xe1, 0xe4, 0xff, 0xd4, 0xc6, 0xc4, 0xff, 0xd5, 0xcd, 0xcc, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xa8, 0x93, 0x8c, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x9c, 0x7a, 0x7c, 0xff, 0xa4, 0x85, 0x84, 0xff, 0xa4, 0x85, 0x84, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0xb4, 0x96, 0x94, 0xff, + 0xbd, 0xa6, 0xa4, 0xff, 0xc5, 0xb7, 0xb4, 0xff, 0xbc, 0xa4, 0x9c, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0x9c, 0x88, 0x7c, 0xff, 0x9c, 0x88, 0x7c, 0xff, + 0x9c, 0x92, 0x84, 0xff, 0xc7, 0xb8, 0xac, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x50, 0x4e, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, 0x63, 0x6a, 0x64, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0xa1, 0xa2, 0x9c, 0xff, 0xb4, 0xb2, 0xb4, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xc2, 0xc2, 0xbc, 0xff, 0x83, 0x82, 0x84, 0xff, + 0x7b, 0x7a, 0x7c, 0xff, 0xa1, 0x9e, 0x9c, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0xdc, 0xd6, 0xdc, 0xff, 0xd4, 0xc6, 0xc4, 0xff, 0xd4, 0xc6, 0xc4, 0xff, + 0xc6, 0xb1, 0xb4, 0xff, 0xe6, 0xd6, 0xd6, 0xff, 0xcc, 0xbe, 0xc4, 0xff, + 0xb4, 0x96, 0x94, 0xff, 0xa4, 0x85, 0x84, 0xff, 0x9c, 0x7a, 0x74, 0xff, + 0x9c, 0x7a, 0x7c, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0xa6, 0x95, 0x94, 0xff, 0xb0, 0xa3, 0x9c, 0xff, + 0xad, 0x9e, 0x9e, 0xff, 0xa8, 0x93, 0x8c, 0xff, 0x90, 0x7f, 0x74, 0xff, + 0x9c, 0x81, 0x74, 0xff, 0xa9, 0x9b, 0x8c, 0xff, 0xd8, 0xdc, 0xd4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, 0xc4, 0xc3, 0xc4, 0xff, + 0x9a, 0x94, 0x94, 0xff, 0xb4, 0xb2, 0xb4, 0xff, 0x71, 0x72, 0x74, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x89, 0x86, 0x84, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0xbc, 0xb9, 0xbc, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xc2, 0xbe, 0xbc, 0xff, + 0xa6, 0x95, 0x94, 0xff, 0xd4, 0xc6, 0xc4, 0xff, 0xe4, 0xd2, 0xd4, 0xff, + 0xb4, 0x9b, 0x94, 0xff, 0x96, 0x86, 0x8c, 0xff, 0x9c, 0x7a, 0x7c, 0xff, + 0x9c, 0x7a, 0x74, 0xff, 0x8e, 0x7a, 0x7c, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0xb4, 0x9b, 0x94, 0xff, 0xad, 0x9e, 0x9e, 0xff, + 0xba, 0xb0, 0xa4, 0xff, 0xbf, 0xaa, 0xa4, 0xff, 0xa8, 0x93, 0x8c, 0xff, + 0x84, 0x7e, 0x74, 0xff, 0x90, 0x7f, 0x74, 0xff, 0xa9, 0x9b, 0x8c, 0xff, + 0xd9, 0xc6, 0xb4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x6a, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0xa1, 0x9e, 0x9c, 0xff, + 0x81, 0x7e, 0x7c, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x83, 0x82, 0x84, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0xb4, 0xb2, 0xb4, 0xff, 0xcc, 0xc5, 0xc4, 0xff, 0xac, 0xa9, 0xac, 0xff, + 0x7b, 0x7a, 0x7c, 0xff, 0xbf, 0xaa, 0xa4, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x9c, 0x7a, 0x74, 0xff, 0x8e, 0x72, 0x74, 0xff, + 0x9c, 0x7a, 0x7c, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x96, 0x85, 0x84, 0xff, 0xa8, 0x93, 0x8c, 0xff, 0xa8, 0x9b, 0x94, 0xff, + 0xae, 0xa4, 0xa4, 0xff, 0xbf, 0xaa, 0xa4, 0xff, 0xa8, 0x93, 0x8c, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x8a, 0x76, 0x6c, 0xff, 0x84, 0x7e, 0x74, 0xff, + 0xa9, 0x9b, 0x8c, 0xff, 0xc9, 0xc0, 0xb4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x83, 0x82, 0x84, 0xff, + 0xb3, 0xae, 0xac, 0xff, 0x9c, 0x9a, 0x94, 0xff, 0xa3, 0xa3, 0xa4, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x9c, 0x93, 0x8c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x81, 0x72, 0x74, 0xff, 0x7f, 0x72, 0x6c, 0xff, + 0x8f, 0x7f, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x96, 0x85, 0x84, 0xff, 0xa4, 0x85, 0x84, 0xff, + 0xa8, 0x93, 0x8c, 0xff, 0xb4, 0x9b, 0x94, 0xff, 0xa8, 0x93, 0x8c, 0xff, + 0x7f, 0x72, 0x6c, 0xff, 0x8c, 0x6e, 0x64, 0xff, 0x90, 0x7f, 0x74, 0xff, + 0xa7, 0x94, 0x84, 0xff, 0xb0, 0xa3, 0x9c, 0xff, 0xe4, 0xd2, 0xc4, 0xff, + 0xf2, 0xe9, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x49, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x64, 0x6a, 0x6c, 0xff, 0x92, 0x92, 0x8c, 0xff, 0x64, 0x6a, 0x6c, 0xff, + 0x83, 0x82, 0x84, 0xff, 0x7b, 0x7a, 0x7c, 0xff, 0x8b, 0x8b, 0x8c, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x7f, 0x72, 0x6c, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x8e, 0x72, 0x74, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x96, 0x85, 0x84, 0xff, 0x9c, 0x80, 0x7c, 0xff, + 0x96, 0x85, 0x84, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0x9e, 0x8b, 0x84, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x9c, 0x92, 0x84, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0xa7, 0x94, 0x84, 0xff, 0xb1, 0xa3, 0x94, 0xff, + 0xda, 0xd4, 0xc4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf3, 0xf2, 0xdc, 0xff, 0xf3, 0xf2, 0xdc, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x64, 0x4e, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x71, 0x6a, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, 0x71, 0x72, 0x6c, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x81, 0x72, 0x74, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x96, 0x86, 0x8c, 0xff, 0xad, 0x9a, 0x9c, 0xff, + 0xa4, 0x85, 0x84, 0xff, 0xa4, 0x85, 0x84, 0xff, 0x8d, 0x72, 0x6b, 0xff, + 0x78, 0x66, 0x64, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x9c, 0x7a, 0x74, 0xff, 0xa4, 0x89, 0x7c, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0xe2, 0xdc, 0xcc, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x64, 0x6a, 0x6c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x58, 0x49, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x81, 0x7e, 0x7c, 0xff, 0x78, 0x66, 0x64, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x9d, 0x8d, 0x8c, 0xff, 0xbc, 0xaa, 0xac, 0xff, + 0xac, 0x8d, 0x8c, 0xff, 0x96, 0x86, 0x8c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x75, 0x62, 0x64, 0xff, 0x84, 0x77, 0x74, 0xff, 0xbd, 0xa6, 0xa4, 0xff, + 0x9c, 0x80, 0x7c, 0xff, 0x8e, 0x79, 0x74, 0xff, 0x9c, 0x7a, 0x74, 0xff, + 0x9c, 0x88, 0x7c, 0xff, 0xa8, 0x9b, 0x94, 0xff, 0xe4, 0xe2, 0xd8, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0xf2, 0xe9, 0xdc, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x6b, 0x56, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x70, 0x66, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x77, 0x66, 0x6c, 0xff, 0x7e, 0x6d, 0x6c, 0xff, 0x84, 0x79, 0x7c, 0xff, + 0x96, 0x86, 0x8c, 0xff, 0xac, 0x8d, 0x84, 0xff, 0x8e, 0x72, 0x74, 0xff, + 0x7f, 0x6a, 0x64, 0xff, 0xad, 0x9e, 0x9e, 0xff, 0xba, 0xb0, 0xa4, 0xff, + 0xb4, 0x9b, 0x94, 0xff, 0x8e, 0x72, 0x74, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x8e, 0x79, 0x74, 0xff, 0x84, 0x7e, 0x74, 0xff, 0xda, 0xd4, 0xc4, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0xf4, 0xf8, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x53, 0x41, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x78, 0x66, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x6c, 0x61, 0x64, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0xa8, 0x93, 0x8c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, + 0x72, 0x62, 0x5c, 0xff, 0x7f, 0x72, 0x6c, 0xff, 0x9d, 0x8d, 0x8c, 0xff, + 0xa8, 0x9b, 0x94, 0xff, 0x9c, 0x80, 0x7c, 0xff, 0x8e, 0x79, 0x74, 0xff, + 0x7f, 0x72, 0x6c, 0xff, 0x8f, 0x86, 0x7c, 0xff, 0xb1, 0xaa, 0xa4, 0xff, + 0xe4, 0xe2, 0xd8, 0xff, 0xf4, 0xf2, 0xe4, 0xff, 0xf3, 0xf2, 0xdc, 0xff, + 0xf4, 0xf2, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x64, 0x4e, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x6b, 0x56, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x77, 0x66, 0x6c, 0xff, + 0x7e, 0x6d, 0x6c, 0xff, 0x8f, 0x7f, 0x7c, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x71, 0x6a, 0x64, 0xff, 0x7e, 0x6d, 0x6c, 0xff, + 0x9d, 0x8d, 0x8c, 0xff, 0x84, 0x79, 0x7c, 0xff, 0x7f, 0x72, 0x6c, 0xff, + 0x7f, 0x72, 0x6c, 0xff, 0x7c, 0x72, 0x64, 0xff, 0x94, 0x8b, 0x84, 0xff, + 0xda, 0xd4, 0xc4, 0xff, 0xf4, 0xea, 0xe4, 0xff, 0xf2, 0xe9, 0xdc, 0xff, + 0xf4, 0xea, 0xe4, 0xff, 0xf4, 0xf8, 0xe4, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x42, 0x42, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x6f, 0x6c, 0x6c, 0xff, 0x78, 0x66, 0x64, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x63, 0x66, 0x64, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x8c, 0x81, 0x84, 0xff, 0x81, 0x72, 0x74, 0xff, 0x81, 0x72, 0x74, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x63, 0x6a, 0x64, 0xff, 0x84, 0x7e, 0x74, 0xff, + 0xc2, 0xbe, 0xbc, 0xff, 0xe4, 0xe2, 0xd8, 0xff, 0xc9, 0xc0, 0xb4, 0xff, + 0xf4, 0xea, 0xe4, 0xff, 0xf4, 0xf2, 0xe4, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x42, 0x42, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x69, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x70, 0x5e, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x63, 0x66, 0x64, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x81, 0x72, 0x74, 0xff, 0x84, 0x77, 0x74, 0xff, 0x77, 0x66, 0x6c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x71, 0x6a, 0x64, 0xff, 0x71, 0x72, 0x6c, 0xff, + 0xb1, 0xaa, 0xa4, 0xff, 0xd6, 0xc5, 0xbc, 0xff, 0x9a, 0x94, 0x94, 0xff, + 0xd9, 0xd4, 0xcc, 0xff, 0xe4, 0xe2, 0xd8, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x6b, 0x56, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x6c, 0x61, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x6c, 0x61, 0x64, 0xff, + 0x6c, 0x66, 0x6c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x71, 0x6a, 0x64, 0xff, 0x6f, 0x6c, 0x6c, 0xff, + 0x84, 0x77, 0x74, 0xff, 0x89, 0x86, 0x84, 0xff, 0x8f, 0x86, 0x7c, 0xff, + 0xcc, 0xc2, 0xbc, 0xff, 0xe4, 0xe2, 0xd8, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x34, 0x2e, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x6f, 0x6c, 0x6c, 0xff, 0x71, 0x6a, 0x64, 0xff, + 0x71, 0x6a, 0x64, 0xff, 0x70, 0x66, 0x5c, 0xff, 0x72, 0x62, 0x5c, 0xff, + 0x88, 0x8a, 0x84, 0xff, 0xa8, 0x9b, 0x94, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x5f, 0x62, 0x64, 0xff, 0x5f, 0x62, 0x64, 0xff, 0x5d, 0x62, 0x5c, 0xff, + 0x63, 0x66, 0x64, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x72, 0x5b, 0x54, 0xff, + 0x78, 0x77, 0x74, 0xff, 0x8f, 0x86, 0x7c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x5d, 0x62, 0x5c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x70, 0x5e, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x70, 0x66, 0x5c, 0xff, 0x63, 0x66, 0x64, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x6b, 0x56, 0x5c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4e, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x4e, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x64, 0x5d, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x69, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x2e, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x64, 0x5d, 0x5c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x30, 0x2a, 0x24, 0xff, 0x2c, 0x26, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x64, 0x4e, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x58, 0x49, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x2c, 0x26, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x41, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x69, 0x55, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x50, 0x4e, 0x44, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x58, 0x49, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x50, 0x4e, 0x44, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x50, 0x4e, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x2e, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x46, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x53, 0x41, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x5d, 0x5a, 0x54, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x64, 0x4e, 0x54, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x53, 0x41, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x64, 0x4e, 0x54, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x50, 0x4e, 0x44, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x50, 0x4e, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x44, 0x46, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x5d, 0x5a, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x50, 0x4e, 0x44, 0xff, 0x44, 0x46, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, 0x53, 0x5a, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, 0x3c, 0x32, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x64, 0x5d, 0x5c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x34, 0x2e, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x5f, 0x62, 0x64, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x46, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x50, 0x4e, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x64, 0x4e, 0x54, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x44, 0x46, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x53, 0x5a, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x2e, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x30, 0x2a, 0x24, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x53, 0x41, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x46, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x46, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x2a, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x30, 0x2a, 0x24, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x46, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x44, 0x46, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x30, 0x2a, 0x24, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, + 0x3c, 0x32, 0x2c, 0xff, 0x34, 0x2e, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x4e, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x53, 0x41, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x53, 0x41, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x4c, 0x52, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x58, 0x5c, 0x5c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x34, 0x2e, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x2c, 0x26, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x32, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x4e, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x58, 0x49, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x53, 0x41, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x2e, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x64, 0x4e, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x2c, 0x26, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x46, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x32, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x55, 0x52, 0x4c, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x5d, 0x5a, 0x54, 0xff, 0x6b, 0x56, 0x5c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x53, 0x41, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x52, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x4e, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x2e, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x32, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, 0x3c, 0x32, 0x2c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x5c, 0x3e, 0x34, 0xff, 0x3c, 0x32, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x49, 0x4c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x53, 0x41, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x5d, 0x62, 0x5c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x34, 0x2e, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x2c, 0x26, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x2c, 0x26, 0x2c, 0xff, 0x44, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x2c, 0x26, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x58, 0x5c, 0x5c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x4e, 0x4c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x2c, 0x26, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x30, 0x2a, 0x24, 0xff, 0x2c, 0x26, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x30, 0x26, 0x24, 0xff, 0x2c, 0x26, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x44, 0x47, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x55, 0x54, 0xff, 0x58, 0x49, 0x4c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x55, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x53, 0x5a, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x30, 0x26, 0x24, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x55, 0x4e, 0x4c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x2c, 0x26, 0x2c, 0xff, + 0x2c, 0x26, 0x2c, 0xff, 0x2c, 0x26, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x2c, 0x26, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x30, 0x2a, 0x24, 0xff, 0x34, 0x2e, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x2e, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x4e, 0x42, 0x44, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x30, 0x2a, 0x24, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x2e, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x4e, 0x42, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x34, 0x32, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x3a, 0x3c, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x49, 0x4a, 0x4c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x30, 0x26, 0x24, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x4c, 0x47, 0x44, 0xff, 0x4c, 0x53, 0x54, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x2c, 0x26, 0x2c, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x2c, 0x26, 0x2c, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x55, 0x55, 0x54, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x44, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x42, 0x42, 0x44, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, + 0x30, 0x2a, 0x24, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3b, 0x3a, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x53, 0x54, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x34, 0x33, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x33, 0x34, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x34, 0x33, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x2e, 0x2c, 0xff, 0x3c, 0x2e, 0x2c, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x30, 0x26, 0x24, 0xff, 0x30, 0x26, 0x24, 0xff, + 0x3c, 0x2e, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x30, 0x2a, 0x24, 0xff, 0x33, 0x2c, 0x2c, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x55, 0x52, 0x4c, 0xff, 0x55, 0x52, 0x4c, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x3c, 0x32, 0x2c, 0xff, 0x33, 0x2c, 0x2c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x4c, 0x53, 0x54, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4c, 0x52, 0x4c, 0xff, + 0x4b, 0x4e, 0x4c, 0xff, 0x55, 0x55, 0x54, 0xff, 0x4b, 0x4e, 0x4c, 0xff, + 0x49, 0x4a, 0x4c, 0xff, 0x34, 0x32, 0x2c, 0xff, 0x34, 0x32, 0x2c, 0xff, + 0x33, 0x2c, 0x2c, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x4e, 0x42, 0x44, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x34, 0x2e, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3c, 0x35, 0x34, 0xff, 0x3b, 0x3a, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x34, 0xff, 0x44, 0x3a, 0x3c, 0xff, 0x42, 0x42, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, 0x49, 0x4a, 0x4c, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x3c, 0x35, 0x34, 0xff, 0x3c, 0x35, 0x34, 0xff, + 0x3b, 0x3a, 0x3c, 0xff, 0x40, 0x3f, 0x3c, 0xff, 0x44, 0x47, 0x44, 0xff, + 0x44, 0x47, 0x44, 0xff, 0x4c, 0x47, 0x44, 0xff, +}; + +#endif // WEBP_TESTS_FUZZER_IMG_PEAK_H_ diff --git a/internal/libwebp-1.2.1/tests/fuzzer/makefile.unix b/internal/libwebp-1.2.1/tests/fuzzer/makefile.unix new file mode 100644 index 0000000..e242563 --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/makefile.unix @@ -0,0 +1,30 @@ +# This Makefile will compile all fuzzing targets. It doesn't check tool +# requirements and paths may need to be updated depending on your environment. +# Note a clang 6+ toolchain is assumed for use of -fsanitize=fuzzer. + +CC = clang +CXX = clang++ +CFLAGS = -fsanitize=fuzzer -I../../src -I../.. -Wall -Wextra +CXXFLAGS = $(CFLAGS) +LDFLAGS = -fsanitize=fuzzer +LDLIBS = ../../src/mux/libwebpmux.a ../../src/demux/libwebpdemux.a +LDLIBS += ../../src/libwebp.a ../../imageio/libimageio_util.a + +FUZZERS = advanced_api_fuzzer animation_api_fuzzer animencoder_fuzzer +FUZZERS += animdecoder_fuzzer mux_demux_api_fuzzer enc_dec_fuzzer +FUZZERS += simple_api_fuzzer + +%.o: fuzz_utils.h img_alpha.h img_grid.h img_peak.h +all: $(FUZZERS) + +define FUZZER_template +$(1): $$(addsuffix .o, $(1)) $(LDLIBS) +OBJS += $$(addsuffix .o, $(1)) +endef + +$(foreach fuzzer, $(FUZZERS), $(eval $(call FUZZER_template, $(fuzzer)))) + +clean: + $(RM) $(FUZZERS) $(OBJS) + +.PHONY: all clean diff --git a/internal/libwebp-1.2.1/tests/fuzzer/mux_demux_api_fuzzer.c b/internal/libwebp-1.2.1/tests/fuzzer/mux_demux_api_fuzzer.c new file mode 100644 index 0000000..4ed0142 --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/mux_demux_api_fuzzer.c @@ -0,0 +1,96 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "./fuzz_utils.h" +#include "src/webp/demux.h" +#include "src/webp/mux.h" + +int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { + WebPData webp_data; + WebPDataInit(&webp_data); + webp_data.size = size; + webp_data.bytes = data; + + // Extracted chunks and frames are not processed or decoded, + // which is already covered extensively by the other fuzz targets. + + if (size & 1) { + // Mux API + WebPMux* mux = WebPMuxCreate(&webp_data, size & 2); + if (!mux) return 0; + + WebPData chunk; + WebPMuxGetChunk(mux, "EXIF", &chunk); + WebPMuxGetChunk(mux, "ICCP", &chunk); + WebPMuxGetChunk(mux, "FUZZ", &chunk); // unknown + + uint32_t flags; + WebPMuxGetFeatures(mux, &flags); + + WebPMuxAnimParams params; + WebPMuxGetAnimationParams(mux, ¶ms); + + WebPMuxError status; + WebPMuxFrameInfo info; + for (int i = 0; i < kFuzzFrameLimit; i++) { + status = WebPMuxGetFrame(mux, i + 1, &info); + if (status == WEBP_MUX_NOT_FOUND) { + break; + } else if (status == WEBP_MUX_OK) { + WebPDataClear(&info.bitstream); + } + } + + WebPMuxDelete(mux); + } else { + // Demux API + WebPDemuxer* demux; + if (size & 2) { + WebPDemuxState state; + demux = WebPDemuxPartial(&webp_data, &state); + if (state < WEBP_DEMUX_PARSED_HEADER) { + WebPDemuxDelete(demux); + return 0; + } + } else { + demux = WebPDemux(&webp_data); + if (!demux) return 0; + } + + WebPChunkIterator chunk_iter; + if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) { + WebPDemuxNextChunk(&chunk_iter); + } + WebPDemuxReleaseChunkIterator(&chunk_iter); + if (WebPDemuxGetChunk(demux, "ICCP", 0, &chunk_iter)) { // 0 == last + WebPDemuxPrevChunk(&chunk_iter); + } + WebPDemuxReleaseChunkIterator(&chunk_iter); + // Skips FUZZ because the Demux API has no concept of (un)known chunks. + + WebPIterator iter; + if (WebPDemuxGetFrame(demux, 1, &iter)) { + for (int i = 1; i < kFuzzFrameLimit; i++) { + if (!WebPDemuxNextFrame(&iter)) break; + } + } + + WebPDemuxReleaseIterator(&iter); + WebPDemuxDelete(demux); + } + + return 0; +} diff --git a/internal/libwebp-1.2.1/tests/fuzzer/simple_api_fuzzer.c b/internal/libwebp-1.2.1/tests/fuzzer/simple_api_fuzzer.c new file mode 100644 index 0000000..7d2b7f8 --- /dev/null +++ b/internal/libwebp-1.2.1/tests/fuzzer/simple_api_fuzzer.c @@ -0,0 +1,88 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "./fuzz_utils.h" +#include "src/webp/decode.h" + +int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) { + int w, h; + if (!WebPGetInfo(data, size, &w, &h)) return 0; + if ((size_t)w * h > kFuzzPxLimit) return 0; + + const uint8_t value = FuzzHash(data, size); + uint8_t* buf = NULL; + + // For *Into functions, which decode into an external buffer, an + // intentionally too small buffer can be given with low probability. + if (value < 0x16) { + buf = WebPDecodeRGBA(data, size, &w, &h); + } else if (value < 0x2b) { + buf = WebPDecodeBGRA(data, size, &w, &h); +#if !defined(WEBP_REDUCE_CSP) + } else if (value < 0x40) { + buf = WebPDecodeARGB(data, size, &w, &h); + } else if (value < 0x55) { + buf = WebPDecodeRGB(data, size, &w, &h); + } else if (value < 0x6a) { + buf = WebPDecodeBGR(data, size, &w, &h); +#endif // !defined(WEBP_REDUCE_CSP) + } else if (value < 0x7f) { + uint8_t *u, *v; + int stride, uv_stride; + buf = WebPDecodeYUV(data, size, &w, &h, &u, &v, &stride, &uv_stride); + } else if (value < 0xe8) { + const int stride = (value < 0xbe ? 4 : 3) * w; + size_t buf_size = stride * h; + if (value % 0x10 == 0) buf_size--; + uint8_t* const ext_buf = (uint8_t*)malloc(buf_size); + if (value < 0x94) { + WebPDecodeRGBAInto(data, size, ext_buf, buf_size, stride); +#if !defined(WEBP_REDUCE_CSP) + } else if (value < 0xa9) { + WebPDecodeARGBInto(data, size, ext_buf, buf_size, stride); + } else if (value < 0xbe) { + WebPDecodeBGRInto(data, size, ext_buf, buf_size, stride); + } else if (value < 0xd3) { + WebPDecodeRGBInto(data, size, ext_buf, buf_size, stride); +#endif // !defined(WEBP_REDUCE_CSP) + } else { + WebPDecodeBGRAInto(data, size, ext_buf, buf_size, stride); + } + free(ext_buf); + } else { + size_t luma_size = w * h; + const int uv_stride = (w + 1) / 2; + size_t u_size = uv_stride * (h + 1) / 2; + size_t v_size = uv_stride * (h + 1) / 2; + if (value % 0x10 == 0) { + if (size & 1) luma_size--; + if (size & 2) u_size--; + if (size & 4) v_size--; + } + uint8_t* const luma_buf = (uint8_t*)malloc(luma_size); + uint8_t* const u_buf = (uint8_t*)malloc(u_size); + uint8_t* const v_buf = (uint8_t*)malloc(v_size); + WebPDecodeYUVInto(data, size, luma_buf, luma_size, w /* luma_stride */, + u_buf, u_size, uv_stride, v_buf, v_size, uv_stride); + free(luma_buf); + free(u_buf); + free(v_buf); + } + + if (buf) WebPFree(buf); + + return 0; +} diff --git a/internal/libwebp-1.2.1/webp_js/index.html b/internal/libwebp-1.2.1/webp_js/index.html new file mode 100644 index 0000000..10873a9 --- /dev/null +++ b/internal/libwebp-1.2.1/webp_js/index.html @@ -0,0 +1,75 @@ + + + + + + simple Javascript WebP decoding demo + + + + + + +

+ WebP in JavaScript demo - +

+

+ WebP decoder in JavaScript, using libwebp compiled with + Emscripten. +

+

+ +

+

Timing: N/A

+ Your browser does not support canvas + + + diff --git a/internal/libwebp-1.2.1/webp_js/index_wasm.html b/internal/libwebp-1.2.1/webp_js/index_wasm.html new file mode 100644 index 0000000..b77c22c --- /dev/null +++ b/internal/libwebp-1.2.1/webp_js/index_wasm.html @@ -0,0 +1,90 @@ + + + + + + simple Javascript WebP decoding demo, using Web-Assembly (WASM) + + + + + +

+ WebP demo using Web-Assembly - +

+

+ WASM version of the WebP decoder, using libwebp compiled with + Emscripten. +

+

+ +

+

Timing: N/A

+ Your browser does not support canvas + + diff --git a/internal/libwebp-1.2.1/webp_js/test_webp_js.webp b/internal/libwebp-1.2.1/webp_js/test_webp_js.webp new file mode 100644 index 0000000000000000000000000000000000000000..f798f5563e4fa21e8784067ed1a0cd5d81239c3b GIT binary patch literal 1321542 zcmX`ScUTkM7cD#qA%p-Ds-Xx0LI5|kh$h{V_WdWM_m4DwAZwwA8u9s>)ZVh=zM3Qvn(eyeTRUmz-EFd5G6<^e`gTC! zMA%(t`9%f96En}Q=ZL$4e}nnXJKp`M{0~O*F93-cwW|8*q2*u19q9DJf17^17wxo#y9X_;2ihrrYYYxC zDnAxCq}5N7K3-nPG`3$9PimV5HM?)T9~+TuSk^PA!_?KDJ@>uKcUhbu`tD#yV*Xz7 zT~|5hTcU*T#X5L;1G4siecQff568?rr_rsf2YC0({0&J_K0Hu5#hdpB(v>hi+E}Y5 zF`QQxTSlDdA&IxZ|Kh_Sbo0`3X=gP9RQg4p<^r>%lrDkU+ECpKsyiy_jv}ZS}fP_OXs}mYj=Yto@N$p4OQk z;F6!ERyM6tk%2ZJd*FRv^FYgkQw@;}k*|qFbu^0k8|0I>W$}>ajK3~vKn$4JvS=LO z3e@tP#oXs4ij#VoaqlWyTZBt~p4A_;sF>(4aD7_N-WfTTS!_^=A2n68^lCilL+_V1 zA5CQLM?Tvv;{w1zVd4sDpb9tT@y~8z+T$)y=PA#0* zSRAv?V0EFspP6v5RVM6qzqPDvUTgBbO3nE}z8ZRaYvoa}tyH%4z4yX>;B;Nze49^&1BhRF}3&?GreMJ zQn?wA1Nf7z=NoV)Ll@TxKb}~PXnfEvp@oN74YUP?M}1kSRGVzT&F$Z6?OCrQ`+(3< z{g9}mT4Zqk>-sv!^PUbY9~+MzOhP-%u=p+CCGCHtrvg?MO>zD^ZnDdFM{3Hk-lyV( zHR!U3^7H(po3kJP&_As(<1Tf~Hkg_+j?*an{I(4oBU4&XUl7FX4I6#pZBS|3=SS)}a00hdoDH!B=}OEYE-TyftYkm(6M4{o?TC zzt)4re{oJKQQl4NOY6608I&5%lMw8uLDLPLl{(GkW&mI&;)txx7b{IP~ z>pK`-6;6`Kuq4cy`R+)!oe3tqz1uF5Q?az}vvXQ$>QJ(pd!{w(vS0s%KgID;VDOKq z+?B?*vxSY<9l8s9<*m0o>(z(3@8o{CmjE)~88!3UUTMlbIO+!aWB@D`7+*37bfPc_ zh>gP|3PMOxQHll;SccMPE2N+32WjVbi{M8&{WN`Zxhy7quL_gGQ-f4ehV zk&_dI->4HnOn}4gukmev`zIrg*sHAy$biWpr}ZX29?r>Wa^uoob)29?EB}{ zStBMi(G>f$OvuY*@NHVjy^$NrAB<8?R+>@OC z#&#cupI@)m*F(6w#G*qSa~v@x=>1F`M0kX}d0S6|l!3OfvA77cgsI_-#M0`)x>|$Kk}u>;18Paq+To!g&CfCo?1;d#;lP;)=5*DMd(yG>Ghj-=a?mM*gyc}+Myr(8rX^J?@%TX{g)TU#a?K_tUQ?U6)f7PEJ?Lnw;3iV7mv15%aG zN5LL^T?bg~7y)>GCqzqo5V=M0N7Ca)E6q9bXO#Gq!nx|43~HJ6_0*f)OE#mPukPD8 z7!X-8W8%@03}Jb`PYvhHg~Zb!cdeL07`z*js6t{0tp)^wLaLJFzmWDER2 z@r2sAO2Swa-=NbFcrrr)_?e^?=`C7e&3aRZj5stgxZZ0W_)q+7KqvpF#-BF^@yjN4 z7UV#(5Mv)Ztz+dgH4OwKdFc!teF(qpQj0R3oQ<`}Q>|BkT&eG`-cmiMVf&<{By{KU z#1an^2_{b%1*ifNOJNf6vSaNzTDRUCD{5#tReztE$WyA&FZn%bp~9b1Ghpcw{;}D8 z*!f^6MM3lnAi!lYZD^b)fopMXtg^1(VaR<|2zs5eV2u`g6Ryt0l=(^t@s^B(gQK`~ zuI_VzQf#D<0DV^|D2!a2FGbxS=RnxVac1n_Kxw&-rN4KmH>S|$gQ(j-)z0FPR!v;1 zw-k&!&J6l;zCITfh-iW&4WvGP8mW%PD&ebY>2P*r>`*nGlJ`nxs0y=pW2Yr?6obVr zZNt$*xGtn+Z{HjmhQ-Up4RY}-^6IGf+a5ft>&1KAY)viCuTjf=yEwT zA$vvX9BFPtg{oX&51qAH6A>ALRY3Xla0N5cVJLpmR|p7(0m!n~wzhLDefW!1&B=~C zKRnu)CmG$$qh*4*!}Rbd8Z*T-G;bMpf+2529=+XvZqsgd>$az&FmdedBvfx5?I&hPS3oP=nM zLR~5x=dKX}Zkcoh*>R4J=^4NT)GBEsO4$oc?i85DwafuLu@9LO5?uO^)NWVG-u0Z~ zG|jw}TD<4g_~?gY5H2D|d^a4+I1)t$|!{Hy)( zd+C$F-Qv4hm=YN?zkuHtM?D{&`M9l&Y8ZH=w<`sj&F)D3XJm!*fTv1C6cmirfAB0D z;l6Lx&EJW*V#6C3?2HMGUGIySa8_V2*F^wN&1j<_BT0C@pn zj=*Nwv&mH{Rw!_Xe>Sp<=cCxG(^1M8~^MLLN-;F*>(+B*Ly!!U}@+NV+d?}sR37-o& zlDfQ}tznmR`G`BGh`{yQb9yDZ_70@>m{zY8ske10>P1sJrBi%GTqnAiEv)Nnw^osErS5Jff~Pl4ok~zpn=Do&VCt^Aj+Vo8shO$iQzI?8L9^DD ze*-F0W^YT)!4G93OTCctq(qurfd=~fc`ZT+a5j=VYka1Cx1tHxx#Q?Ru z4m+CIbscecqU%GlLJ>7pw%Bw5xK1&=b|5Y_mC=j1NWUz-NMEqtnETY+QR7HszuCR< zo|KB-e$j&A;+WF&-(QpCxaw&C#Gz?nt|-VFabXIYRHpD&f=1i1L@9qVpA7Zy$rmUid!9pmWc?i}$Dh% z*5+B(%Z0%DR?}LKsrnYb{ey<9rBC$7R*{yTDLN&O>Z=_@9Jpx8rW|qgn{aI>?&hj5 z6FN=~_F<96MK_mD*|cYIm-FntrDP2!zvpLtjf$Hn(s$rzR4+2d5!YEpVIWYjHXpI^ z0t5NMB4vErYjlNvS%ksp54Da3rug1D{T_Yro2bt9;i`$?+Q&75ioKAD`kB2`6ZTNP5ZZCr@AEy+X&Oa{(6O(3mIo4(h5GC4 z=!j>?xzj}zeW0uq-Yk1TwGFAE#d2Q0cO105-0=-9ZT015bjD;!Z7V7hG86vE*gB>H zBO?GB5QUL>dmqZp8S7KoDvMS=qTewX*8b!z-x#gxedvZ^Rm3+xNosB3qiQbYlYOZ< z3fMkSDadVY_UI$8PW4R6Y;7GS?0NT0xAso~_;ij^6Jy1e=D_zN|JBdv{hRMoF3}{V zT3Q;BIj~yep`BoFR5e(h+&D-*3HliGi^HzFzjZ_;x!Koc>Vh1xt?0VSeqUqtu<@j6 z{Mn&W3Tskk?JtkRaU*e2PIt6?j^O#|Qhn+22Q*pwz)0!HU#s9yIey>6(Gm7hfpfj~ zO|IZuOM;>vcFzAI&R3)-yKGbak6VUa!+rlcQD)`2Q8rcxE-tEGEycgS+mX(?r81H_ zU%i38r64TsEquRY&Tw=@bNZ8-Ti#c9>i!pMsRF4ieOE!w?_>X?>9b$lp$x#`zo{mp z)f9W#OKlHWsM7UJO~X1C`-y@rKI0W0!6?gjdys$c5W4o3H!Ry0^F(@jmNoSvVgg8S z>}vjJCs5#cV|Zj{D{dA5v|Wi8fByRV2M8Kgn#CCKTDEhIH*uM$)F3+Ak|}#z=BqDN6$A1Qi&&&)}ICWJiN}nx?6Cc`(-C_*yZ>2-+8s? zW~L0zyK>3s=?2`Q$nn5Tf0ol>Aj?zFV$BgG|QzdfaRSF0Q6>(d{E^%mSw%YN1qZ?OmObK4zbA{a!ERuzfu6lv6F4QjE0f@Zx9RD>aa#|`KfSn}Zv93;|5s$RPmUA$ zVRp?>BU&vLt=6E_Yk81lG~HN#`1bm`_sF;D(7m8gOaO(lLyrYwP*nXRiVLd)#7DNBR2B zgSXYAR?n_FWO~)AhnMx1b8%Pq7hk+9m-JQ3S#9k&H(WF}bE@2V5cTllCZ_6ft)A)? z73adU3s+fW*cE=*XZBs`yk=(X$3XQw>caqY{*gHq8e;&yC|5yJ%BgXchJOIG1<}5($ zVXZ8pAZ+02Eq;ul3=^|7YIu=8>(cIem3n^W<#M!d0u>#;$->Cy4-93o&B+X547XO6 z?_baZgY~#(Pj>d&zW@KR4?nbmGWIr$xDQo-<+@b?=Tj$ zj?ep7h;JkH($`HEou?c22Yz3Fe4}IH)Se)`ZET{WXq~aoVXF?~EyNBG>^cxqmJR_fU{Y$LMFx5T7XC*R*#zdN6C6f6oSZAgkwNcdO3W(ok)4U6`Ck)N0-mprtSZTh{a zqrNP!3W{3jx$~U!h+*z*B^K z@)f8v2;K;aEsn}n7+t{qU3>P-s;TWhn$I4dcbqFKPNUKuyfU)7yLm1m zYR5a=^~T=Wwv>xH=TCaXEX$Jm27WgRw==)NwZfY#}Pj zx`p`k#GMX%Lx_0~W`lDZ&SiUQumdtj#IOFr1W(A6usK+ILbE~LtxOHzO zh;Z&3=W5L(I&2?@3#zkwxZa8zuWK!H97;DTF<0(&k_~cRoekXaaiWl!YuI0CSeC3f zt)6*!-hK_zS>eBJM%7EAI%K2vpcWDW<7HjZK>xhZNSD~Xm%UR0*`K5xq>nFAPouIK^P8vx%1TSN?hA>CttV z-}gMS_ea8g=LhZj2DVKO#afGa-=4?w;x1SJi}CTja~di=QE$p2LV5U*BOmexc(%TX zi1tvq_-`!Jh>D6@c{~yIwv)3|^l55MFL^a^{3Ee{p+Rr40bNqxaTXZKmtNbdZ;ig4 zN&4;2>b#TKYBJg=89h%=6`P^*t>Qn?W7%VuRf1%r7&i#j*EWzz5VuK`<{Q>_}R*^6JuD% zi^WvjBCc@eGVN-K1heIqMMb98agG}LyI5=GjHWXzU}rD*=H@t}xVGFr%e)+hR*Oe3 zHWYO(ag??EkDd~aSjm`0`h1YV0A+UV>~7@{?EV*6{Ni<4!*T=5GJdcr?fw^w)G;cJ z25Z~?bqPZ8gPXFr?;qkauM09C+L{|xNF{XCDyAt~U(o@j(zw7JTluG#yPPH3F zStH19d%8<}b^Ykn{VoF+voli7{Y~6Y7U$8?l@X$tD5slKb}{kC-Cq48)IPF$CH#t0 zqsE5863!igVc2>$GBQ#|aIjkt!x6>b=|{&JU8b!b+5{k7+FS`KUU#4WPslAR>o^6D z1RikO6#0bykOK;qWc4iG2pCbZfpx99I{s*VJ-A8S8-IG;WMQ_kPgzT|!S;`k!ld%U^dF7D_@mEHTe(%l+Seq7|8!?1fH z|4T3Q4hU79=BucTTlIL8GgQ5GXMN|0lbocd!SjSho7WX3Df$i8sSZcJ&?ZI6W#BP*^H?Z=^4ZS86odkNU<`ob zgBBMTPgLgdVUc(uyd;1E=nCQG(5{+hT>y9wnCF3^Yd!(6NB|{?Q$Z0#jB0Cb?ZCio zYeb%eXgWZ_4T9t6smSp$EY>(<=Q`Bq|6+_}{ZHGoP(R#O53jtg#vdJDsa$gg5BqC& ze!Y_jAP9vf`>$W0IJb!kE2s6!kz)#yb4uaEC|Wut8oCtfqGGtP;=urV>T781?$;S} zdNP>bXNrnaMY*@sDfTI7hB49hGJZcn3PS-oS`ZevS()a<1RjnLy@`6EukyrnjO08S zSS@a!wGc8GBf+;51jr*pqb+(rlVpo9LqK3W0JItnM~e%AzL2+8zoVkN1(>4{6C7=- zEYWxGfKchT*Wj>Myf6?E5ib;)FH-#a$HFaDtC_$%oQfgps#Blj>lUk(h2#o zl#JZv>ytF_#EkCVpn6P0LU=H9gp8CqKx>Lc)7~m;SuRT#T2wwnh#V~{+ zZhp-JbYWmD5s%NuM;x@_q1~GU7$v+13G(B$>_nJ~ps@_2fIPoiXwC&Bulpe(tDp$V zY9kZz@?S%4>sbJ~kd-T@XFkZVZ*Xz5A^rM#%Gp=n~V`TSn)e&-8j z-QPqUjy?Z*21Z$?4rf%fZZz(#jQ>5)4JP8TcRKAWWOSs!LV!B962m7ipG z0;kEGQNvAZTD!A8@y|U}nCc}^Jb2f z4gS;c+T}#rO~I!Qv|rroAESc}5eJ}n$A}C>m5vj(3fW6k8sQ9pgScpJsXPYocfz3C z!B*0Lj-Os{JbGK3`hBRQHA?t!{EuhQ#Y)I9>#oY)=Jj8Lf8+O9F_s8o80dnvt%C^j z%`T|s8E~T1KVmNB6JibSu5%p?CIv%5-{l7`qY*$aX+$W1q7%7&8^dM{-AR$R`Abc7p1Os29L64i1yj z6el^g?DcLP7_A|jQXP_9;>OXQlRuNSirxV{PveQD{ze99{ zGkj z+}=ZsgGROP&sOMm!)}9bJ?41(ID*r=n+FR5rRb1T3t%+JLKCfGzv{^dh*N_A5P(9F z6VeN)I3AOEEBieCBqk2%Ab1c+BtQ!Q5PTKJJ{%fN#D)M6;Y4vY(un8^{;AM@rU_lS z@t$wnnS;*nRphNzGFfdY|9kXpPbSn3pv(nFvzj7(z8$pLaQaBYzQHjx#Apl)I;++tFSTl^!trs3aN(@Gk z!a@LhK)#}%wCID+EA}LWoPu1mB2?qsM{E2##Gw)fKp>$A7y|Qi%!(S62ZrH$ryh?~ z+ILMh*pH5)0?i((AqgFmFiMZ9{+vaQ^}W3nn4@i^Z)m6YX++QBrpjKGijH zhR!h7kw}irFkj>6cvkDatiVmX_7K~Fw%i2&gxZgOZ&TZYSUz3VY_muP#P|OhF^PO_ z@SO;OS0FDN8b9LkTQe3*t=KiRT)tB0y*|Vr(I;Sy*(tZW2opkT@f))d7-|9B2W&96@)O+5?nD# z94|LIIhOL_BoBW=h#0sCC0|3qC`Iro@Rv0FbC?-1B-*0x4qOGo!JrBe<1hyUaELe= zj1Of76oV{@03e^S1ct^h|4{x!##dZ}3YQD0Nt>36_biFEgY<@7^{b=HUt1Hu>b%wF7Hn9 znYWL{ltf#kKYU~J3lBvARPZS{ewdah9QY(J2Xq!VYAflH1DLLbV&V!%IXE78RL&(9 zTvjSbS`uOjsQ%Q&!+PH}7HaK6KoZJ4}L7qrQeqRdog@u0VOYH0lLui(|Q|E{60YLF?Xrlt>i zh|`8i)IPjz*su*=rOb{yXZYCnIP0kKv;pqW9Z`i^Wh$F~rRP{(lB6G!Rc1>C#>CJA z#KnNySr;6_E?hPq$5tU&+rT0zu|LqT8nDE`;cy{H;(+x(zhHxosMxfO+#eLB(IvZLpuD9_zadze%N2jRy0X%f4cF2SYH#ric@$7^a>Fi`Ujy8E1O5n9x7^=Za<(0D~%3bREEs z!|uB&P<~oc@a^+6L+m1-CJ3wIs#L9GX zp#X4)3jtURRRop<^fctLLg5Qikjndfw>`eiJnc|%IIMR~$)GIue!Z@Zpa74XFi4Ro zu|ZW#f_w^LrX}8&=Kv5n14zNtcazjH#d-23Y2L$EZmT|Hc8N$KR=|jYR;$P>y)F=LfRY% z=&WOVK_#8hS&sz5660cCP1g#przUnEawbDKT|rfH;i|XT-KTrLX#z?&5F>HPDp{G)IIqKFtto?ITuQHFiJ_NGvkdon9rbV_P&o#}zU)0WU3 z&BR>DJJN+yz!p%|cek7*0n3SS2sidzxa%LmMA--fyQc|}1LM9B}GErpUL?BI^+(U8-0?Q#M6-pIu>W>!Wlen+1mX@*A?J(v? zi^$(JpDPt*wz9tWeXAU`GZZ%<+-;g?FZ7uzPg@V9BMjIR>n_R5wdSF8Yiceez$knK zoL_9owL-8G#6zyzE3_bNZBH!rIav#=1mr=j(K0?=p;UB)#0hG0&<$il=0UR@aT<)gy%d117q)S*-%a8)IH`KNaa z3MAM8D1ZTk@*sQ!z*uxf)oK$H(H+ewQXsDZ+gufc@uCH8MuZec129!pRRA?qLl(A> ze`kyRHUnhQs(3;PGZ@KOq^ozEU!$@TipjGi)=%XJm%-JIwjSxF8tD~NVXKkFcp+c_ zhDo&M1QipfMH;-Wy!FA$9y*NK|pSSRWgwwQ>T zhf#^TK+%d26C-8}GJiUgb5dWDF>%Budv0QPQpG¨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%0AH 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

s;QtfP>{DVWoQTmJ^4G>GtZOMuK`^FWv* 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%0AYYT1GUib;NOfeNbsmV?Y-=*63Xa_Uur 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

lz4u1ohNJZ~tcZOBe+vsF8E0^m$sTsjP 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


6hTOy3>!(mV)o=*fVX@ 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%0Af1Eo6yy2 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

DNUpN(!^ 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

s;QtfP>{DVWoQTmJ^4G>GtZOMuK`^FWv* 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%0AYYT1GUib;NOfeNbsmV?Y-=*63Xa_Uur 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

lz4u1ohNJZ~tcZOBe+vsF8E0^m$sTsjP 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


6hTOy3>!(mV)o=*fVX@ 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%0Af1Eo6yy2 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

DNUpN(!^ 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

O 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[-]'. +PLATFORMS[$IOS]="iPhoneOS-armv7 iPhoneOS-armv7s iPhoneOS-arm64" +PLATFORMS[$IOS_SIMULATOR]="iPhoneSimulator-i386 iPhoneSimulator-x86_64" +PLATFORMS[$MACOS]="MacOSX-x86_64" +PLATFORMS[$MACOS_CATALYST]="MacOSX-Catalyst-x86_64" +if [[ "${XCODE%%.*}" -ge 12 ]]; then + PLATFORMS[$MACOS]+=" MacOSX-arm64" + PLATFORMS[$MACOS_CATALYST]+=" MacOSX-Catalyst-arm64" + PLATFORMS[$IOS_SIMULATOR]+=" iPhoneSimulator-arm64" +elif [[ "${XCODE%%.*}" -eq 11 ]]; then + cat << EOF +WARNING: Xcode 12.0 or higher is required to build targets for +WARNING: Apple Silicon (arm64). The XCFrameworks generated with Xcode 11 will +WARNING: contain libraries for MacOS & Catalyst supporting x86_64 only. +WARNING: The build will continue in 5 seconds... +EOF + sleep 5 +else + echo "Xcode 11.0 or higher is required!" + exit 1 +fi +readonly PLATFORMS +readonly SRCDIR=$(dirname $0) +readonly TOPDIR=$(pwd) +readonly BUILDDIR="${TOPDIR}/xcframeworkbuild" +readonly TARGETDIR="${TOPDIR}/WebP.xcframework" +readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.xcframework" +readonly MUXTARGETDIR="${TOPDIR}/WebPMux.xcframework" +readonly DEMUXTARGETDIR="${TOPDIR}/WebPDemux.xcframework" +readonly DEVELOPER=$(xcode-select --print-path) +readonly DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain" +readonly PLATFORMSROOT="${DEVELOPER}/Platforms" +readonly LIPO=$(xcrun -sdk iphoneos${SDK[$IOS]} -find lipo) + +if [[ -z "${SDK[$IOS]}" ]] || [[ ${SDK[$IOS]%%.*} -lt 8 ]]; then + echo "iOS SDK version 8.0 or higher is required!" + exit 1 +fi + +echo "Xcode Version: ${XCODE}" +echo "iOS SDK Version: ${SDK[$IOS]}" +echo "MacOS SDK Version: ${SDK[$MACOS]}" + +if [[ -e "${BUILDDIR}" || -e "${TARGETDIR}" || -e "${DECTARGETDIR}" \ + || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" ]]; then + cat << EOF +WARNING: The following directories will be deleted: +WARNING: ${BUILDDIR} +WARNING: ${TARGETDIR} +WARNING: ${DECTARGETDIR} +WARNING: ${MUXTARGETDIR} +WARNING: ${DEMUXTARGETDIR} +WARNING: The build will continue in 5 seconds... +EOF + sleep 5 +fi +rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR} \ + ${MUXTARGETDIR} ${DEMUXTARGETDIR} + +if [[ ! -e ${SRCDIR}/configure ]]; then + if ! (cd ${SRCDIR} && sh autogen.sh); then + cat << EOF +Error creating configure script! +This script requires the autoconf/automake and libtool to build. MacPorts or +Homebrew can be used to obtain these: +http://www.macports.org/install.php +https://brew.sh/ +EOF + exit 1 + fi +fi + +for (( i = 0; i < $NUM_PLATFORMS; ++i )); do + LIBLIST=() + DECLIBLIST=() + MUXLIBLIST=() + DEMUXLIBLIST=() + + for PLATFORM in ${PLATFORMS[$i]}; do + ROOTDIR="${BUILDDIR}/${PLATFORM}" + mkdir -p "${ROOTDIR}" + + ARCH="${PLATFORM##*-}" + case "${PLATFORM}" in + iPhone*) + sdk="${SDK[$IOS]}" + ;; + MacOS*) + sdk="${SDK[$MACOS]}" + ;; + *) + echo "Unrecognized platform: ${PLATFORM}!" + exit 1 + ;; + esac + + SDKROOT="${PLATFORMSROOT}/${PLATFORM%%-*}.platform/" + SDKROOT+="Developer/SDKs/${PLATFORM%%-*}${sdk}.sdk/" + CFLAGS="-pipe -isysroot ${SDKROOT} -O3 -DNDEBUG" + case "${PLATFORM}" in + iPhone*) + CFLAGS+=" -fembed-bitcode" + CFLAGS+=" -target ${ARCH}-apple-ios${IOS_MIN_VERSION}" + [[ "${PLATFORM}" == *Simulator* ]] && CFLAGS+="-simulator" + ;; + MacOSX-Catalyst*) + CFLAGS+=" -target" + CFLAGS+=" ${ARCH}-apple-ios${MACOSX_CATALYST_MIN_VERSION}-macabi" + ;; + MacOSX*) + CFLAGS+=" -mmacosx-version-min=${MACOSX_MIN_VERSION}" + ;; + esac + + set -x + export PATH="${DEVROOT}/usr/bin:${OLDPATH}" + ${SRCDIR}/configure --host=${ARCH/arm64/aarch64}-apple-darwin \ + --build=$(${SRCDIR}/config.guess) \ + --prefix=${ROOTDIR} \ + --disable-shared --enable-static \ + --enable-libwebpdecoder --enable-swap-16bit-csp \ + --enable-libwebpmux \ + CC="clang -arch ${ARCH}" \ + CFLAGS="${CFLAGS}" + set +x + + # run make only in the src/ directory to create libwebp.a/libwebpdecoder.a + cd src/ + make V=0 + make install + + LIBLIST+=("${ROOTDIR}/lib/libwebp.a") + DECLIBLIST+=("${ROOTDIR}/lib/libwebpdecoder.a") + MUXLIBLIST+=("${ROOTDIR}/lib/libwebpmux.a") + DEMUXLIBLIST+=("${ROOTDIR}/lib/libwebpdemux.a") + # xcodebuild requires a directory for the -headers option, these will match + # for all builds. + make install-data DESTDIR="${ROOTDIR}/lib-headers" + make install-commonHEADERS DESTDIR="${ROOTDIR}/dec-headers" + make -C demux install-data DESTDIR="${ROOTDIR}/demux-headers" + make -C mux install-data DESTDIR="${ROOTDIR}/mux-headers" + LIB_HEADERS="${ROOTDIR}/lib-headers/${ROOTDIR}/include/webp" + DEC_HEADERS="${ROOTDIR}/dec-headers/${ROOTDIR}/include/webp" + DEMUX_HEADERS="${ROOTDIR}/demux-headers/${ROOTDIR}/include/webp" + MUX_HEADERS="${ROOTDIR}/mux-headers/${ROOTDIR}/include/webp" + + make distclean + cd .. + + export PATH=${OLDPATH} + done + + [[ -z "${LIBLIST[@]}" ]] && continue + + # Create a temporary target directory for each [-]. + target_dir="${BUILDDIR}/${PLATFORMS[$i]}" + target_dir="${target_dir%% *}" + target_dir="${target_dir%-*}" + target_lib="${target_dir}/$(basename ${LIBLIST[0]})" + target_declib="${target_dir}/$(basename ${DECLIBLIST[0]})" + target_demuxlib="${target_dir}/$(basename ${DEMUXLIBLIST[0]})" + target_muxlib="${target_dir}/$(basename ${MUXLIBLIST[0]})" + + mkdir -p "${target_dir}" + ${LIPO} -create ${LIBLIST[@]} -output "${target_lib}" + ${LIPO} -create ${DECLIBLIST[@]} -output "${target_declib}" + ${LIPO} -create ${DEMUXLIBLIST[@]} -output "${target_demuxlib}" + ${LIPO} -create ${MUXLIBLIST[@]} -output "${target_muxlib}" + FAT_LIBLIST+=(-library "${target_lib}" -headers "${LIB_HEADERS}") + FAT_DECLIBLIST+=(-library "${target_declib}" -headers "${DEC_HEADERS}") + FAT_DEMUXLIBLIST+=(-library "${target_demuxlib}" -headers "${DEMUX_HEADERS}") + FAT_MUXLIBLIST+=(-library "${target_muxlib}" -headers "${MUX_HEADERS}") +done + +# lipo will not put archives with the same architecture (e.g., x86_64 +# iPhoneSimulator & MacOS) in the same fat output file. xcodebuild +# -create-xcframework requires universal archives to avoid e.g.: +# Both ios-x86_64-maccatalyst and ios-arm64-maccatalyst represent two +# equivalent library definitions +set -x +xcodebuild -create-xcframework "${FAT_LIBLIST[@]}" \ + -output ${TARGETDIR} +xcodebuild -create-xcframework "${FAT_DECLIBLIST[@]}" \ + -output ${DECTARGETDIR} +xcodebuild -create-xcframework "${FAT_DEMUXLIBLIST[@]}" \ + -output ${DEMUXTARGETDIR} +xcodebuild -create-xcframework "${FAT_MUXLIBLIST[@]}" \ + -output ${MUXTARGETDIR} +set +x + +echo "SUCCESS" diff --git a/z_libwebp_src_dec_alpha_dec.c b/z_libwebp_src_dec_alpha_dec.c deleted file mode 100644 index b05768c..0000000 --- a/z_libwebp_src_dec_alpha_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/alpha_dec.c" diff --git a/z_libwebp_src_dec_buffer_dec.c b/z_libwebp_src_dec_buffer_dec.c deleted file mode 100644 index a163924..0000000 --- a/z_libwebp_src_dec_buffer_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/buffer_dec.c" diff --git a/z_libwebp_src_dec_frame_dec.c b/z_libwebp_src_dec_frame_dec.c deleted file mode 100644 index e8b8d1c..0000000 --- a/z_libwebp_src_dec_frame_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/frame_dec.c" diff --git a/z_libwebp_src_dec_idec_dec.c b/z_libwebp_src_dec_idec_dec.c deleted file mode 100644 index dc7ebaa..0000000 --- a/z_libwebp_src_dec_idec_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/idec_dec.c" diff --git a/z_libwebp_src_dec_io_dec.c b/z_libwebp_src_dec_io_dec.c deleted file mode 100644 index 32daeec..0000000 --- a/z_libwebp_src_dec_io_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/io_dec.c" diff --git a/z_libwebp_src_dec_quant_dec.c b/z_libwebp_src_dec_quant_dec.c deleted file mode 100644 index 9201fd8..0000000 --- a/z_libwebp_src_dec_quant_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/quant_dec.c" diff --git a/z_libwebp_src_dec_tree_dec.c b/z_libwebp_src_dec_tree_dec.c deleted file mode 100644 index 530575d..0000000 --- a/z_libwebp_src_dec_tree_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/tree_dec.c" diff --git a/z_libwebp_src_dec_vp8_dec.c b/z_libwebp_src_dec_vp8_dec.c deleted file mode 100644 index 488fb8b..0000000 --- a/z_libwebp_src_dec_vp8_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/vp8_dec.c" diff --git a/z_libwebp_src_dec_vp8l_dec.c b/z_libwebp_src_dec_vp8l_dec.c deleted file mode 100644 index 94f04cc..0000000 --- a/z_libwebp_src_dec_vp8l_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/vp8l_dec.c" diff --git a/z_libwebp_src_dec_webp_dec.c b/z_libwebp_src_dec_webp_dec.c deleted file mode 100644 index 71e7dab..0000000 --- a/z_libwebp_src_dec_webp_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dec/webp_dec.c" diff --git a/z_libwebp_src_demux_anim_decode.c b/z_libwebp_src_demux_anim_decode.c deleted file mode 100644 index 8802c83..0000000 --- a/z_libwebp_src_demux_anim_decode.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/demux/anim_decode.c" diff --git a/z_libwebp_src_demux_demux.c b/z_libwebp_src_demux_demux.c deleted file mode 100644 index a021b1a..0000000 --- a/z_libwebp_src_demux_demux.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/demux/demux.c" diff --git a/z_libwebp_src_dsp_alpha_processing.c b/z_libwebp_src_dsp_alpha_processing.c deleted file mode 100644 index f9e0d55..0000000 --- a/z_libwebp_src_dsp_alpha_processing.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/alpha_processing.c" diff --git a/z_libwebp_src_dsp_alpha_processing_mips_dsp_r2.c b/z_libwebp_src_dsp_alpha_processing_mips_dsp_r2.c deleted file mode 100644 index f4e6245..0000000 --- a/z_libwebp_src_dsp_alpha_processing_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/alpha_processing_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_alpha_processing_neon.c b/z_libwebp_src_dsp_alpha_processing_neon.c deleted file mode 100644 index 71f15a9..0000000 --- a/z_libwebp_src_dsp_alpha_processing_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/alpha_processing_neon.c" diff --git a/z_libwebp_src_dsp_alpha_processing_sse2.c b/z_libwebp_src_dsp_alpha_processing_sse2.c deleted file mode 100644 index 5fbea84..0000000 --- a/z_libwebp_src_dsp_alpha_processing_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/alpha_processing_sse2.c" diff --git a/z_libwebp_src_dsp_alpha_processing_sse41.c b/z_libwebp_src_dsp_alpha_processing_sse41.c deleted file mode 100644 index e97f879..0000000 --- a/z_libwebp_src_dsp_alpha_processing_sse41.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/alpha_processing_sse41.c" diff --git a/z_libwebp_src_dsp_cost.c b/z_libwebp_src_dsp_cost.c deleted file mode 100644 index 1c8b81f..0000000 --- a/z_libwebp_src_dsp_cost.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/cost.c" diff --git a/z_libwebp_src_dsp_cost_mips32.c b/z_libwebp_src_dsp_cost_mips32.c deleted file mode 100644 index c31851a..0000000 --- a/z_libwebp_src_dsp_cost_mips32.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/cost_mips32.c" diff --git a/z_libwebp_src_dsp_cost_mips_dsp_r2.c b/z_libwebp_src_dsp_cost_mips_dsp_r2.c deleted file mode 100644 index d1b9a31..0000000 --- a/z_libwebp_src_dsp_cost_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/cost_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_cost_neon.c b/z_libwebp_src_dsp_cost_neon.c deleted file mode 100644 index ca02118..0000000 --- a/z_libwebp_src_dsp_cost_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/cost_neon.c" diff --git a/z_libwebp_src_dsp_cost_sse2.c b/z_libwebp_src_dsp_cost_sse2.c deleted file mode 100644 index 7421601..0000000 --- a/z_libwebp_src_dsp_cost_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/cost_sse2.c" diff --git a/z_libwebp_src_dsp_cpu.c b/z_libwebp_src_dsp_cpu.c deleted file mode 100644 index cd22977..0000000 --- a/z_libwebp_src_dsp_cpu.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/cpu.c" diff --git a/z_libwebp_src_dsp_dec.c b/z_libwebp_src_dsp_dec.c deleted file mode 100644 index c13abe9..0000000 --- a/z_libwebp_src_dsp_dec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/dec.c" diff --git a/z_libwebp_src_dsp_dec_clip_tables.c b/z_libwebp_src_dsp_dec_clip_tables.c deleted file mode 100644 index 7eb2a58..0000000 --- a/z_libwebp_src_dsp_dec_clip_tables.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/dec_clip_tables.c" diff --git a/z_libwebp_src_dsp_dec_mips32.c b/z_libwebp_src_dsp_dec_mips32.c deleted file mode 100644 index f76cf8f..0000000 --- a/z_libwebp_src_dsp_dec_mips32.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/dec_mips32.c" diff --git a/z_libwebp_src_dsp_dec_mips_dsp_r2.c b/z_libwebp_src_dsp_dec_mips_dsp_r2.c deleted file mode 100644 index 0a9ffcd..0000000 --- a/z_libwebp_src_dsp_dec_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/dec_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_dec_msa.c b/z_libwebp_src_dsp_dec_msa.c deleted file mode 100644 index d6bbe88..0000000 --- a/z_libwebp_src_dsp_dec_msa.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/dec_msa.c" diff --git a/z_libwebp_src_dsp_dec_neon.c b/z_libwebp_src_dsp_dec_neon.c deleted file mode 100644 index db44119..0000000 --- a/z_libwebp_src_dsp_dec_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/dec_neon.c" diff --git a/z_libwebp_src_dsp_dec_sse2.c b/z_libwebp_src_dsp_dec_sse2.c deleted file mode 100644 index 559eb48..0000000 --- a/z_libwebp_src_dsp_dec_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/dec_sse2.c" diff --git a/z_libwebp_src_dsp_dec_sse41.c b/z_libwebp_src_dsp_dec_sse41.c deleted file mode 100644 index f015c50..0000000 --- a/z_libwebp_src_dsp_dec_sse41.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/dec_sse41.c" diff --git a/z_libwebp_src_dsp_enc.c b/z_libwebp_src_dsp_enc.c deleted file mode 100644 index e493c05..0000000 --- a/z_libwebp_src_dsp_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/enc.c" diff --git a/z_libwebp_src_dsp_enc_mips32.c b/z_libwebp_src_dsp_enc_mips32.c deleted file mode 100644 index 8161712..0000000 --- a/z_libwebp_src_dsp_enc_mips32.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/enc_mips32.c" diff --git a/z_libwebp_src_dsp_enc_mips_dsp_r2.c b/z_libwebp_src_dsp_enc_mips_dsp_r2.c deleted file mode 100644 index b23116a..0000000 --- a/z_libwebp_src_dsp_enc_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/enc_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_enc_msa.c b/z_libwebp_src_dsp_enc_msa.c deleted file mode 100644 index 3c7ce68..0000000 --- a/z_libwebp_src_dsp_enc_msa.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/enc_msa.c" diff --git a/z_libwebp_src_dsp_enc_neon.c b/z_libwebp_src_dsp_enc_neon.c deleted file mode 100644 index e8cfbf7..0000000 --- a/z_libwebp_src_dsp_enc_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/enc_neon.c" diff --git a/z_libwebp_src_dsp_enc_sse2.c b/z_libwebp_src_dsp_enc_sse2.c deleted file mode 100644 index 24baa6e..0000000 --- a/z_libwebp_src_dsp_enc_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/enc_sse2.c" diff --git a/z_libwebp_src_dsp_enc_sse41.c b/z_libwebp_src_dsp_enc_sse41.c deleted file mode 100644 index ed9eaf9..0000000 --- a/z_libwebp_src_dsp_enc_sse41.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/enc_sse41.c" diff --git a/z_libwebp_src_dsp_filters.c b/z_libwebp_src_dsp_filters.c deleted file mode 100644 index 21f5064..0000000 --- a/z_libwebp_src_dsp_filters.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/filters.c" diff --git a/z_libwebp_src_dsp_filters_mips_dsp_r2.c b/z_libwebp_src_dsp_filters_mips_dsp_r2.c deleted file mode 100644 index 7a557ad..0000000 --- a/z_libwebp_src_dsp_filters_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/filters_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_filters_msa.c b/z_libwebp_src_dsp_filters_msa.c deleted file mode 100644 index 0ec761c..0000000 --- a/z_libwebp_src_dsp_filters_msa.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/filters_msa.c" diff --git a/z_libwebp_src_dsp_filters_neon.c b/z_libwebp_src_dsp_filters_neon.c deleted file mode 100644 index de3a322..0000000 --- a/z_libwebp_src_dsp_filters_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/filters_neon.c" diff --git a/z_libwebp_src_dsp_filters_sse2.c b/z_libwebp_src_dsp_filters_sse2.c deleted file mode 100644 index c99fbb3..0000000 --- a/z_libwebp_src_dsp_filters_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/filters_sse2.c" diff --git a/z_libwebp_src_dsp_lossless.c b/z_libwebp_src_dsp_lossless.c deleted file mode 100644 index 129ed4b..0000000 --- a/z_libwebp_src_dsp_lossless.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless.c" diff --git a/z_libwebp_src_dsp_lossless_enc.c b/z_libwebp_src_dsp_lossless_enc.c deleted file mode 100644 index a4d274b..0000000 --- a/z_libwebp_src_dsp_lossless_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_enc.c" diff --git a/z_libwebp_src_dsp_lossless_enc_mips32.c b/z_libwebp_src_dsp_lossless_enc_mips32.c deleted file mode 100644 index ed9af3d..0000000 --- a/z_libwebp_src_dsp_lossless_enc_mips32.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_enc_mips32.c" diff --git a/z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c b/z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c deleted file mode 100644 index 404c32e..0000000 --- a/z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_enc_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_lossless_enc_msa.c b/z_libwebp_src_dsp_lossless_enc_msa.c deleted file mode 100644 index 5b8949c..0000000 --- a/z_libwebp_src_dsp_lossless_enc_msa.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_enc_msa.c" diff --git a/z_libwebp_src_dsp_lossless_enc_neon.c b/z_libwebp_src_dsp_lossless_enc_neon.c deleted file mode 100644 index 653ab9e..0000000 --- a/z_libwebp_src_dsp_lossless_enc_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_enc_neon.c" diff --git a/z_libwebp_src_dsp_lossless_enc_sse2.c b/z_libwebp_src_dsp_lossless_enc_sse2.c deleted file mode 100644 index 144b060..0000000 --- a/z_libwebp_src_dsp_lossless_enc_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_enc_sse2.c" diff --git a/z_libwebp_src_dsp_lossless_enc_sse41.c b/z_libwebp_src_dsp_lossless_enc_sse41.c deleted file mode 100644 index 71d0594..0000000 --- a/z_libwebp_src_dsp_lossless_enc_sse41.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_enc_sse41.c" diff --git a/z_libwebp_src_dsp_lossless_mips_dsp_r2.c b/z_libwebp_src_dsp_lossless_mips_dsp_r2.c deleted file mode 100644 index 7de85c1..0000000 --- a/z_libwebp_src_dsp_lossless_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_lossless_msa.c b/z_libwebp_src_dsp_lossless_msa.c deleted file mode 100644 index 24c9047..0000000 --- a/z_libwebp_src_dsp_lossless_msa.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_msa.c" diff --git a/z_libwebp_src_dsp_lossless_neon.c b/z_libwebp_src_dsp_lossless_neon.c deleted file mode 100644 index c8cab2b..0000000 --- a/z_libwebp_src_dsp_lossless_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_neon.c" diff --git a/z_libwebp_src_dsp_lossless_sse2.c b/z_libwebp_src_dsp_lossless_sse2.c deleted file mode 100644 index 92dc460..0000000 --- a/z_libwebp_src_dsp_lossless_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/lossless_sse2.c" diff --git a/z_libwebp_src_dsp_rescaler.c b/z_libwebp_src_dsp_rescaler.c deleted file mode 100644 index dbb9ff8..0000000 --- a/z_libwebp_src_dsp_rescaler.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/rescaler.c" diff --git a/z_libwebp_src_dsp_rescaler_mips32.c b/z_libwebp_src_dsp_rescaler_mips32.c deleted file mode 100644 index 8222583..0000000 --- a/z_libwebp_src_dsp_rescaler_mips32.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/rescaler_mips32.c" diff --git a/z_libwebp_src_dsp_rescaler_mips_dsp_r2.c b/z_libwebp_src_dsp_rescaler_mips_dsp_r2.c deleted file mode 100644 index 2f0694b..0000000 --- a/z_libwebp_src_dsp_rescaler_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/rescaler_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_rescaler_msa.c b/z_libwebp_src_dsp_rescaler_msa.c deleted file mode 100644 index 568f722..0000000 --- a/z_libwebp_src_dsp_rescaler_msa.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/rescaler_msa.c" diff --git a/z_libwebp_src_dsp_rescaler_neon.c b/z_libwebp_src_dsp_rescaler_neon.c deleted file mode 100644 index 31ae2dc..0000000 --- a/z_libwebp_src_dsp_rescaler_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/rescaler_neon.c" diff --git a/z_libwebp_src_dsp_rescaler_sse2.c b/z_libwebp_src_dsp_rescaler_sse2.c deleted file mode 100644 index 0db6a8e..0000000 --- a/z_libwebp_src_dsp_rescaler_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/rescaler_sse2.c" diff --git a/z_libwebp_src_dsp_ssim.c b/z_libwebp_src_dsp_ssim.c deleted file mode 100644 index a3c8c45..0000000 --- a/z_libwebp_src_dsp_ssim.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/ssim.c" diff --git a/z_libwebp_src_dsp_ssim_sse2.c b/z_libwebp_src_dsp_ssim_sse2.c deleted file mode 100644 index f4f2288..0000000 --- a/z_libwebp_src_dsp_ssim_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/ssim_sse2.c" diff --git a/z_libwebp_src_dsp_upsampling.c b/z_libwebp_src_dsp_upsampling.c deleted file mode 100644 index a62836c..0000000 --- a/z_libwebp_src_dsp_upsampling.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/upsampling.c" diff --git a/z_libwebp_src_dsp_upsampling_mips_dsp_r2.c b/z_libwebp_src_dsp_upsampling_mips_dsp_r2.c deleted file mode 100644 index 59ad3c9..0000000 --- a/z_libwebp_src_dsp_upsampling_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/upsampling_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_upsampling_msa.c b/z_libwebp_src_dsp_upsampling_msa.c deleted file mode 100644 index 2e29bed..0000000 --- a/z_libwebp_src_dsp_upsampling_msa.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/upsampling_msa.c" diff --git a/z_libwebp_src_dsp_upsampling_neon.c b/z_libwebp_src_dsp_upsampling_neon.c deleted file mode 100644 index d434489..0000000 --- a/z_libwebp_src_dsp_upsampling_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/upsampling_neon.c" diff --git a/z_libwebp_src_dsp_upsampling_sse2.c b/z_libwebp_src_dsp_upsampling_sse2.c deleted file mode 100644 index 1b82c41..0000000 --- a/z_libwebp_src_dsp_upsampling_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/upsampling_sse2.c" diff --git a/z_libwebp_src_dsp_upsampling_sse41.c b/z_libwebp_src_dsp_upsampling_sse41.c deleted file mode 100644 index d85afb7..0000000 --- a/z_libwebp_src_dsp_upsampling_sse41.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/upsampling_sse41.c" diff --git a/z_libwebp_src_dsp_yuv.c b/z_libwebp_src_dsp_yuv.c deleted file mode 100644 index 919d42e..0000000 --- a/z_libwebp_src_dsp_yuv.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/yuv.c" diff --git a/z_libwebp_src_dsp_yuv_mips32.c b/z_libwebp_src_dsp_yuv_mips32.c deleted file mode 100644 index c8a59ab..0000000 --- a/z_libwebp_src_dsp_yuv_mips32.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/yuv_mips32.c" diff --git a/z_libwebp_src_dsp_yuv_mips_dsp_r2.c b/z_libwebp_src_dsp_yuv_mips_dsp_r2.c deleted file mode 100644 index a1c57f5..0000000 --- a/z_libwebp_src_dsp_yuv_mips_dsp_r2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/yuv_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_yuv_neon.c b/z_libwebp_src_dsp_yuv_neon.c deleted file mode 100644 index 566b137..0000000 --- a/z_libwebp_src_dsp_yuv_neon.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/yuv_neon.c" diff --git a/z_libwebp_src_dsp_yuv_sse2.c b/z_libwebp_src_dsp_yuv_sse2.c deleted file mode 100644 index 6152b8f..0000000 --- a/z_libwebp_src_dsp_yuv_sse2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/yuv_sse2.c" diff --git a/z_libwebp_src_dsp_yuv_sse41.c b/z_libwebp_src_dsp_yuv_sse41.c deleted file mode 100644 index 0d09155..0000000 --- a/z_libwebp_src_dsp_yuv_sse41.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/dsp/yuv_sse41.c" diff --git a/z_libwebp_src_enc_alpha_enc.c b/z_libwebp_src_enc_alpha_enc.c deleted file mode 100644 index 5a71453..0000000 --- a/z_libwebp_src_enc_alpha_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/alpha_enc.c" diff --git a/z_libwebp_src_enc_analysis_enc.c b/z_libwebp_src_enc_analysis_enc.c deleted file mode 100644 index 4765748..0000000 --- a/z_libwebp_src_enc_analysis_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/analysis_enc.c" diff --git a/z_libwebp_src_enc_backward_references_cost_enc.c b/z_libwebp_src_enc_backward_references_cost_enc.c deleted file mode 100644 index 52412b8..0000000 --- a/z_libwebp_src_enc_backward_references_cost_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/backward_references_cost_enc.c" diff --git a/z_libwebp_src_enc_backward_references_enc.c b/z_libwebp_src_enc_backward_references_enc.c deleted file mode 100644 index 8c80a73..0000000 --- a/z_libwebp_src_enc_backward_references_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/backward_references_enc.c" diff --git a/z_libwebp_src_enc_config_enc.c b/z_libwebp_src_enc_config_enc.c deleted file mode 100644 index 647ba58..0000000 --- a/z_libwebp_src_enc_config_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/config_enc.c" diff --git a/z_libwebp_src_enc_cost_enc.c b/z_libwebp_src_enc_cost_enc.c deleted file mode 100644 index 0170ca9..0000000 --- a/z_libwebp_src_enc_cost_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/cost_enc.c" diff --git a/z_libwebp_src_enc_filter_enc.c b/z_libwebp_src_enc_filter_enc.c deleted file mode 100644 index 7bc3ce7..0000000 --- a/z_libwebp_src_enc_filter_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/filter_enc.c" diff --git a/z_libwebp_src_enc_frame_enc.c b/z_libwebp_src_enc_frame_enc.c deleted file mode 100644 index ef907cb..0000000 --- a/z_libwebp_src_enc_frame_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/frame_enc.c" diff --git a/z_libwebp_src_enc_histogram_enc.c b/z_libwebp_src_enc_histogram_enc.c deleted file mode 100644 index f836d83..0000000 --- a/z_libwebp_src_enc_histogram_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/histogram_enc.c" diff --git a/z_libwebp_src_enc_iterator_enc.c b/z_libwebp_src_enc_iterator_enc.c deleted file mode 100644 index 180e998..0000000 --- a/z_libwebp_src_enc_iterator_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/iterator_enc.c" diff --git a/z_libwebp_src_enc_near_lossless_enc.c b/z_libwebp_src_enc_near_lossless_enc.c deleted file mode 100644 index 0795418..0000000 --- a/z_libwebp_src_enc_near_lossless_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/near_lossless_enc.c" diff --git a/z_libwebp_src_enc_picture_csp_enc.c b/z_libwebp_src_enc_picture_csp_enc.c deleted file mode 100644 index 776d0d6..0000000 --- a/z_libwebp_src_enc_picture_csp_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/picture_csp_enc.c" diff --git a/z_libwebp_src_enc_picture_enc.c b/z_libwebp_src_enc_picture_enc.c deleted file mode 100644 index 1427611..0000000 --- a/z_libwebp_src_enc_picture_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/picture_enc.c" diff --git a/z_libwebp_src_enc_picture_psnr_enc.c b/z_libwebp_src_enc_picture_psnr_enc.c deleted file mode 100644 index dc67889..0000000 --- a/z_libwebp_src_enc_picture_psnr_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/picture_psnr_enc.c" diff --git a/z_libwebp_src_enc_picture_rescale_enc.c b/z_libwebp_src_enc_picture_rescale_enc.c deleted file mode 100644 index a22e9dd..0000000 --- a/z_libwebp_src_enc_picture_rescale_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/picture_rescale_enc.c" diff --git a/z_libwebp_src_enc_picture_tools_enc.c b/z_libwebp_src_enc_picture_tools_enc.c deleted file mode 100644 index aa45404..0000000 --- a/z_libwebp_src_enc_picture_tools_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/picture_tools_enc.c" diff --git a/z_libwebp_src_enc_predictor_enc.c b/z_libwebp_src_enc_predictor_enc.c deleted file mode 100644 index 6742787..0000000 --- a/z_libwebp_src_enc_predictor_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/predictor_enc.c" diff --git a/z_libwebp_src_enc_quant_enc.c b/z_libwebp_src_enc_quant_enc.c deleted file mode 100644 index 351064d..0000000 --- a/z_libwebp_src_enc_quant_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/quant_enc.c" diff --git a/z_libwebp_src_enc_syntax_enc.c b/z_libwebp_src_enc_syntax_enc.c deleted file mode 100644 index 03cb202..0000000 --- a/z_libwebp_src_enc_syntax_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/syntax_enc.c" diff --git a/z_libwebp_src_enc_token_enc.c b/z_libwebp_src_enc_token_enc.c deleted file mode 100644 index a2f7eb8..0000000 --- a/z_libwebp_src_enc_token_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/token_enc.c" diff --git a/z_libwebp_src_enc_tree_enc.c b/z_libwebp_src_enc_tree_enc.c deleted file mode 100644 index 447f341..0000000 --- a/z_libwebp_src_enc_tree_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/tree_enc.c" diff --git a/z_libwebp_src_enc_vp8l_enc.c b/z_libwebp_src_enc_vp8l_enc.c deleted file mode 100644 index a8224e9..0000000 --- a/z_libwebp_src_enc_vp8l_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/vp8l_enc.c" diff --git a/z_libwebp_src_enc_webp_enc.c b/z_libwebp_src_enc_webp_enc.c deleted file mode 100644 index 81596c5..0000000 --- a/z_libwebp_src_enc_webp_enc.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/enc/webp_enc.c" diff --git a/z_libwebp_src_mux_anim_encode.c b/z_libwebp_src_mux_anim_encode.c deleted file mode 100644 index 68d997a..0000000 --- a/z_libwebp_src_mux_anim_encode.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/mux/anim_encode.c" diff --git a/z_libwebp_src_mux_muxedit.c b/z_libwebp_src_mux_muxedit.c deleted file mode 100644 index 8e8d4f7..0000000 --- a/z_libwebp_src_mux_muxedit.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/mux/muxedit.c" diff --git a/z_libwebp_src_mux_muxinternal.c b/z_libwebp_src_mux_muxinternal.c deleted file mode 100644 index 7eeba4b..0000000 --- a/z_libwebp_src_mux_muxinternal.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/mux/muxinternal.c" diff --git a/z_libwebp_src_mux_muxread.c b/z_libwebp_src_mux_muxread.c deleted file mode 100644 index c26864e..0000000 --- a/z_libwebp_src_mux_muxread.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/mux/muxread.c" diff --git a/z_libwebp_src_utils_bit_reader_utils.c b/z_libwebp_src_utils_bit_reader_utils.c deleted file mode 100644 index d40568c..0000000 --- a/z_libwebp_src_utils_bit_reader_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/bit_reader_utils.c" diff --git a/z_libwebp_src_utils_bit_writer_utils.c b/z_libwebp_src_utils_bit_writer_utils.c deleted file mode 100644 index ebcde83..0000000 --- a/z_libwebp_src_utils_bit_writer_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/bit_writer_utils.c" diff --git a/z_libwebp_src_utils_color_cache_utils.c b/z_libwebp_src_utils_color_cache_utils.c deleted file mode 100644 index 33d38d1..0000000 --- a/z_libwebp_src_utils_color_cache_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/color_cache_utils.c" diff --git a/z_libwebp_src_utils_filters_utils.c b/z_libwebp_src_utils_filters_utils.c deleted file mode 100644 index fe95f11..0000000 --- a/z_libwebp_src_utils_filters_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/filters_utils.c" diff --git a/z_libwebp_src_utils_huffman_encode_utils.c b/z_libwebp_src_utils_huffman_encode_utils.c deleted file mode 100644 index 34b820a..0000000 --- a/z_libwebp_src_utils_huffman_encode_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/huffman_encode_utils.c" diff --git a/z_libwebp_src_utils_huffman_utils.c b/z_libwebp_src_utils_huffman_utils.c deleted file mode 100644 index 0eb6581..0000000 --- a/z_libwebp_src_utils_huffman_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/huffman_utils.c" diff --git a/z_libwebp_src_utils_quant_levels_dec_utils.c b/z_libwebp_src_utils_quant_levels_dec_utils.c deleted file mode 100644 index 735dfe3..0000000 --- a/z_libwebp_src_utils_quant_levels_dec_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/quant_levels_dec_utils.c" diff --git a/z_libwebp_src_utils_quant_levels_utils.c b/z_libwebp_src_utils_quant_levels_utils.c deleted file mode 100644 index 6e7c097..0000000 --- a/z_libwebp_src_utils_quant_levels_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/quant_levels_utils.c" diff --git a/z_libwebp_src_utils_random_utils.c b/z_libwebp_src_utils_random_utils.c deleted file mode 100644 index b15b019..0000000 --- a/z_libwebp_src_utils_random_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/random_utils.c" diff --git a/z_libwebp_src_utils_rescaler_utils.c b/z_libwebp_src_utils_rescaler_utils.c deleted file mode 100644 index 0fab07d..0000000 --- a/z_libwebp_src_utils_rescaler_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/rescaler_utils.c" diff --git a/z_libwebp_src_utils_thread_utils.c b/z_libwebp_src_utils_thread_utils.c deleted file mode 100644 index 989a7d4..0000000 --- a/z_libwebp_src_utils_thread_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/thread_utils.c" diff --git a/z_libwebp_src_utils_utils.c b/z_libwebp_src_utils_utils.c deleted file mode 100644 index 36c5ffc..0000000 --- a/z_libwebp_src_utils_utils.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Auto Generated By 'go generate', DONOT EDIT!!! - -// +build cgo - -#include "src/utils/utils.c" From d96a1bad1823581b95f1a770ce8c4d24747bba18 Mon Sep 17 00:00:00 2001 From: Pablo Lagos Date: Mon, 15 Nov 2021 12:43:58 -0300 Subject: [PATCH 2/4] libwebp version 1.2.1 --- z_libwebp_src_dec_alpha_dec.c | 9 +++++++++ z_libwebp_src_dec_buffer_dec.c | 9 +++++++++ z_libwebp_src_dec_frame_dec.c | 9 +++++++++ z_libwebp_src_dec_idec_dec.c | 9 +++++++++ z_libwebp_src_dec_io_dec.c | 9 +++++++++ z_libwebp_src_dec_quant_dec.c | 9 +++++++++ z_libwebp_src_dec_tree_dec.c | 9 +++++++++ z_libwebp_src_dec_vp8_dec.c | 9 +++++++++ z_libwebp_src_dec_vp8l_dec.c | 9 +++++++++ z_libwebp_src_dec_webp_dec.c | 9 +++++++++ z_libwebp_src_demux_anim_decode.c | 9 +++++++++ z_libwebp_src_demux_demux.c | 9 +++++++++ z_libwebp_src_dsp_alpha_processing.c | 9 +++++++++ z_libwebp_src_dsp_alpha_processing_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_alpha_processing_neon.c | 9 +++++++++ z_libwebp_src_dsp_alpha_processing_sse2.c | 9 +++++++++ z_libwebp_src_dsp_alpha_processing_sse41.c | 9 +++++++++ z_libwebp_src_dsp_cost.c | 9 +++++++++ z_libwebp_src_dsp_cost_mips32.c | 9 +++++++++ z_libwebp_src_dsp_cost_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_cost_neon.c | 9 +++++++++ z_libwebp_src_dsp_cost_sse2.c | 9 +++++++++ z_libwebp_src_dsp_cpu.c | 9 +++++++++ z_libwebp_src_dsp_dec.c | 9 +++++++++ z_libwebp_src_dsp_dec_clip_tables.c | 9 +++++++++ z_libwebp_src_dsp_dec_mips32.c | 9 +++++++++ z_libwebp_src_dsp_dec_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_dec_msa.c | 9 +++++++++ z_libwebp_src_dsp_dec_neon.c | 9 +++++++++ z_libwebp_src_dsp_dec_sse2.c | 9 +++++++++ z_libwebp_src_dsp_dec_sse41.c | 9 +++++++++ z_libwebp_src_dsp_enc.c | 9 +++++++++ z_libwebp_src_dsp_enc_mips32.c | 9 +++++++++ z_libwebp_src_dsp_enc_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_enc_msa.c | 9 +++++++++ z_libwebp_src_dsp_enc_neon.c | 9 +++++++++ z_libwebp_src_dsp_enc_sse2.c | 9 +++++++++ z_libwebp_src_dsp_enc_sse41.c | 9 +++++++++ z_libwebp_src_dsp_filters.c | 9 +++++++++ z_libwebp_src_dsp_filters_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_filters_msa.c | 9 +++++++++ z_libwebp_src_dsp_filters_neon.c | 9 +++++++++ z_libwebp_src_dsp_filters_sse2.c | 9 +++++++++ z_libwebp_src_dsp_lossless.c | 9 +++++++++ z_libwebp_src_dsp_lossless_enc.c | 9 +++++++++ z_libwebp_src_dsp_lossless_enc_mips32.c | 9 +++++++++ z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_lossless_enc_msa.c | 9 +++++++++ z_libwebp_src_dsp_lossless_enc_neon.c | 9 +++++++++ z_libwebp_src_dsp_lossless_enc_sse2.c | 9 +++++++++ z_libwebp_src_dsp_lossless_enc_sse41.c | 9 +++++++++ z_libwebp_src_dsp_lossless_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_lossless_msa.c | 9 +++++++++ z_libwebp_src_dsp_lossless_neon.c | 9 +++++++++ z_libwebp_src_dsp_lossless_sse2.c | 9 +++++++++ z_libwebp_src_dsp_lossless_sse41.c | 9 +++++++++ z_libwebp_src_dsp_rescaler.c | 9 +++++++++ z_libwebp_src_dsp_rescaler_mips32.c | 9 +++++++++ z_libwebp_src_dsp_rescaler_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_rescaler_msa.c | 9 +++++++++ z_libwebp_src_dsp_rescaler_neon.c | 9 +++++++++ z_libwebp_src_dsp_rescaler_sse2.c | 9 +++++++++ z_libwebp_src_dsp_ssim.c | 9 +++++++++ z_libwebp_src_dsp_ssim_sse2.c | 9 +++++++++ z_libwebp_src_dsp_upsampling.c | 9 +++++++++ z_libwebp_src_dsp_upsampling_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_upsampling_msa.c | 9 +++++++++ z_libwebp_src_dsp_upsampling_neon.c | 9 +++++++++ z_libwebp_src_dsp_upsampling_sse2.c | 9 +++++++++ z_libwebp_src_dsp_upsampling_sse41.c | 9 +++++++++ z_libwebp_src_dsp_yuv.c | 9 +++++++++ z_libwebp_src_dsp_yuv_mips32.c | 9 +++++++++ z_libwebp_src_dsp_yuv_mips_dsp_r2.c | 9 +++++++++ z_libwebp_src_dsp_yuv_neon.c | 9 +++++++++ z_libwebp_src_dsp_yuv_sse2.c | 9 +++++++++ z_libwebp_src_dsp_yuv_sse41.c | 9 +++++++++ z_libwebp_src_enc_alpha_enc.c | 9 +++++++++ z_libwebp_src_enc_analysis_enc.c | 9 +++++++++ z_libwebp_src_enc_backward_references_cost_enc.c | 9 +++++++++ z_libwebp_src_enc_backward_references_enc.c | 9 +++++++++ z_libwebp_src_enc_config_enc.c | 9 +++++++++ z_libwebp_src_enc_cost_enc.c | 9 +++++++++ z_libwebp_src_enc_filter_enc.c | 9 +++++++++ z_libwebp_src_enc_frame_enc.c | 9 +++++++++ z_libwebp_src_enc_histogram_enc.c | 9 +++++++++ z_libwebp_src_enc_iterator_enc.c | 9 +++++++++ z_libwebp_src_enc_near_lossless_enc.c | 9 +++++++++ z_libwebp_src_enc_picture_csp_enc.c | 9 +++++++++ z_libwebp_src_enc_picture_enc.c | 9 +++++++++ z_libwebp_src_enc_picture_psnr_enc.c | 9 +++++++++ z_libwebp_src_enc_picture_rescale_enc.c | 9 +++++++++ z_libwebp_src_enc_picture_tools_enc.c | 9 +++++++++ z_libwebp_src_enc_predictor_enc.c | 9 +++++++++ z_libwebp_src_enc_quant_enc.c | 9 +++++++++ z_libwebp_src_enc_syntax_enc.c | 9 +++++++++ z_libwebp_src_enc_token_enc.c | 9 +++++++++ z_libwebp_src_enc_tree_enc.c | 9 +++++++++ z_libwebp_src_enc_vp8l_enc.c | 9 +++++++++ z_libwebp_src_enc_webp_enc.c | 9 +++++++++ z_libwebp_src_mux_anim_encode.c | 9 +++++++++ z_libwebp_src_mux_muxedit.c | 9 +++++++++ z_libwebp_src_mux_muxinternal.c | 9 +++++++++ z_libwebp_src_mux_muxread.c | 9 +++++++++ z_libwebp_src_utils_bit_reader_utils.c | 9 +++++++++ z_libwebp_src_utils_bit_writer_utils.c | 9 +++++++++ z_libwebp_src_utils_color_cache_utils.c | 9 +++++++++ z_libwebp_src_utils_filters_utils.c | 9 +++++++++ z_libwebp_src_utils_huffman_encode_utils.c | 9 +++++++++ z_libwebp_src_utils_huffman_utils.c | 9 +++++++++ z_libwebp_src_utils_quant_levels_dec_utils.c | 9 +++++++++ z_libwebp_src_utils_quant_levels_utils.c | 9 +++++++++ z_libwebp_src_utils_random_utils.c | 9 +++++++++ z_libwebp_src_utils_rescaler_utils.c | 9 +++++++++ z_libwebp_src_utils_thread_utils.c | 9 +++++++++ z_libwebp_src_utils_utils.c | 9 +++++++++ 115 files changed, 1035 insertions(+) create mode 100644 z_libwebp_src_dec_alpha_dec.c create mode 100644 z_libwebp_src_dec_buffer_dec.c create mode 100644 z_libwebp_src_dec_frame_dec.c create mode 100644 z_libwebp_src_dec_idec_dec.c create mode 100644 z_libwebp_src_dec_io_dec.c create mode 100644 z_libwebp_src_dec_quant_dec.c create mode 100644 z_libwebp_src_dec_tree_dec.c create mode 100644 z_libwebp_src_dec_vp8_dec.c create mode 100644 z_libwebp_src_dec_vp8l_dec.c create mode 100644 z_libwebp_src_dec_webp_dec.c create mode 100644 z_libwebp_src_demux_anim_decode.c create mode 100644 z_libwebp_src_demux_demux.c create mode 100644 z_libwebp_src_dsp_alpha_processing.c create mode 100644 z_libwebp_src_dsp_alpha_processing_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_alpha_processing_neon.c create mode 100644 z_libwebp_src_dsp_alpha_processing_sse2.c create mode 100644 z_libwebp_src_dsp_alpha_processing_sse41.c create mode 100644 z_libwebp_src_dsp_cost.c create mode 100644 z_libwebp_src_dsp_cost_mips32.c create mode 100644 z_libwebp_src_dsp_cost_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_cost_neon.c create mode 100644 z_libwebp_src_dsp_cost_sse2.c create mode 100644 z_libwebp_src_dsp_cpu.c create mode 100644 z_libwebp_src_dsp_dec.c create mode 100644 z_libwebp_src_dsp_dec_clip_tables.c create mode 100644 z_libwebp_src_dsp_dec_mips32.c create mode 100644 z_libwebp_src_dsp_dec_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_dec_msa.c create mode 100644 z_libwebp_src_dsp_dec_neon.c create mode 100644 z_libwebp_src_dsp_dec_sse2.c create mode 100644 z_libwebp_src_dsp_dec_sse41.c create mode 100644 z_libwebp_src_dsp_enc.c create mode 100644 z_libwebp_src_dsp_enc_mips32.c create mode 100644 z_libwebp_src_dsp_enc_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_enc_msa.c create mode 100644 z_libwebp_src_dsp_enc_neon.c create mode 100644 z_libwebp_src_dsp_enc_sse2.c create mode 100644 z_libwebp_src_dsp_enc_sse41.c create mode 100644 z_libwebp_src_dsp_filters.c create mode 100644 z_libwebp_src_dsp_filters_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_filters_msa.c create mode 100644 z_libwebp_src_dsp_filters_neon.c create mode 100644 z_libwebp_src_dsp_filters_sse2.c create mode 100644 z_libwebp_src_dsp_lossless.c create mode 100644 z_libwebp_src_dsp_lossless_enc.c create mode 100644 z_libwebp_src_dsp_lossless_enc_mips32.c create mode 100644 z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_lossless_enc_msa.c create mode 100644 z_libwebp_src_dsp_lossless_enc_neon.c create mode 100644 z_libwebp_src_dsp_lossless_enc_sse2.c create mode 100644 z_libwebp_src_dsp_lossless_enc_sse41.c create mode 100644 z_libwebp_src_dsp_lossless_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_lossless_msa.c create mode 100644 z_libwebp_src_dsp_lossless_neon.c create mode 100644 z_libwebp_src_dsp_lossless_sse2.c create mode 100644 z_libwebp_src_dsp_lossless_sse41.c create mode 100644 z_libwebp_src_dsp_rescaler.c create mode 100644 z_libwebp_src_dsp_rescaler_mips32.c create mode 100644 z_libwebp_src_dsp_rescaler_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_rescaler_msa.c create mode 100644 z_libwebp_src_dsp_rescaler_neon.c create mode 100644 z_libwebp_src_dsp_rescaler_sse2.c create mode 100644 z_libwebp_src_dsp_ssim.c create mode 100644 z_libwebp_src_dsp_ssim_sse2.c create mode 100644 z_libwebp_src_dsp_upsampling.c create mode 100644 z_libwebp_src_dsp_upsampling_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_upsampling_msa.c create mode 100644 z_libwebp_src_dsp_upsampling_neon.c create mode 100644 z_libwebp_src_dsp_upsampling_sse2.c create mode 100644 z_libwebp_src_dsp_upsampling_sse41.c create mode 100644 z_libwebp_src_dsp_yuv.c create mode 100644 z_libwebp_src_dsp_yuv_mips32.c create mode 100644 z_libwebp_src_dsp_yuv_mips_dsp_r2.c create mode 100644 z_libwebp_src_dsp_yuv_neon.c create mode 100644 z_libwebp_src_dsp_yuv_sse2.c create mode 100644 z_libwebp_src_dsp_yuv_sse41.c create mode 100644 z_libwebp_src_enc_alpha_enc.c create mode 100644 z_libwebp_src_enc_analysis_enc.c create mode 100644 z_libwebp_src_enc_backward_references_cost_enc.c create mode 100644 z_libwebp_src_enc_backward_references_enc.c create mode 100644 z_libwebp_src_enc_config_enc.c create mode 100644 z_libwebp_src_enc_cost_enc.c create mode 100644 z_libwebp_src_enc_filter_enc.c create mode 100644 z_libwebp_src_enc_frame_enc.c create mode 100644 z_libwebp_src_enc_histogram_enc.c create mode 100644 z_libwebp_src_enc_iterator_enc.c create mode 100644 z_libwebp_src_enc_near_lossless_enc.c create mode 100644 z_libwebp_src_enc_picture_csp_enc.c create mode 100644 z_libwebp_src_enc_picture_enc.c create mode 100644 z_libwebp_src_enc_picture_psnr_enc.c create mode 100644 z_libwebp_src_enc_picture_rescale_enc.c create mode 100644 z_libwebp_src_enc_picture_tools_enc.c create mode 100644 z_libwebp_src_enc_predictor_enc.c create mode 100644 z_libwebp_src_enc_quant_enc.c create mode 100644 z_libwebp_src_enc_syntax_enc.c create mode 100644 z_libwebp_src_enc_token_enc.c create mode 100644 z_libwebp_src_enc_tree_enc.c create mode 100644 z_libwebp_src_enc_vp8l_enc.c create mode 100644 z_libwebp_src_enc_webp_enc.c create mode 100644 z_libwebp_src_mux_anim_encode.c create mode 100644 z_libwebp_src_mux_muxedit.c create mode 100644 z_libwebp_src_mux_muxinternal.c create mode 100644 z_libwebp_src_mux_muxread.c create mode 100644 z_libwebp_src_utils_bit_reader_utils.c create mode 100644 z_libwebp_src_utils_bit_writer_utils.c create mode 100644 z_libwebp_src_utils_color_cache_utils.c create mode 100644 z_libwebp_src_utils_filters_utils.c create mode 100644 z_libwebp_src_utils_huffman_encode_utils.c create mode 100644 z_libwebp_src_utils_huffman_utils.c create mode 100644 z_libwebp_src_utils_quant_levels_dec_utils.c create mode 100644 z_libwebp_src_utils_quant_levels_utils.c create mode 100644 z_libwebp_src_utils_random_utils.c create mode 100644 z_libwebp_src_utils_rescaler_utils.c create mode 100644 z_libwebp_src_utils_thread_utils.c create mode 100644 z_libwebp_src_utils_utils.c diff --git a/z_libwebp_src_dec_alpha_dec.c b/z_libwebp_src_dec_alpha_dec.c new file mode 100644 index 0000000..b05768c --- /dev/null +++ b/z_libwebp_src_dec_alpha_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/alpha_dec.c" diff --git a/z_libwebp_src_dec_buffer_dec.c b/z_libwebp_src_dec_buffer_dec.c new file mode 100644 index 0000000..a163924 --- /dev/null +++ b/z_libwebp_src_dec_buffer_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/buffer_dec.c" diff --git a/z_libwebp_src_dec_frame_dec.c b/z_libwebp_src_dec_frame_dec.c new file mode 100644 index 0000000..e8b8d1c --- /dev/null +++ b/z_libwebp_src_dec_frame_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/frame_dec.c" diff --git a/z_libwebp_src_dec_idec_dec.c b/z_libwebp_src_dec_idec_dec.c new file mode 100644 index 0000000..dc7ebaa --- /dev/null +++ b/z_libwebp_src_dec_idec_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/idec_dec.c" diff --git a/z_libwebp_src_dec_io_dec.c b/z_libwebp_src_dec_io_dec.c new file mode 100644 index 0000000..32daeec --- /dev/null +++ b/z_libwebp_src_dec_io_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/io_dec.c" diff --git a/z_libwebp_src_dec_quant_dec.c b/z_libwebp_src_dec_quant_dec.c new file mode 100644 index 0000000..9201fd8 --- /dev/null +++ b/z_libwebp_src_dec_quant_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/quant_dec.c" diff --git a/z_libwebp_src_dec_tree_dec.c b/z_libwebp_src_dec_tree_dec.c new file mode 100644 index 0000000..530575d --- /dev/null +++ b/z_libwebp_src_dec_tree_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/tree_dec.c" diff --git a/z_libwebp_src_dec_vp8_dec.c b/z_libwebp_src_dec_vp8_dec.c new file mode 100644 index 0000000..488fb8b --- /dev/null +++ b/z_libwebp_src_dec_vp8_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/vp8_dec.c" diff --git a/z_libwebp_src_dec_vp8l_dec.c b/z_libwebp_src_dec_vp8l_dec.c new file mode 100644 index 0000000..94f04cc --- /dev/null +++ b/z_libwebp_src_dec_vp8l_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/vp8l_dec.c" diff --git a/z_libwebp_src_dec_webp_dec.c b/z_libwebp_src_dec_webp_dec.c new file mode 100644 index 0000000..71e7dab --- /dev/null +++ b/z_libwebp_src_dec_webp_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dec/webp_dec.c" diff --git a/z_libwebp_src_demux_anim_decode.c b/z_libwebp_src_demux_anim_decode.c new file mode 100644 index 0000000..8802c83 --- /dev/null +++ b/z_libwebp_src_demux_anim_decode.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/demux/anim_decode.c" diff --git a/z_libwebp_src_demux_demux.c b/z_libwebp_src_demux_demux.c new file mode 100644 index 0000000..a021b1a --- /dev/null +++ b/z_libwebp_src_demux_demux.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/demux/demux.c" diff --git a/z_libwebp_src_dsp_alpha_processing.c b/z_libwebp_src_dsp_alpha_processing.c new file mode 100644 index 0000000..f9e0d55 --- /dev/null +++ b/z_libwebp_src_dsp_alpha_processing.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/alpha_processing.c" diff --git a/z_libwebp_src_dsp_alpha_processing_mips_dsp_r2.c b/z_libwebp_src_dsp_alpha_processing_mips_dsp_r2.c new file mode 100644 index 0000000..f4e6245 --- /dev/null +++ b/z_libwebp_src_dsp_alpha_processing_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/alpha_processing_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_alpha_processing_neon.c b/z_libwebp_src_dsp_alpha_processing_neon.c new file mode 100644 index 0000000..71f15a9 --- /dev/null +++ b/z_libwebp_src_dsp_alpha_processing_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/alpha_processing_neon.c" diff --git a/z_libwebp_src_dsp_alpha_processing_sse2.c b/z_libwebp_src_dsp_alpha_processing_sse2.c new file mode 100644 index 0000000..5fbea84 --- /dev/null +++ b/z_libwebp_src_dsp_alpha_processing_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/alpha_processing_sse2.c" diff --git a/z_libwebp_src_dsp_alpha_processing_sse41.c b/z_libwebp_src_dsp_alpha_processing_sse41.c new file mode 100644 index 0000000..e97f879 --- /dev/null +++ b/z_libwebp_src_dsp_alpha_processing_sse41.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/alpha_processing_sse41.c" diff --git a/z_libwebp_src_dsp_cost.c b/z_libwebp_src_dsp_cost.c new file mode 100644 index 0000000..1c8b81f --- /dev/null +++ b/z_libwebp_src_dsp_cost.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/cost.c" diff --git a/z_libwebp_src_dsp_cost_mips32.c b/z_libwebp_src_dsp_cost_mips32.c new file mode 100644 index 0000000..c31851a --- /dev/null +++ b/z_libwebp_src_dsp_cost_mips32.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/cost_mips32.c" diff --git a/z_libwebp_src_dsp_cost_mips_dsp_r2.c b/z_libwebp_src_dsp_cost_mips_dsp_r2.c new file mode 100644 index 0000000..d1b9a31 --- /dev/null +++ b/z_libwebp_src_dsp_cost_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/cost_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_cost_neon.c b/z_libwebp_src_dsp_cost_neon.c new file mode 100644 index 0000000..ca02118 --- /dev/null +++ b/z_libwebp_src_dsp_cost_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/cost_neon.c" diff --git a/z_libwebp_src_dsp_cost_sse2.c b/z_libwebp_src_dsp_cost_sse2.c new file mode 100644 index 0000000..7421601 --- /dev/null +++ b/z_libwebp_src_dsp_cost_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/cost_sse2.c" diff --git a/z_libwebp_src_dsp_cpu.c b/z_libwebp_src_dsp_cpu.c new file mode 100644 index 0000000..cd22977 --- /dev/null +++ b/z_libwebp_src_dsp_cpu.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/cpu.c" diff --git a/z_libwebp_src_dsp_dec.c b/z_libwebp_src_dsp_dec.c new file mode 100644 index 0000000..c13abe9 --- /dev/null +++ b/z_libwebp_src_dsp_dec.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/dec.c" diff --git a/z_libwebp_src_dsp_dec_clip_tables.c b/z_libwebp_src_dsp_dec_clip_tables.c new file mode 100644 index 0000000..7eb2a58 --- /dev/null +++ b/z_libwebp_src_dsp_dec_clip_tables.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/dec_clip_tables.c" diff --git a/z_libwebp_src_dsp_dec_mips32.c b/z_libwebp_src_dsp_dec_mips32.c new file mode 100644 index 0000000..f76cf8f --- /dev/null +++ b/z_libwebp_src_dsp_dec_mips32.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/dec_mips32.c" diff --git a/z_libwebp_src_dsp_dec_mips_dsp_r2.c b/z_libwebp_src_dsp_dec_mips_dsp_r2.c new file mode 100644 index 0000000..0a9ffcd --- /dev/null +++ b/z_libwebp_src_dsp_dec_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/dec_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_dec_msa.c b/z_libwebp_src_dsp_dec_msa.c new file mode 100644 index 0000000..d6bbe88 --- /dev/null +++ b/z_libwebp_src_dsp_dec_msa.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/dec_msa.c" diff --git a/z_libwebp_src_dsp_dec_neon.c b/z_libwebp_src_dsp_dec_neon.c new file mode 100644 index 0000000..db44119 --- /dev/null +++ b/z_libwebp_src_dsp_dec_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/dec_neon.c" diff --git a/z_libwebp_src_dsp_dec_sse2.c b/z_libwebp_src_dsp_dec_sse2.c new file mode 100644 index 0000000..559eb48 --- /dev/null +++ b/z_libwebp_src_dsp_dec_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/dec_sse2.c" diff --git a/z_libwebp_src_dsp_dec_sse41.c b/z_libwebp_src_dsp_dec_sse41.c new file mode 100644 index 0000000..f015c50 --- /dev/null +++ b/z_libwebp_src_dsp_dec_sse41.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/dec_sse41.c" diff --git a/z_libwebp_src_dsp_enc.c b/z_libwebp_src_dsp_enc.c new file mode 100644 index 0000000..e493c05 --- /dev/null +++ b/z_libwebp_src_dsp_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/enc.c" diff --git a/z_libwebp_src_dsp_enc_mips32.c b/z_libwebp_src_dsp_enc_mips32.c new file mode 100644 index 0000000..8161712 --- /dev/null +++ b/z_libwebp_src_dsp_enc_mips32.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/enc_mips32.c" diff --git a/z_libwebp_src_dsp_enc_mips_dsp_r2.c b/z_libwebp_src_dsp_enc_mips_dsp_r2.c new file mode 100644 index 0000000..b23116a --- /dev/null +++ b/z_libwebp_src_dsp_enc_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/enc_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_enc_msa.c b/z_libwebp_src_dsp_enc_msa.c new file mode 100644 index 0000000..3c7ce68 --- /dev/null +++ b/z_libwebp_src_dsp_enc_msa.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/enc_msa.c" diff --git a/z_libwebp_src_dsp_enc_neon.c b/z_libwebp_src_dsp_enc_neon.c new file mode 100644 index 0000000..e8cfbf7 --- /dev/null +++ b/z_libwebp_src_dsp_enc_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/enc_neon.c" diff --git a/z_libwebp_src_dsp_enc_sse2.c b/z_libwebp_src_dsp_enc_sse2.c new file mode 100644 index 0000000..24baa6e --- /dev/null +++ b/z_libwebp_src_dsp_enc_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/enc_sse2.c" diff --git a/z_libwebp_src_dsp_enc_sse41.c b/z_libwebp_src_dsp_enc_sse41.c new file mode 100644 index 0000000..ed9eaf9 --- /dev/null +++ b/z_libwebp_src_dsp_enc_sse41.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/enc_sse41.c" diff --git a/z_libwebp_src_dsp_filters.c b/z_libwebp_src_dsp_filters.c new file mode 100644 index 0000000..21f5064 --- /dev/null +++ b/z_libwebp_src_dsp_filters.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/filters.c" diff --git a/z_libwebp_src_dsp_filters_mips_dsp_r2.c b/z_libwebp_src_dsp_filters_mips_dsp_r2.c new file mode 100644 index 0000000..7a557ad --- /dev/null +++ b/z_libwebp_src_dsp_filters_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/filters_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_filters_msa.c b/z_libwebp_src_dsp_filters_msa.c new file mode 100644 index 0000000..0ec761c --- /dev/null +++ b/z_libwebp_src_dsp_filters_msa.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/filters_msa.c" diff --git a/z_libwebp_src_dsp_filters_neon.c b/z_libwebp_src_dsp_filters_neon.c new file mode 100644 index 0000000..de3a322 --- /dev/null +++ b/z_libwebp_src_dsp_filters_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/filters_neon.c" diff --git a/z_libwebp_src_dsp_filters_sse2.c b/z_libwebp_src_dsp_filters_sse2.c new file mode 100644 index 0000000..c99fbb3 --- /dev/null +++ b/z_libwebp_src_dsp_filters_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/filters_sse2.c" diff --git a/z_libwebp_src_dsp_lossless.c b/z_libwebp_src_dsp_lossless.c new file mode 100644 index 0000000..129ed4b --- /dev/null +++ b/z_libwebp_src_dsp_lossless.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless.c" diff --git a/z_libwebp_src_dsp_lossless_enc.c b/z_libwebp_src_dsp_lossless_enc.c new file mode 100644 index 0000000..a4d274b --- /dev/null +++ b/z_libwebp_src_dsp_lossless_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_enc.c" diff --git a/z_libwebp_src_dsp_lossless_enc_mips32.c b/z_libwebp_src_dsp_lossless_enc_mips32.c new file mode 100644 index 0000000..ed9af3d --- /dev/null +++ b/z_libwebp_src_dsp_lossless_enc_mips32.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_enc_mips32.c" diff --git a/z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c b/z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c new file mode 100644 index 0000000..404c32e --- /dev/null +++ b/z_libwebp_src_dsp_lossless_enc_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_enc_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_lossless_enc_msa.c b/z_libwebp_src_dsp_lossless_enc_msa.c new file mode 100644 index 0000000..5b8949c --- /dev/null +++ b/z_libwebp_src_dsp_lossless_enc_msa.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_enc_msa.c" diff --git a/z_libwebp_src_dsp_lossless_enc_neon.c b/z_libwebp_src_dsp_lossless_enc_neon.c new file mode 100644 index 0000000..653ab9e --- /dev/null +++ b/z_libwebp_src_dsp_lossless_enc_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_enc_neon.c" diff --git a/z_libwebp_src_dsp_lossless_enc_sse2.c b/z_libwebp_src_dsp_lossless_enc_sse2.c new file mode 100644 index 0000000..144b060 --- /dev/null +++ b/z_libwebp_src_dsp_lossless_enc_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_enc_sse2.c" diff --git a/z_libwebp_src_dsp_lossless_enc_sse41.c b/z_libwebp_src_dsp_lossless_enc_sse41.c new file mode 100644 index 0000000..71d0594 --- /dev/null +++ b/z_libwebp_src_dsp_lossless_enc_sse41.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_enc_sse41.c" diff --git a/z_libwebp_src_dsp_lossless_mips_dsp_r2.c b/z_libwebp_src_dsp_lossless_mips_dsp_r2.c new file mode 100644 index 0000000..7de85c1 --- /dev/null +++ b/z_libwebp_src_dsp_lossless_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_lossless_msa.c b/z_libwebp_src_dsp_lossless_msa.c new file mode 100644 index 0000000..24c9047 --- /dev/null +++ b/z_libwebp_src_dsp_lossless_msa.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_msa.c" diff --git a/z_libwebp_src_dsp_lossless_neon.c b/z_libwebp_src_dsp_lossless_neon.c new file mode 100644 index 0000000..c8cab2b --- /dev/null +++ b/z_libwebp_src_dsp_lossless_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_neon.c" diff --git a/z_libwebp_src_dsp_lossless_sse2.c b/z_libwebp_src_dsp_lossless_sse2.c new file mode 100644 index 0000000..92dc460 --- /dev/null +++ b/z_libwebp_src_dsp_lossless_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_sse2.c" diff --git a/z_libwebp_src_dsp_lossless_sse41.c b/z_libwebp_src_dsp_lossless_sse41.c new file mode 100644 index 0000000..44ae72f --- /dev/null +++ b/z_libwebp_src_dsp_lossless_sse41.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/lossless_sse41.c" diff --git a/z_libwebp_src_dsp_rescaler.c b/z_libwebp_src_dsp_rescaler.c new file mode 100644 index 0000000..dbb9ff8 --- /dev/null +++ b/z_libwebp_src_dsp_rescaler.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/rescaler.c" diff --git a/z_libwebp_src_dsp_rescaler_mips32.c b/z_libwebp_src_dsp_rescaler_mips32.c new file mode 100644 index 0000000..8222583 --- /dev/null +++ b/z_libwebp_src_dsp_rescaler_mips32.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/rescaler_mips32.c" diff --git a/z_libwebp_src_dsp_rescaler_mips_dsp_r2.c b/z_libwebp_src_dsp_rescaler_mips_dsp_r2.c new file mode 100644 index 0000000..2f0694b --- /dev/null +++ b/z_libwebp_src_dsp_rescaler_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/rescaler_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_rescaler_msa.c b/z_libwebp_src_dsp_rescaler_msa.c new file mode 100644 index 0000000..568f722 --- /dev/null +++ b/z_libwebp_src_dsp_rescaler_msa.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/rescaler_msa.c" diff --git a/z_libwebp_src_dsp_rescaler_neon.c b/z_libwebp_src_dsp_rescaler_neon.c new file mode 100644 index 0000000..31ae2dc --- /dev/null +++ b/z_libwebp_src_dsp_rescaler_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/rescaler_neon.c" diff --git a/z_libwebp_src_dsp_rescaler_sse2.c b/z_libwebp_src_dsp_rescaler_sse2.c new file mode 100644 index 0000000..0db6a8e --- /dev/null +++ b/z_libwebp_src_dsp_rescaler_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/rescaler_sse2.c" diff --git a/z_libwebp_src_dsp_ssim.c b/z_libwebp_src_dsp_ssim.c new file mode 100644 index 0000000..a3c8c45 --- /dev/null +++ b/z_libwebp_src_dsp_ssim.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/ssim.c" diff --git a/z_libwebp_src_dsp_ssim_sse2.c b/z_libwebp_src_dsp_ssim_sse2.c new file mode 100644 index 0000000..f4f2288 --- /dev/null +++ b/z_libwebp_src_dsp_ssim_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/ssim_sse2.c" diff --git a/z_libwebp_src_dsp_upsampling.c b/z_libwebp_src_dsp_upsampling.c new file mode 100644 index 0000000..a62836c --- /dev/null +++ b/z_libwebp_src_dsp_upsampling.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/upsampling.c" diff --git a/z_libwebp_src_dsp_upsampling_mips_dsp_r2.c b/z_libwebp_src_dsp_upsampling_mips_dsp_r2.c new file mode 100644 index 0000000..59ad3c9 --- /dev/null +++ b/z_libwebp_src_dsp_upsampling_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/upsampling_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_upsampling_msa.c b/z_libwebp_src_dsp_upsampling_msa.c new file mode 100644 index 0000000..2e29bed --- /dev/null +++ b/z_libwebp_src_dsp_upsampling_msa.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/upsampling_msa.c" diff --git a/z_libwebp_src_dsp_upsampling_neon.c b/z_libwebp_src_dsp_upsampling_neon.c new file mode 100644 index 0000000..d434489 --- /dev/null +++ b/z_libwebp_src_dsp_upsampling_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/upsampling_neon.c" diff --git a/z_libwebp_src_dsp_upsampling_sse2.c b/z_libwebp_src_dsp_upsampling_sse2.c new file mode 100644 index 0000000..1b82c41 --- /dev/null +++ b/z_libwebp_src_dsp_upsampling_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/upsampling_sse2.c" diff --git a/z_libwebp_src_dsp_upsampling_sse41.c b/z_libwebp_src_dsp_upsampling_sse41.c new file mode 100644 index 0000000..d85afb7 --- /dev/null +++ b/z_libwebp_src_dsp_upsampling_sse41.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/upsampling_sse41.c" diff --git a/z_libwebp_src_dsp_yuv.c b/z_libwebp_src_dsp_yuv.c new file mode 100644 index 0000000..919d42e --- /dev/null +++ b/z_libwebp_src_dsp_yuv.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/yuv.c" diff --git a/z_libwebp_src_dsp_yuv_mips32.c b/z_libwebp_src_dsp_yuv_mips32.c new file mode 100644 index 0000000..c8a59ab --- /dev/null +++ b/z_libwebp_src_dsp_yuv_mips32.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/yuv_mips32.c" diff --git a/z_libwebp_src_dsp_yuv_mips_dsp_r2.c b/z_libwebp_src_dsp_yuv_mips_dsp_r2.c new file mode 100644 index 0000000..a1c57f5 --- /dev/null +++ b/z_libwebp_src_dsp_yuv_mips_dsp_r2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/yuv_mips_dsp_r2.c" diff --git a/z_libwebp_src_dsp_yuv_neon.c b/z_libwebp_src_dsp_yuv_neon.c new file mode 100644 index 0000000..566b137 --- /dev/null +++ b/z_libwebp_src_dsp_yuv_neon.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/yuv_neon.c" diff --git a/z_libwebp_src_dsp_yuv_sse2.c b/z_libwebp_src_dsp_yuv_sse2.c new file mode 100644 index 0000000..6152b8f --- /dev/null +++ b/z_libwebp_src_dsp_yuv_sse2.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/yuv_sse2.c" diff --git a/z_libwebp_src_dsp_yuv_sse41.c b/z_libwebp_src_dsp_yuv_sse41.c new file mode 100644 index 0000000..0d09155 --- /dev/null +++ b/z_libwebp_src_dsp_yuv_sse41.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/dsp/yuv_sse41.c" diff --git a/z_libwebp_src_enc_alpha_enc.c b/z_libwebp_src_enc_alpha_enc.c new file mode 100644 index 0000000..5a71453 --- /dev/null +++ b/z_libwebp_src_enc_alpha_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/alpha_enc.c" diff --git a/z_libwebp_src_enc_analysis_enc.c b/z_libwebp_src_enc_analysis_enc.c new file mode 100644 index 0000000..4765748 --- /dev/null +++ b/z_libwebp_src_enc_analysis_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/analysis_enc.c" diff --git a/z_libwebp_src_enc_backward_references_cost_enc.c b/z_libwebp_src_enc_backward_references_cost_enc.c new file mode 100644 index 0000000..52412b8 --- /dev/null +++ b/z_libwebp_src_enc_backward_references_cost_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/backward_references_cost_enc.c" diff --git a/z_libwebp_src_enc_backward_references_enc.c b/z_libwebp_src_enc_backward_references_enc.c new file mode 100644 index 0000000..8c80a73 --- /dev/null +++ b/z_libwebp_src_enc_backward_references_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/backward_references_enc.c" diff --git a/z_libwebp_src_enc_config_enc.c b/z_libwebp_src_enc_config_enc.c new file mode 100644 index 0000000..647ba58 --- /dev/null +++ b/z_libwebp_src_enc_config_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/config_enc.c" diff --git a/z_libwebp_src_enc_cost_enc.c b/z_libwebp_src_enc_cost_enc.c new file mode 100644 index 0000000..0170ca9 --- /dev/null +++ b/z_libwebp_src_enc_cost_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/cost_enc.c" diff --git a/z_libwebp_src_enc_filter_enc.c b/z_libwebp_src_enc_filter_enc.c new file mode 100644 index 0000000..7bc3ce7 --- /dev/null +++ b/z_libwebp_src_enc_filter_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/filter_enc.c" diff --git a/z_libwebp_src_enc_frame_enc.c b/z_libwebp_src_enc_frame_enc.c new file mode 100644 index 0000000..ef907cb --- /dev/null +++ b/z_libwebp_src_enc_frame_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/frame_enc.c" diff --git a/z_libwebp_src_enc_histogram_enc.c b/z_libwebp_src_enc_histogram_enc.c new file mode 100644 index 0000000..f836d83 --- /dev/null +++ b/z_libwebp_src_enc_histogram_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/histogram_enc.c" diff --git a/z_libwebp_src_enc_iterator_enc.c b/z_libwebp_src_enc_iterator_enc.c new file mode 100644 index 0000000..180e998 --- /dev/null +++ b/z_libwebp_src_enc_iterator_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/iterator_enc.c" diff --git a/z_libwebp_src_enc_near_lossless_enc.c b/z_libwebp_src_enc_near_lossless_enc.c new file mode 100644 index 0000000..0795418 --- /dev/null +++ b/z_libwebp_src_enc_near_lossless_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/near_lossless_enc.c" diff --git a/z_libwebp_src_enc_picture_csp_enc.c b/z_libwebp_src_enc_picture_csp_enc.c new file mode 100644 index 0000000..776d0d6 --- /dev/null +++ b/z_libwebp_src_enc_picture_csp_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/picture_csp_enc.c" diff --git a/z_libwebp_src_enc_picture_enc.c b/z_libwebp_src_enc_picture_enc.c new file mode 100644 index 0000000..1427611 --- /dev/null +++ b/z_libwebp_src_enc_picture_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/picture_enc.c" diff --git a/z_libwebp_src_enc_picture_psnr_enc.c b/z_libwebp_src_enc_picture_psnr_enc.c new file mode 100644 index 0000000..dc67889 --- /dev/null +++ b/z_libwebp_src_enc_picture_psnr_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/picture_psnr_enc.c" diff --git a/z_libwebp_src_enc_picture_rescale_enc.c b/z_libwebp_src_enc_picture_rescale_enc.c new file mode 100644 index 0000000..a22e9dd --- /dev/null +++ b/z_libwebp_src_enc_picture_rescale_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/picture_rescale_enc.c" diff --git a/z_libwebp_src_enc_picture_tools_enc.c b/z_libwebp_src_enc_picture_tools_enc.c new file mode 100644 index 0000000..aa45404 --- /dev/null +++ b/z_libwebp_src_enc_picture_tools_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/picture_tools_enc.c" diff --git a/z_libwebp_src_enc_predictor_enc.c b/z_libwebp_src_enc_predictor_enc.c new file mode 100644 index 0000000..6742787 --- /dev/null +++ b/z_libwebp_src_enc_predictor_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/predictor_enc.c" diff --git a/z_libwebp_src_enc_quant_enc.c b/z_libwebp_src_enc_quant_enc.c new file mode 100644 index 0000000..351064d --- /dev/null +++ b/z_libwebp_src_enc_quant_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/quant_enc.c" diff --git a/z_libwebp_src_enc_syntax_enc.c b/z_libwebp_src_enc_syntax_enc.c new file mode 100644 index 0000000..03cb202 --- /dev/null +++ b/z_libwebp_src_enc_syntax_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/syntax_enc.c" diff --git a/z_libwebp_src_enc_token_enc.c b/z_libwebp_src_enc_token_enc.c new file mode 100644 index 0000000..a2f7eb8 --- /dev/null +++ b/z_libwebp_src_enc_token_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/token_enc.c" diff --git a/z_libwebp_src_enc_tree_enc.c b/z_libwebp_src_enc_tree_enc.c new file mode 100644 index 0000000..447f341 --- /dev/null +++ b/z_libwebp_src_enc_tree_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/tree_enc.c" diff --git a/z_libwebp_src_enc_vp8l_enc.c b/z_libwebp_src_enc_vp8l_enc.c new file mode 100644 index 0000000..a8224e9 --- /dev/null +++ b/z_libwebp_src_enc_vp8l_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/vp8l_enc.c" diff --git a/z_libwebp_src_enc_webp_enc.c b/z_libwebp_src_enc_webp_enc.c new file mode 100644 index 0000000..81596c5 --- /dev/null +++ b/z_libwebp_src_enc_webp_enc.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/enc/webp_enc.c" diff --git a/z_libwebp_src_mux_anim_encode.c b/z_libwebp_src_mux_anim_encode.c new file mode 100644 index 0000000..68d997a --- /dev/null +++ b/z_libwebp_src_mux_anim_encode.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/mux/anim_encode.c" diff --git a/z_libwebp_src_mux_muxedit.c b/z_libwebp_src_mux_muxedit.c new file mode 100644 index 0000000..8e8d4f7 --- /dev/null +++ b/z_libwebp_src_mux_muxedit.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/mux/muxedit.c" diff --git a/z_libwebp_src_mux_muxinternal.c b/z_libwebp_src_mux_muxinternal.c new file mode 100644 index 0000000..7eeba4b --- /dev/null +++ b/z_libwebp_src_mux_muxinternal.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/mux/muxinternal.c" diff --git a/z_libwebp_src_mux_muxread.c b/z_libwebp_src_mux_muxread.c new file mode 100644 index 0000000..c26864e --- /dev/null +++ b/z_libwebp_src_mux_muxread.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/mux/muxread.c" diff --git a/z_libwebp_src_utils_bit_reader_utils.c b/z_libwebp_src_utils_bit_reader_utils.c new file mode 100644 index 0000000..d40568c --- /dev/null +++ b/z_libwebp_src_utils_bit_reader_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/bit_reader_utils.c" diff --git a/z_libwebp_src_utils_bit_writer_utils.c b/z_libwebp_src_utils_bit_writer_utils.c new file mode 100644 index 0000000..ebcde83 --- /dev/null +++ b/z_libwebp_src_utils_bit_writer_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/bit_writer_utils.c" diff --git a/z_libwebp_src_utils_color_cache_utils.c b/z_libwebp_src_utils_color_cache_utils.c new file mode 100644 index 0000000..33d38d1 --- /dev/null +++ b/z_libwebp_src_utils_color_cache_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/color_cache_utils.c" diff --git a/z_libwebp_src_utils_filters_utils.c b/z_libwebp_src_utils_filters_utils.c new file mode 100644 index 0000000..fe95f11 --- /dev/null +++ b/z_libwebp_src_utils_filters_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/filters_utils.c" diff --git a/z_libwebp_src_utils_huffman_encode_utils.c b/z_libwebp_src_utils_huffman_encode_utils.c new file mode 100644 index 0000000..34b820a --- /dev/null +++ b/z_libwebp_src_utils_huffman_encode_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/huffman_encode_utils.c" diff --git a/z_libwebp_src_utils_huffman_utils.c b/z_libwebp_src_utils_huffman_utils.c new file mode 100644 index 0000000..0eb6581 --- /dev/null +++ b/z_libwebp_src_utils_huffman_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/huffman_utils.c" diff --git a/z_libwebp_src_utils_quant_levels_dec_utils.c b/z_libwebp_src_utils_quant_levels_dec_utils.c new file mode 100644 index 0000000..735dfe3 --- /dev/null +++ b/z_libwebp_src_utils_quant_levels_dec_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/quant_levels_dec_utils.c" diff --git a/z_libwebp_src_utils_quant_levels_utils.c b/z_libwebp_src_utils_quant_levels_utils.c new file mode 100644 index 0000000..6e7c097 --- /dev/null +++ b/z_libwebp_src_utils_quant_levels_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/quant_levels_utils.c" diff --git a/z_libwebp_src_utils_random_utils.c b/z_libwebp_src_utils_random_utils.c new file mode 100644 index 0000000..b15b019 --- /dev/null +++ b/z_libwebp_src_utils_random_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/random_utils.c" diff --git a/z_libwebp_src_utils_rescaler_utils.c b/z_libwebp_src_utils_rescaler_utils.c new file mode 100644 index 0000000..0fab07d --- /dev/null +++ b/z_libwebp_src_utils_rescaler_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/rescaler_utils.c" diff --git a/z_libwebp_src_utils_thread_utils.c b/z_libwebp_src_utils_thread_utils.c new file mode 100644 index 0000000..989a7d4 --- /dev/null +++ b/z_libwebp_src_utils_thread_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/thread_utils.c" diff --git a/z_libwebp_src_utils_utils.c b/z_libwebp_src_utils_utils.c new file mode 100644 index 0000000..36c5ffc --- /dev/null +++ b/z_libwebp_src_utils_utils.c @@ -0,0 +1,9 @@ +// Copyright 2014 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Auto Generated By 'go generate', DONOT EDIT!!! + +// +build cgo + +#include "src/utils/utils.c" From b6fdde9cc92b5b9fa6dce9d886d9125b80c85a22 Mon Sep 17 00:00:00 2001 From: Pablo Lagos Date: Mon, 15 Nov 2021 12:45:26 -0300 Subject: [PATCH 3/4] libwebp version 1.2.1 --- capi.go | 1 + 1 file changed, 1 insertion(+) diff --git a/capi.go b/capi.go index 274b266..8e9bee5 100644 --- a/capi.go +++ b/capi.go @@ -11,6 +11,7 @@ // Go1.6: // https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md // +// libwebp version 1.2.1 package webp From ee22b020e1d80e286dd2228d02d2aa49177c09e9 Mon Sep 17 00:00:00 2001 From: Pablo Lagos Date: Mon, 15 Nov 2021 12:46:45 -0300 Subject: [PATCH 4/4] libwebp version 1.2.1 --- capi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/capi.c b/capi.c index 4171773..309eebe 100644 --- a/capi.c +++ b/capi.c @@ -1,6 +1,7 @@ // Copyright 2014 . All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// version libwebp 1.2.1 // +build cgo

BVRfEUc_mbZRPVjmT|!*cPakqufvB>`QIPP=hP2*miB> zQc9W3dmWHU6;9gxt4u)Bp7bL33Gb)eOqtb@2}bwsQ$LE zBZ6aGi~i{K{pR=l<9nQ&pR1SU-9S-fY(~Qwmww#uDd_3(sqKi%&gH_bbXcB`kLLR; ze)0C;o2-_sj6QSu!2JVWV`5<@+9ky)ogv@}g&qw!sG|t*7g3G`%{DO|$V`z+K(NW0 zPIMWPo=KSUzG*^6awPg`DU4j8Bg<$@-1684C=F2(v4Cbm5swY!^47V&@;rJSb~HD} z(!F#N?*QJ=8BL1NDS4dBA}K-)Q{-R}rsT!ReI_BXpr`{(`0mP?TVzyJO2D^(~9Ht30l5 zC4&2e59{K;dyX^JC+2OHfN`>LnhVyMhX*avovUW!LdT?D=R&z^KZg}H;9w9(@SfA8D5e`m!dWDcb_ zh3BAGrbd_J56|zc{QCCm(^ib>{#F$RnIKLIAxO6fF|Ja3&H^p3j&(hnK$|C$E)^o4su<`Vs^sIUewrbV zQN1jT5&DitR>m#g-(IhkY}@wE4{xEp9_m+=*Y@XGVI2qCm;K*={N~Ga)F1tO4-+o@ zsvrI7#~YtDb?g4z1}lo@)VGRWhP&5*3Z{fZ`+hy`pBB!swS^jCPJNq@C_nKbE9?N- zu-m4U1E-qtAuv=RC`p_1KFunV>z{RAXTmo0n2G<#UBQO%=a54f9Vf!Bj`XX; zO-IO8Y2Qj>jaHAB^LTdmSJSqG6ysB^5mko|>w%H?_ zv(sZFrgv>2)YzegeBWH3glm&8{ObMvr^#xN1x;}2D^6100WI=zUFl#XtatYhHP=Ut zy~LAYZQ)Vd%MZLf_ucKKexD?2?4(X@$`ZOxXV*ez7fz$vlJ-tFF)&mS#w@&=?FKYn zDbQFn>x%RcXv5_pIzI}W8s$~qcd(Xc^YyHGhe-s_MA{+J^dJ;pxc3uVcE0nQ!Pvq$ z?SLln9DbA@-aZd*Tso)Ek^c}fo&JTOU$M?CVSQEFbe#^oZFpgA_PpAuf=y5aO^0%f zNp*n5Us&2PpZsUo4(k4A>gV6NT>@SXQ=uT+Pg=nl`a@``U;X-+1sM;3xS*fhRIc%W zuY?kd@9BPr5I{wNMb8c7@527Q8u)zL2@)p;xCBDByb+em1*<+^f!!}eVa7Hyd2 z3VwQef5YFcE)IA`B&9pM9BWg%;!Q**f_AY~c}C%dMC(DVsqomZO!&(Vhu`Zp^;aRo zD9S+F1S}c^MuDIvK!8o+0B;OPoY2u6i8bjK_={$LN#s{le$72MDf7rmcQg#5L6Qz1 zPy&^e?3QF3_?#G35q`*>iMkMUfPYaJz6sep&Jk%O{lvtN!{Ojd19q2a$Q0P@1rISO zEuchpj~NQcs5}^kHq93A;-s%G82j6am$}l<%cT&xjm(Y*CEt18i6cAFemA#Lm%$u; zSW=?kte<>zd?n9gl?sl!rNA zw@q9yZ+xFGXD?nIK+_jEx6}mHtOp<+H}CBxixn1%oDNcW92`d zxccK2@lkUc`i5>V(fQ0z8%)AbP(;=SZ*X6*eAE2ce=w5R-KEde*&4q6Zhgv{mXa4( z;Akf%rx*pYMKH_GiqX>KG~ot&4i2jr$dGa>4E_n zf@Ea{QVcGmKm{5FPejy+?Lj^fXyGd)h%E0$j1sc3kJ4^`ZnVe>Ug(QrDFj?c`WEIQ zx`kCLl9`aNvXb<5E_{5zAEVN+Jl&%zrO>j)p@3k%B3b-!VY+ZP;*128Lto?+WJh`O zcy`;uj=#U>=exbJ(8w4AXx_t77h2P9bx;rVi@|_0-Z}Vxyb*y8=W-$jqb_J#C8fE{wly`HRLc zn|A-~wE1)9%X$ZEMf|1u+gK*_s{h@^zgLZx87k^CSFR&uDCici#KIo^5Hf9O^n9O> zVD9wx`>UH(iVq496A~CHF^-i4z*J+JK{Y}}7$eV-XlYegfrKPjBz1rR6(tiJ3{r@J zKn7{#!T?DtYB9!QG*OMHTrfmQm*lmLP>mr}rno3Wq>=>N8VE!-$B7UWF$JdA@pJ(c zfXYE2F%G7H06-WoU;#^pX$SstK_w$tFgD64To6i>iy2rhMYF27MO*c*0tF$5;-2>$ z3l>_yv^eXQtaT?N%Cc+2j2fegln!a_RKKx2a1Z6mz5nAp45bmLAX12eYRm*8xEO?x zhX3RVgn+1aK51S&4c5laL>s7ijIPzE3tFBOza*@~1O=;PsfT>Enh7z{7zZ-F zuMyRu(tW?Xk6p{f%7ZxA!px~s+MuS1WYY}3ulQNftN>#)2vW!U&+{CVm74~B?!WRY z;TCgJFC`Lqa;#6~!Ua=c2@Mqy+xQGE7=%dWA|EotFAKMVdp+L!G6|@YJhs?2YW6tx zVVz{A$^SwJz!WIE20ue5-ST8a1lky$-(Jj2Tp8TT(p1FU%-y#TM;xZW#_X1C?zw*&O zcWY9M(kc*o+Prmy4LXR-oY#H+BYZuF|DR&S`L+E$sn$jHml4~F@fY~)Fj9d5lR(Py za7|#RN=lD0r8pNcw#dswp#T(H)L^qm zARy2>Y63E-r!>bFXE;fbKJXuii(0O(dtH7sPkU~yvCa~Z;_ZosW3D-LBc^%JJRv!n zen`h@5vYn44JkpO2{0l<0F*1ZBf%2@iAZpb=?<~4Q^OKo8KIuwJ=~*BAf!on^R#!U z$EqJnuetyk$U_Zz!vR>aYe=-}24-O?>| zU5=}k85K5n_3QMjM`@Hp_j&U7UlZ8)N6((V>gefnW=&oMPJ6Z$mXaw&GUPA>W@O5% z2+h$)uldTE9u0)yTzXbd8)kVxPwM_j34dV$$$bv|?~DX5?|52wZ!o5RT=M>LfB%jD zbA9GmJ|slKN~7GH4&;vj0w6%OHwY4`JkW6DsE`#^by#^NFa?YNpL);;^+DjvT;enq zl;E#ubwwlaW7na&=vJPidng-@sy4J}&3xFiC-?dKY+qOByuIX)Zn?Ckbm}2JE{4x8*>#Xit5Y>uE7Q=MH$>=Ewkt#Za?%X;0`yTuM!p64`CPLEOlm4xj zkGoPg`8RdS83iIem+7o~j7R&`n0w*t-}n6ZH2tUR_}2p1PoJLusdStw>&h!v2KbL& zcnxlN%LF>qV$`O_`sQKFfsk&VC=8bktbo;dI!W#Bt!JP56l2nKO|TmNSM$v36Y^2L zowljtiN7r3Qdhzp+n%8-Ch=af^Y#zGHA`Hs3ZG3U(zm@_q*V~VI5p_E^iPAY1DnAi7i`5r36JrQp0oXh8%T>gfCKO+9v6C}R-PS52T%KrUF;kkT1HGdrIizgi1 zI`-G%*1FHH>WkvpR9}J%QocWv?(I}3{_E_|`}qEze{w=HvE`o2n6dYXr%G+i2)(|WoX5-x1nVsGUi8~hI-lg(wcc8LG+r`ae{j;`{GLyKJBxk4?=kj$esuNx z!{>4Ci*aCCqx^xaeaad-urU0SlCK?dCI#3cIk0k1esr5hFu|F78s&WkYS z#yt4*huUY}S(Spx@KMgISpR{W-HvfaBbuzmG-O~_bJ*$DZ2(&6KA{7SO~!&-5=_CG zl0jG|$IxJ5O|-8oam{ZdaVn9%JNqWjaIF>&r2&F2hyY{o64Q-(`TJk*{jLA6wqN=+ zKkAj6-KaHnxuf;5bz9?^jSl#z^2m-HpGR~)C7;5pvsyC!Z}79}L(h}4Vjv-7s>?`_ zxF`ew6sVP$A;MS?-6X7v>B1pt1U4$HutNZXcz|p9gWUkN;m@8SqVd8N=40!*6xFWdWFQH8N^tc`jR)S~vcNU{i%vmR z4J`JSfQ7_Q@><@aiJFIw$md70{Pj<#vh!vPclaQrGLcI@3A$6_@uk_hF( zsV{k-h=#e6R(z3ms3(C02!>$@!_Xn}l`WEo)_1+@Vtpa~N$?iLHP*^KUEbK<1c&Aq zZ|p}(gYo;^44!1#{L2pqL3fXsX$@TABuF00pXsxPifPLf(dGt6GT38qUKrYSi=YEv z?4`8TxnJcUmf5@7I&se?D*Flen82UN>Qnv_mRyAi+o$5v9*+n*?ROh7Dc?sH{_`Lm4yWF0vcQB8MS$Sp_dwGczXznX zapq%MJV95gEo@_O#(*Sarl)Oi+7w_`%TF)cTkXHHeGL0-tzo1GGWrtQMqftSU9^Xa zNGwQDp7>sOrEoq+B!ZN6QP$6aN{&nr{gh4c21Is2Orit}&;V3`1icvsWeQaLNX;N& z;;-cUOVZ?!uz#bRjHV#dPaQR2dDR^WO0thG!AB&AzK70>j?s;dSS`!vepPm4APp$}z%Lbqr-R1FTvzcj3JkRyga|&=^Cdub)m`M`9F5w8!Oy9-}=N~eShj( z|M%}*Dzw!&0do~SmI+Hihk+ukJfLIsC+yGNWBRlHX|yk~_7iG*wyMaW!R&&`TS5jBOT$2KGJOw#Gluz7?8 zF2xQyOy}NC+KPs@Dhdf&P=$`rQ-<@X4A=ui$T^~rTwoLGbx_aKeD0|2d`_w;m8qAs zPTgB%D(D(9P?mC~qDTc5ryavkkS0JO&;&J%ny`hRum?yaCigM4miKlMNRyX#L2*vz$} zHfu$G87d`l=(-BC$zhHsLFu?=Q7P&c)#cYSkex8G(u}KOrYA*U9$Zkq;;1u|2sCg} zE_lUAEOkDLHFqWTbK zIYbSr>ljb;p?Ws>`uGgY%y4~%Q7enm#8?$k4cLyt&WG!p-pnef>CfAjxW)mC)Dr)nSpCF*1 zLKL~pFHq!yhsU54rz&GsIIA)vQfbJcMuU(Jgc@8wGF|*oA%(#L3M&UWj5?%`pn)0kP|fB$#Qv|N5exM*Wm&5P1YTDW^?>Elz6D z20%AYfdUIcXiFzz%UukjwGp#|h5cL- zf#1*_Q<0d00zfDv1lAU26reOfLt>N~jI?bZ0!Cl&Q8i#D4#bner{-8Otx;U^{HZ_8lu_3kdgCji>_z^9(XcBb;Y62d1F_tVK zD&LiL5z-?$+zlBj&_TCNp7i|z?QTls6ldbPnf3^v0bpfSQ>eVA)X%W1a? zIm0*R@A}UF%Mbp|cJV7b>XK)ePG}d_s?`UvE4nghx!DAP-489-JkNMly0p92K+w9~ z^+{Ge50^X6RyBNepU)s~^LnzcyfW)D9|!*O|9Jgpe{SFYyZ`ux-}}3-(|--kpqOYG?Jk|E;b9<0Zy(3n33Ch@Q5XGj&v%uNl;X zofjOh9LnOqb%aym{)Wr{b(#P4@$TO#dtQrQzxBU-4F2?Y`)`TaYo9q(T$Z|y{Bi6l zTmZp0*GT_#R`;&1*mET9`wa9o!xRup-_y0 zXc3vQ*GG0;Ue3pg^JQ+|=;MBp*J!jo(Z_55@;dnB57VmwP_n*J7w>0}H+qUd3P zzjR$Mn@v8b5zwAY`?YcJANVZv8th@5gi42^+wDxaIE#_X-I0@eoxfrG@`KjAbA837 z=#AlO*+J#a3H>0QiLRA4PpLVMQfV1{Sfs^0<>yc3UvA-Usk`)pZ~Qag{Ecq@HsjyE zkdIS!*Bte$vry>k{onA;Pro?-dCmV7isxanifrMPSD&w`>%Z~6Nszunl!yTobXWfD ze0fIs9sq6v>yE-gE#2BbH{V{Azb{j7ffaC2UK~}3M%7dU(G?CuD}WUu1utg58%ZSr)VMOL~peZB*Q!gljw@K|TqyC~K zG3s%8N;Nf?dh*Lrhan^ZG(m)XAQJFJaX49u#}#l~m-H{m-iHPOu$-iw+pzjw#EoM@ z7s!R@3w`qj>UR0wo(Wq$@Ae3Pi_zZzUFiBQTcz|MAJao@-`R5Q7x^oagGgu=U|1wYa695K?$XxP5$hUh83&k@d@a3m$Vn zrJtGprtIx5TJhSypItYq!@qK;K|eB|es|>$o9EY0$TRLA8ZSxyV$1#!3&}tms?txlO9X?D}6CRumWe|%eRFu%QBI8&QbtGLg;wH5Pr`w#ELJ5TMM zc(?HNedhQ7@`LY3O5EgvliVi^rh^z4P-Tps6b2P6!zjHpkBCEh;gfH#>PY$|=Y;{J z)Qmq;RA~D@#8i|hs6rDm5qW4(ASz^r5z?UYup;k?j0-<}C3izOby?%gPKKjaE049r zAUJ++tHoX8Zf~pf2hU?rk5eD-)E^w*K31v<@Exh7P&4sF=*1L0 zzJ=$9Sp+Q^*muqRg}Re4GeCl-1h-&4H7QGtA6|U@EBdYOe}8Kp*bNK;;OW_st>_EO zDWN;O5Op~~ZMVns`@xSBT%i8Vf|q?}>##8ccf)KW%#fRq1*mcWatN0M0U7kDlsjeE z@IKR&^UyJRS7n*5HcuY#_XFrgg!PxBLsNg=!~ag;`-;5UZd9@K^>aLRqJrFv68C)W zQx>*1xHHU;H-!;!mqd}2IUtU0nROs8ZjZY!`=$K&oh?t4QO)y{H%NR$w@jz^2;+wD zywgChVdc&Ijd!aPOOq`%90v2-#t27!>VMZz+T#Dmq*TsOHZm$dT4oct&Jc{o_(P}13+ho6uBf00Gujb8I$#dsc8 zgTyKv>PTb?|!0XzWEh=$@1>` zxDO|YglloT(%;or0^n#a0i4_bYiKV{FhB|c)Ro#$M*x2v)R2jT83yR6E?%xxoEgN2 zq2GHh6GctxrpL=-D#z>aIQ#xtoH_R+u_$t^-}%x)E2!GxkG?(5Hy1x=N*5@EyJG?K zs1bKurx-KWdpeoPvI9kpPJvl$|3Ua(4~((6(MxDVx7Wz;wH{&$4&;YB?2$wxi&@75 z<^(<0BUV++yRJCar@@F|48|ZIb2biO{pPa>j)7xupy}a&3qesJX@n=-52ut z`n_@}`~VYSnL$b(Qe%imW~3_ygYDqXokHw-^L}3ZFT2(wFU4H0an5dVd|c?OFRq&N z6Q&Bs{;7L=_7|zJ__Kk~)SZkZWT~t%WUP=V&iTL0+pN#`p;g$B9WQ&!2>1GZvtI!FS*PX7#TzoFA#`UbBCMcO$7NRK|id9?%?0p&MXk{Jr zHXayXWuABRJm&|FCbxTkUDjvLFIWq%STV2BD|DgT7R?OTd}`oTzYuT1dZeQZz(>KT zJvESK6EBVxL+%};s9JdT@6O~8{BqCf6;rw|t>!YV&EyT|9}`*Gsqzvb)uj8now z3QE*RMy-5M50IZHI#S%}KiD^o#~|=gx?PjaD|RtOjK(wr}!*CA;nYeQ#zp*@6w>1Sh||`PN32$ccaE z%Nr}tL99(R0eOL z!p@|N@}pm;yNO=r_3ioNMxtny+34;7V}=P~36>V->J5zeE}Mw?Y{pS|MbxL=X~$QAGxe zyk?XfaU7M7Eg?(6GrK?jeg66U z{iE=SzrZR00Qxhl*FuO676VZ*OxAWJ1HxAnbkeB`Pk1Pr1fdZT{sa&yEU5b0um;5K`c}Y?>li@z7VZ7}ZKLNCglme#cS} z@j65ER$S>;!bD7tqE&`)91Skki&hv2WSF-k3Yfg=DylytBj-i^@m!xXn66cxO!FbA zhq)lpEDM*Ji%hUg=vwvRw|ghhYunevAr}07$v@&b>fe72kI60pqERSqk&wW|fVprM zrVe_Uh`7)Da`Wl@vCPjKybt@W+t1qX@SuHEQdt=0ubrGKdHUMQeIrfuavO|MKnce;=K_?d9|C7-|Tiyxn^)b_Tm1$$DQrX6Ar`$w~Nx zR#=%vraJJNydn_8jM-(>4P_fwo+4Tk$PkR6;yd6qG^Ko^CPG-JzfLa~W2xDGj`#OB zt7`nibSCwUtkYXj5XPsd$>Zy8hIYHB4Z4Iy>Syi!yJwUh{CPs=CG+z-b6huLKqy#p zKww=6Yok6EZQzaX*`Ux)dY?fL?0~6uHSt<4o0p zOY5(KZZwRP!rC+U&jj08^fyqqTEd+|m}gt;t|lOwL2EWki6&dF7IQT$#qg^UeU-`A zshDs$j=f<6Va1V54E-R%jU>IVU}CmOn)8R(j8k3`{T&&M8j51@8|2;>j_tz+D5rrN7>R;WCu!Z z?jh0v6PhB9=fPygG7cu=q_@N-&*|7uj1Nz{q^iQ-$V|18|KUoQ{PW&;{t^Fb{{Tfm zy1(d$&*Rs#cn!6i3--`J6)Rj=@R9Y{AvqA<^widIt$>(yu=ylG$RA3PCb;?b$v+FZB zp06;2m+LGiPh`#O+W!0`(R3pID6+HbgtwXdHtwA;W9f_-!0V)@^m9FhoN(rGnBOd} zop4(Y;B=W>3>-LF!U$NvFiLy%fvG1#L8#1&&{lOJ;s^dP~$1T2e@RHUK4}bIV_P1aD*xzvG`^qfS@BbH z9WsD?W&+G`0Qf$1;Z9;z#si0;ZlUtz?D@9-R=ER}!>K1A)lfURh}S?0FcVm40Cj*U z^{QbjY_UPmqCLd-!BN~a4=~LkZ5xN|5JKKL8V%Y*4WaK=_TctD^dzlp!yDv5-Y__` z-14~6W9vFkW&aH9+dffFO5e5DK}(y{24{5Dnu;U`5U@)%ifmMZr1Np$7ulIk18@j3 zH5vYJ^9}qna6lvO7g5F=lGN>YT&~~W;46&lvfbkQ7Bj!4*kZ?*syhs@qU)WMvXCmz zbjoU0z`8VG-?v~!7>h#_*YMiWg=53d8edU+kX;74R0OQJn*2iMQ<=Ya)6beW!MBgM zm*2jn*3qI}oO6Zanp_KJX7uoo_(rYc$npWRse+r2NxaLo(ks?K&-BT4o9QvHG+Y;v z_*jpwjMP!i8r$&Sb1{FUN3-bs=h6ZDW?Jk3$T} zROJ$UsMlAor#&}8|4XF*uHlVFZJ$SlJ6hWCDhH=7CXJ-9W$ErHQUlEI*LD6CWRTM2 zF&WGkUAVyvcb9BjpQoONzT5b^3749IGLQt2Fx}u7%m6Fq^*QfTj;qJePWy1DtQV$3Np=66x5h z*o$xL&Wy1(2(`Km=F#e~>s|?9hMo^1VqqAL)OPKG%yO~B(?uFYuTsNY$6ziP>7l%r zo5yoNR$5v#pacRSaKUM)odc_=3uTY(dyMDm%iA9po1ftwKDBn-b>;khjp4fa79Mi4 z9{c$!@RRcXh#t$mD;WyS%sAegnC#6z``xhuLpmL^t2VnWG3oXK!b(*k0?Ze5R_Ff2 zVi1@((toKsEW8C5-n~em;)R`(57Z^x?Mx7a3ZN#{Ngo@$!D|5C8Qr;mYjmUq53leGfi2Rpv!#G)Rr1 z<5-LckO1IOHs$GujzrV!P7V!-?c4yS&#*`w7~{_d{?(8LGN^xesDBpJpDyff)E6vv zFHw<>t&dVRzkz(U4tAdX!PMwQUDd0~Lyt~dd{6q(RdrT9#Vn%QP>G1-L*1 zVXI0D>DX+WO#p#vY~%dIyp(_|G(5Hyv;p1?8%`3Nd6gJ~58&_4<@>Y#6RvFcI~}ww zHNHj_fpdZCBiHm0px(w2W_h?$+TwuI zj8@+{m^7N>#Dx0M8;xtd`eVI>`earxyk_wgF)m+Rbh0yy2m)9;7~R5CuM-L1DtZNb zjWd?W6W#=6--}+gJuOp}Q|!~|Ay%K2VL!^F_2Iu7v$TWzWFPFc7QOsjUPhm2!W+7h z=EyRuJ*X((+HVe8AiZ_9akO>dc!FOnuj`OMRQ<(!Pt3A|%IDQuS}wQU;>_sR^x2v5 ztRGZG?OdWAxifu9~ z^%R>E$srhGlH197Jh@R@ZL8d;zWJVw|Ma!XtJ2SJ{6ANG?$1u#kjTs5hEzza5>LHK zbKc<$KRFw@clCvKPALxprPK#~4HzhlN-zpn6zD2oDPeWoa_xh0;l(Bzemr_K%RJ}F z53$v`FUPIbAU*Nj%6-54d26Sgd^^@x?6-E0mW?vw9^V=BhPFifQ;%n|>~o1814syd z)BCBuOisuLhFC0s(xp{SYP%5~jo{n$#BV=$YT>+TWKs>!a(kMdI%fk9X*=Heh=(kj zv%gud>uV{-DOw)}Vrb6xgG(+%SD&K7VLrJ6HZ_}*UBB6 zx$}qTr+NDAZB3JLN{W4`PP#NLvSezIQft@)A$6V3-vQ8xcGz@>6^V}5L)Jd9^@q9@ ztJpANj=iOI?9_!58#$e5LWB)=Jw$-I0UbP{12d6R)l_(}LRAQ+>)MmgqX>-<2-F!`j^YXeocjH9y?y8Wpd-?zvTzb3 zQ*w3HvHDzSt-2Hd!oV{Wu8p2~FPvy46qCYE83;svZ2rSy#9C~B{_;if-9ke;mJH@n zeGYD=UY|8?(5ku94=?o!WGpFD79M)Pt%8M>;yXSLjepPkmxo|-VlRugv!RcgzBbi= zZfw^Y3rHx%CQg+XJYVQ>Ge32=?AQlpM9)TfeKMaZ8mxyqN@;y4 zf9Y*aHdUpFcfwpHqDF8Aw{u{rUr5GBWnGrHv<7CV?Ood>H&@9fp92{+e3ju$w)goP z_Sw8lHsHrH`zl3b_cL0xpsa*kk<Eldha z1{ZZl#%76gZ`;FPwM`|3UVQ(hJU^f8*Kc`mkI&QL=@eC6Ip?~-p$>YZ#ZlII_&)6M zvZK~qh9K3{S`uj&cv~e(%842yYI@7XMs}QOcHj9j{bKki{d*_L3L4xK674tzgf-NP zVuT(<5QR7}(1%)6BorH;SmlQv$w0vd7pL2kkE9M&a^W;h#AFCusf7y|=1_{6OiM|o zC0vpV<5gjnQz&Hu-A-NUSZk@}K!pIqWQA6vgm?1v3QN!$!%Tqko*KM=G-@*3Bd+$AuISNSw9P!RCEQ( zK)FAK|q3HHnfvgrmTQOVUju>F{(G)+F)yr?&mqY=j*jtB+s2= zS$Ees_T>fFU;$^eG4tR1@cbYD{Ns=K2Qd>X0008;4-q>=6eyH7N)V$b+US8Pf}x8H z5CA~1H1G)*e6dIP5QRi3--VSu+kJfV`93l5EzyiQv@?hAQjqXZqXG445;9RL=jS`h{@Bs0yz`M zVWld8$LIyT$UHW)+;vab>%MQD$Y(FRh}ABMP5JGqzN(JUO|D$`($LoE=7Bfx8})n1 z;(sVg_Wu#RpKU+Ddaky)a+MKD2~9ncm<^%9jq~z{ll)CPuWP@5zaZa#fB0K@Bw`gn zmGzGWO}_2UoQvt#y~o>M=1TX*o`=pmU)ep^y?V3nxtqwI+qd7ijLzTvt25{;CgkcI zC-J@eG-@OSctvY9TxGN6LC4OREWO7}gkTFK5;us(yg+ccpX7J|Sxzd105M2~5TO!M zv8D7k)84CbdlhC$VQnJ5pSM(fv+2m-1FpOdyhpDEP+qS(XW7elkD0pDKBGoB<6f0N zZzujo#s>${#Z~6-T^j-;axeAsTj-C&-Ry1o-)p+ZQcod`r#z}zwGD@1v@wPrJ51S? zi(K68kX`}W1Gl1bQ}cnM_)yU+LqcvuK&Gws1(B1sLyy!H7)z0#K}O6o zz&(_LFs2%3rat!Q*u#++OZBaGPPV;~P?lck8}5K3oM$nCUw)kf6rcn^D3I{Lv!bJ^ zlkJshPWQ3Rj~%YKYR6fUD=}pCP$9=6c50F(l%lXM-`i@mqRwWO1KE=v#fzddQN0ujMbV`6#!3(SIjf>0UqQ_4u6r_X+`iNH=mPyT(cQ`2Sy^l$DQw z_6Q)yv-Hb|umaxrST?L!R|F*pxW|eh2tYxWNFJpuJE@t$b#~ns?$--Dzv2D&m%qMm z<@qL$ttQetp4WwX973)T9dk0PInNUomVIXNx;%2FSEJsJdPns@Y{Zf2Uqwln81!m1K3>&U&U-*K)^mc$sfEJ$RliVL5FE>{IV!zK?BQ zGxUVn)Fy4OYQ0&bP@r_A$T%@jk;uYH4P?AnLqgtW_IVI#jWn&@nt3YY?2Ldhz+u~M zMbp-?Z+~&*9PokdK}3KFM5AQ1ItT%cDGCcf6qv=qN=RFooB_@;7Ymn!76paSQ}ia1 zftzjw>G2qi#zb{SHalytkw|mI2&cS#-Mvob_J_l|S#9Ci$YK$ub`-`Z@jpQB@8XlWwhKcq#bO9;!qTvQ5D>-)P_4DFr41{ zlU)}#yw<#v;@?cq^2xb;t(Q;kg-c}C_JN038mRJso5x4R81H0vl8~l-e_=PRnRM8n zXU6a5{okN(`zl1v3{NL&`|+>CZ+l7BjZtb_6E2?%X~YeeOV{tdOEUJY=XFVRE$Ff2 zla#z!XaB#%Mnj`kz>~s=VYWa-oX!nM7M=DgP4G^$f+0B2|37a|R6p;+@8TD8H9-|F z>HJhh*Q6*p!S9?SR0tUdA`VyeKw0$M7{VkK>TTpz@}R1|&>^Nsq;9i9Y{gKUV!Bn; zo`wh7)9rGHasodoI*9(yuHN6WXEM5#B+)5~N>{xvj(FC;YQ3_`W3nZ57PEA}j0kPP z1g_fKau)SgU}k>qD`@U?YF@MA!H->PbB(Nm6NWUv20F9~KrlL#AO9lJS>I^A>O0CB zKZ(=5jU#k#_uq1+&D4&=-r-FN20XU6ja-2E;nCy+HNA!p6T@lZ+n`@CmFyM zT&)i$D{dNW!f+N1YQ{{!1n-UpF>stFNU09h2syDMhi4FwG!tN$UTNoOtp`1%;wfH@7@fq?uro0Oe`EaB`^Ty4N^oxxI`05AczAxE z`WN1dzEXP!56J!?KsQcR<0)5W*x*wvVgda~S34N74c7r+fo$TGvO+(AE(QDsj^jQW z<(k&E3}1EHECz~WNaT7q%II27tA|WSI(`1j{)K_0cnO40-5`c}-^>?)&8To;k~7Wc zrMR;3z~e#%F?PbxoGeP0La-j-p{xi{B2`Pj@!k00rS0E$eMjrql3j36FrnxXeqYYN zcVbE0HE4~B{SlAH8kuu0Jw3+epH%JN92U7zo6|6MHC|tj?He5o4Z}*&4i4HvqM8f3&jO+^*@@v+4?V zkX{ULWa~?$-PeH8F2O)v!|HaUqKWseZNd5>>**+piDZsDdTs+4ZI1rk-n;AU3ioBM zzohBR19kWqg$$shcP&O10--D{M5%S3@ogRWrC)bNe=J49h3MDLo_%Ta<|Z*95F@*% z%I;U`H(yoGu@NysnZ&M4s4dA$41A(W%4jk@(c35|dK^Ju$OQrEHbasy##&B;qf@np z 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

O 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¨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%0AH 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 z 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