diff --git a/.github/workflows/5.x.yml b/.github/workflows/5.x.yml new file mode 100644 index 0000000000..5066110c5e --- /dev/null +++ b/.github/workflows/5.x.yml @@ -0,0 +1,13 @@ +name: 5.x + +on: + schedule: + - cron: '0 3 * * *' + workflow_dispatch: + +jobs: + CodeQL: + uses: opencv/ci-gha-workflow/.github/workflows/OCV-CodeQL.yaml@main + with: + target_branch: '5.x' + workflow_branch: main diff --git a/3rdparty/libjpeg-turbo/CMakeLists.txt b/3rdparty/libjpeg-turbo/CMakeLists.txt index b08fb266ec..fe9b574805 100644 --- a/3rdparty/libjpeg-turbo/CMakeLists.txt +++ b/3rdparty/libjpeg-turbo/CMakeLists.txt @@ -18,8 +18,8 @@ if(CV_GCC AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13) ocv_warnings_disable(CMAKE_C_FLAGS -Wstringop-overflow) endif() -set(VERSION 3.1.0) -set(COPYRIGHT_YEAR "1991-2024") +set(VERSION 3.1.2) +set(COPYRIGHT_YEAR "1991-2025") string(REPLACE "." ";" VERSION_TRIPLET ${VERSION}) list(GET VERSION_TRIPLET 0 VERSION_MAJOR) list(GET VERSION_TRIPLET 1 VERSION_MINOR) diff --git a/3rdparty/libjpeg-turbo/simd/arm/aarch32/jccolext-neon.c b/3rdparty/libjpeg-turbo/simd/arm/aarch32/jccolext-neon.c index 362102d2b2..3d7134e795 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/aarch32/jccolext-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/aarch32/jccolext-neon.c @@ -1,5 +1,5 @@ /* - * jccolext-neon.c - colorspace conversion (32-bit Arm Neon) + * Colorspace conversion (32-bit Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2020, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c b/3rdparty/libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c index 153da1f1c1..491072cba9 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c @@ -1,5 +1,5 @@ /* - * jchuff-neon.c - Huffman entropy encoding (32-bit Arm Neon) + * Huffman entropy encoding (32-bit Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/aarch32/jsimd.c b/3rdparty/libjpeg-turbo/simd/arm/aarch32/jsimd.c index 7c8ea306bd..292b8a36f9 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/aarch32/jsimd.c +++ b/3rdparty/libjpeg-turbo/simd/arm/aarch32/jsimd.c @@ -1,6 +1,4 @@ /* - * jsimd_arm.c - * * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2011, Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, 2022, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/arm/aarch64/jccolext-neon.c b/3rdparty/libjpeg-turbo/simd/arm/aarch64/jccolext-neon.c index 37130c225e..c7720700d3 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/aarch64/jccolext-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/aarch64/jccolext-neon.c @@ -1,5 +1,5 @@ /* - * jccolext-neon.c - colorspace conversion (64-bit Arm Neon) + * Colorspace conversion (64-bit Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * diff --git a/3rdparty/libjpeg-turbo/simd/arm/aarch64/jchuff-neon.c b/3rdparty/libjpeg-turbo/simd/arm/aarch64/jchuff-neon.c index 11bf6dab13..5001167e2f 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/aarch64/jchuff-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/aarch64/jchuff-neon.c @@ -1,5 +1,5 @@ /* - * jchuff-neon.c - Huffman entropy encoding (64-bit Arm Neon) + * Huffman entropy encoding (64-bit Arm Neon) * * Copyright (C) 2020-2021, Arm Limited. All Rights Reserved. * Copyright (C) 2020, 2022, 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/aarch64/jsimd.c b/3rdparty/libjpeg-turbo/simd/arm/aarch64/jsimd.c index 8a6f30a1a8..e91ff83d1e 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/aarch64/jsimd.c +++ b/3rdparty/libjpeg-turbo/simd/arm/aarch64/jsimd.c @@ -1,6 +1,4 @@ /* - * jsimd_arm64.c - * * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2011, Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, 2020, 2022, 2024, diff --git a/3rdparty/libjpeg-turbo/simd/arm/jccolor-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jccolor-neon.c index d14a7bf501..4ac1e55955 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jccolor-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jccolor-neon.c @@ -1,8 +1,8 @@ /* - * jccolor-neon.c - colorspace conversion (Arm Neon) + * Colorspace conversion (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. - * Copyright (C) 2020, 2024, D. R. Commander. All Rights Reserved. + * Copyright (C) 2020, 2024-2025, D. R. Commander. All Rights Reserved. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -53,7 +53,7 @@ ALIGN(16) static const uint16_t jsimd_rgb_ycc_neon_consts[] = { /* Include inline routines for colorspace extensions. */ -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include "aarch64/jccolext-neon.c" #else #include "aarch32/jccolext-neon.c" @@ -68,7 +68,7 @@ ALIGN(16) static const uint16_t jsimd_rgb_ycc_neon_consts[] = { #define RGB_BLUE EXT_RGB_BLUE #define RGB_PIXELSIZE EXT_RGB_PIXELSIZE #define jsimd_rgb_ycc_convert_neon jsimd_extrgb_ycc_convert_neon -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include "aarch64/jccolext-neon.c" #else #include "aarch32/jccolext-neon.c" @@ -84,7 +84,7 @@ ALIGN(16) static const uint16_t jsimd_rgb_ycc_neon_consts[] = { #define RGB_BLUE EXT_RGBX_BLUE #define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE #define jsimd_rgb_ycc_convert_neon jsimd_extrgbx_ycc_convert_neon -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include "aarch64/jccolext-neon.c" #else #include "aarch32/jccolext-neon.c" @@ -100,7 +100,7 @@ ALIGN(16) static const uint16_t jsimd_rgb_ycc_neon_consts[] = { #define RGB_BLUE EXT_BGR_BLUE #define RGB_PIXELSIZE EXT_BGR_PIXELSIZE #define jsimd_rgb_ycc_convert_neon jsimd_extbgr_ycc_convert_neon -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include "aarch64/jccolext-neon.c" #else #include "aarch32/jccolext-neon.c" @@ -116,7 +116,7 @@ ALIGN(16) static const uint16_t jsimd_rgb_ycc_neon_consts[] = { #define RGB_BLUE EXT_BGRX_BLUE #define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE #define jsimd_rgb_ycc_convert_neon jsimd_extbgrx_ycc_convert_neon -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include "aarch64/jccolext-neon.c" #else #include "aarch32/jccolext-neon.c" @@ -132,7 +132,7 @@ ALIGN(16) static const uint16_t jsimd_rgb_ycc_neon_consts[] = { #define RGB_BLUE EXT_XBGR_BLUE #define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE #define jsimd_rgb_ycc_convert_neon jsimd_extxbgr_ycc_convert_neon -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include "aarch64/jccolext-neon.c" #else #include "aarch32/jccolext-neon.c" @@ -148,7 +148,7 @@ ALIGN(16) static const uint16_t jsimd_rgb_ycc_neon_consts[] = { #define RGB_BLUE EXT_XRGB_BLUE #define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE #define jsimd_rgb_ycc_convert_neon jsimd_extxrgb_ycc_convert_neon -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include "aarch64/jccolext-neon.c" #else #include "aarch32/jccolext-neon.c" diff --git a/3rdparty/libjpeg-turbo/simd/arm/jcgray-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jcgray-neon.c index fbcf821405..381913115b 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jcgray-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jcgray-neon.c @@ -1,5 +1,5 @@ /* - * jcgray-neon.c - grayscale colorspace conversion (Arm Neon) + * Grayscale colorspace conversion (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jcgryext-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jcgryext-neon.c index 416a7385df..559b6a5b50 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jcgryext-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jcgryext-neon.c @@ -1,5 +1,5 @@ /* - * jcgryext-neon.c - grayscale colorspace conversion (Arm Neon) + * Grayscale colorspace conversion (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * diff --git a/3rdparty/libjpeg-turbo/simd/arm/jchuff.h b/3rdparty/libjpeg-turbo/simd/arm/jchuff.h index 2fbd252b9b..5ca9629394 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jchuff.h +++ b/3rdparty/libjpeg-turbo/simd/arm/jchuff.h @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2018, 2021, D. R. Commander. + * Copyright (C) 2009, 2018, 2021, 2025, D. R. Commander. * Copyright (C) 2018, Matthias Räncker. * Copyright (C) 2020-2021, Arm Limited. * For conditions of distribution and use, see the accompanying README.ijg @@ -17,7 +17,7 @@ * but must not be updated permanently until we complete the MCU. */ -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #define BIT_BUF_SIZE 64 #else #define BIT_BUF_SIZE 32 @@ -54,7 +54,7 @@ typedef struct { * directly to the output buffer. Otherwise, use the EMIT_BYTE() macro to * encode 0xFF as 0xFF 0x00. */ -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #define FLUSH() { \ if (put_buffer & 0x8080808080808080 & ~(put_buffer + 0x0101010101010101)) { \ diff --git a/3rdparty/libjpeg-turbo/simd/arm/jcphuff-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jcphuff-neon.c index 435f96ee96..d3aeedbd15 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jcphuff-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jcphuff-neon.c @@ -1,9 +1,9 @@ /* - * jcphuff-neon.c - prepare data for progressive Huffman encoding (Arm Neon) + * Prepare data for progressive Huffman encoding (Arm Neon) * * Copyright (C) 2020-2021, Arm Limited. All Rights Reserved. * Copyright (C) 2022, Matthieu Darbois. All Rights Reserved. - * Copyright (C) 2022, 2024, D. R. Commander. All Rights Reserved. + * Copyright (C) 2022, 2024-2025, D. R. Commander. All Rights Reserved. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -251,7 +251,7 @@ void jsimd_encode_mcu_AC_first_prepare_neon uint8x8_t bitmap_rows_4567 = vpadd_u8(bitmap_rows_45, bitmap_rows_67); uint8x8_t bitmap_all = vpadd_u8(bitmap_rows_0123, bitmap_rows_4567); -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) /* Move bitmap to a 64-bit scalar register. */ uint64_t bitmap = vget_lane_u64(vreinterpret_u64_u8(bitmap_all), 0); /* Store zerobits bitmap. */ @@ -511,7 +511,7 @@ int jsimd_encode_mcu_AC_refine_prepare_neon uint8x8_t bitmap_rows_4567 = vpadd_u8(bitmap_rows_45, bitmap_rows_67); uint8x8_t bitmap_all = vpadd_u8(bitmap_rows_0123, bitmap_rows_4567); -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) /* Move bitmap to a 64-bit scalar register. */ uint64_t bitmap = vget_lane_u64(vreinterpret_u64_u8(bitmap_all), 0); /* Store zerobits bitmap. */ @@ -552,7 +552,7 @@ int jsimd_encode_mcu_AC_refine_prepare_neon bitmap_rows_4567 = vpadd_u8(bitmap_rows_45, bitmap_rows_67); bitmap_all = vpadd_u8(bitmap_rows_0123, bitmap_rows_4567); -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) /* Move bitmap to a 64-bit scalar register. */ bitmap = vget_lane_u64(vreinterpret_u64_u8(bitmap_all), 0); /* Store signbits bitmap. */ @@ -595,7 +595,7 @@ int jsimd_encode_mcu_AC_refine_prepare_neon bitmap_rows_4567 = vpadd_u8(bitmap_rows_45, bitmap_rows_67); bitmap_all = vpadd_u8(bitmap_rows_0123, bitmap_rows_4567); -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) /* Move bitmap to a 64-bit scalar register. */ bitmap = vget_lane_u64(vreinterpret_u64_u8(bitmap_all), 0); diff --git a/3rdparty/libjpeg-turbo/simd/arm/jcsample-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jcsample-neon.c index fd8a93e520..ccba6f42e4 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jcsample-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jcsample-neon.c @@ -1,8 +1,8 @@ /* - * jcsample-neon.c - downsampling (Arm Neon) + * Downsampling (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. - * Copyright (C) 2024, D. R. Commander. All Rights Reserved. + * Copyright (C) 2024-2025, D. R. Commander. All Rights Reserved. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -107,7 +107,7 @@ void jsimd_h2v1_downsample_neon(JDIMENSION image_width, int max_v_samp_factor, /* Load pixels in last DCT block into a table. */ uint8x16_t pixels = vld1q_u8(inptr + (width_in_blocks - 1) * 2 * DCTSIZE); -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) /* Pad the empty elements with the value of the last pixel. */ pixels = vqtbl1q_u8(pixels, expand_mask); #else @@ -169,7 +169,7 @@ void jsimd_h2v2_downsample_neon(JDIMENSION image_width, int max_v_samp_factor, vld1q_u8(inptr0 + (width_in_blocks - 1) * 2 * DCTSIZE); uint8x16_t pixels_r1 = vld1q_u8(inptr1 + (width_in_blocks - 1) * 2 * DCTSIZE); -#if defined(__aarch64__) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) /* Pad the empty elements with the value of the last pixel. */ pixels_r0 = vqtbl1q_u8(pixels_r0, expand_mask); pixels_r1 = vqtbl1q_u8(pixels_r1, expand_mask); diff --git a/3rdparty/libjpeg-turbo/simd/arm/jdcolext-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jdcolext-neon.c index c3c07a1964..1a154a021c 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jdcolext-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jdcolext-neon.c @@ -1,5 +1,5 @@ /* - * jdcolext-neon.c - colorspace conversion (Arm Neon) + * Colorspace conversion (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2020, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jdcolor-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jdcolor-neon.c index 97bb02a1ed..fc8d2bef54 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jdcolor-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jdcolor-neon.c @@ -1,5 +1,5 @@ /* - * jdcolor-neon.c - colorspace conversion (Arm Neon) + * Colorspace conversion (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jdmerge-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jdmerge-neon.c index 95e6d32830..744dab5135 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jdmerge-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jdmerge-neon.c @@ -1,5 +1,5 @@ /* - * jdmerge-neon.c - merged upsampling/color conversion (Arm Neon) + * Merged upsampling/color conversion (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jdmrgext-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jdmrgext-neon.c index 5b89bdb339..1e29bc6267 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jdmrgext-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jdmrgext-neon.c @@ -1,5 +1,5 @@ /* - * jdmrgext-neon.c - merged upsampling/color conversion (Arm Neon) + * Merged upsampling/color conversion (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2020, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jdsample-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jdsample-neon.c index a130b1a958..0847b8d061 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jdsample-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jdsample-neon.c @@ -1,5 +1,5 @@ /* - * jdsample-neon.c - upsampling (Arm Neon) + * Upsampling (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2020, 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jfdctfst-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jfdctfst-neon.c index d6109f11d3..f49a9a8314 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jfdctfst-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jfdctfst-neon.c @@ -1,5 +1,5 @@ /* - * jfdctfst-neon.c - fast integer FDCT (Arm Neon) + * Fast integer FDCT (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jfdctint-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jfdctint-neon.c index bb290ea45d..e770c706d0 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jfdctint-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jfdctint-neon.c @@ -1,5 +1,5 @@ /* - * jfdctint-neon.c - accurate integer FDCT (Arm Neon) + * Accurate integer FDCT (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2020, 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jidctfst-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jidctfst-neon.c index e789125344..5da698d61b 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jidctfst-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jidctfst-neon.c @@ -1,5 +1,5 @@ /* - * jidctfst-neon.c - fast integer IDCT (Arm Neon) + * Fast integer IDCT (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jidctint-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jidctint-neon.c index 709e0eaf4e..0e351bebb9 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jidctint-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jidctint-neon.c @@ -1,5 +1,5 @@ /* - * jidctint-neon.c - accurate integer IDCT (Arm Neon) + * Accurate integer IDCT (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2020, 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jidctred-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jidctred-neon.c index 25b1addc6a..f676e3a8c1 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jidctred-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jidctred-neon.c @@ -1,5 +1,5 @@ /* - * jidctred-neon.c - reduced-size IDCT (Arm Neon) + * Reduced-size IDCT (Arm Neon) * * Copyright (C) 2020, Arm Limited. All Rights Reserved. * Copyright (C) 2020, 2024, D. R. Commander. All Rights Reserved. diff --git a/3rdparty/libjpeg-turbo/simd/arm/jquanti-neon.c b/3rdparty/libjpeg-turbo/simd/arm/jquanti-neon.c index e44fb3d413..1e78dd8c89 100644 --- a/3rdparty/libjpeg-turbo/simd/arm/jquanti-neon.c +++ b/3rdparty/libjpeg-turbo/simd/arm/jquanti-neon.c @@ -1,8 +1,8 @@ /* - * jquanti-neon.c - sample data conversion and quantization (Arm Neon) + * Sample data conversion and quantization (Arm Neon) * * Copyright (C) 2020-2021, Arm Limited. All Rights Reserved. - * Copyright (C) 2024, D. R. Commander. All Rights Reserved. + * Copyright (C) 2024-2025, D. R. Commander. All Rights Reserved. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -102,7 +102,8 @@ void jsimd_quantize_neon(JCOEFPTR coef_block, DCTELEM *divisors, DCTELEM *shift_ptr = divisors + 3 * DCTSIZE2; int i; -#if defined(__clang__) && (defined(__aarch64__) || defined(_M_ARM64)) +#if defined(__clang__) && (defined(__aarch64__) || defined(_M_ARM64) || \ + defined(_M_ARM64EC)) #pragma unroll #endif for (i = 0; i < DCTSIZE; i += DCTSIZE / 2) { diff --git a/3rdparty/libjpeg-turbo/simd/i386/jccolext-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jccolext-avx2.asm index 28ac952807..0acfe3214c 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jccolext-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jccolext-avx2.asm @@ -1,5 +1,5 @@ ; -; jccolext.asm - colorspace conversion (AVX2) +; Colorspace conversion (AVX2) ; ; Copyright (C) 2015, Intel Corporation. ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jccolext-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jccolext-mmx.asm index 44b62512e9..cadd90bbfc 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jccolext-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jccolext-mmx.asm @@ -1,5 +1,5 @@ ; -; jccolext.asm - colorspace conversion (MMX) +; Colorspace conversion (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jccolext-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jccolext-sse2.asm index 1d8d5f5a20..b8f84343fd 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jccolext-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jccolext-sse2.asm @@ -1,5 +1,5 @@ ; -; jccolext.asm - colorspace conversion (SSE2) +; Colorspace conversion (SSE2) ; ; Copyright (C) 2016, 2024, D. R. Commander. ; diff --git a/3rdparty/libjpeg-turbo/simd/i386/jccolor-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jccolor-avx2.asm index 9ad5ea95f8..e3eda9efb1 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jccolor-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jccolor-avx2.asm @@ -1,5 +1,5 @@ ; -; jccolor.asm - colorspace conversion (AVX2) +; Colorspace conversion (AVX2) ; ; Copyright (C) 2009, 2016, 2024, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jccolor-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jccolor-mmx.asm index 0dbec54817..9232c4bd5f 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jccolor-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jccolor-mmx.asm @@ -1,5 +1,5 @@ ; -; jccolor.asm - colorspace conversion (MMX) +; Colorspace conversion (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jccolor-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jccolor-sse2.asm index 678306a10c..ed72f38d3a 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jccolor-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jccolor-sse2.asm @@ -1,5 +1,5 @@ ; -; jccolor.asm - colorspace conversion (SSE2) +; Colorspace conversion (SSE2) ; ; Copyright (C) 2009, 2016, 2024, D. R. Commander. ; diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcgray-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jcgray-avx2.asm index ded39567df..fae3c2121b 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcgray-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcgray-avx2.asm @@ -1,5 +1,5 @@ ; -; jcgray.asm - grayscale colorspace conversion (AVX2) +; Grayscale colorspace conversion (AVX2) ; ; Copyright (C) 2011, 2016, 2024, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcgray-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jcgray-mmx.asm index d6f031869a..47597c97ca 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcgray-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcgray-mmx.asm @@ -1,5 +1,5 @@ ; -; jcgray.asm - grayscale colorspace conversion (MMX) +; Grayscale colorspace conversion (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2011, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcgray-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jcgray-sse2.asm index ecc7fa08ab..3027866148 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcgray-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcgray-sse2.asm @@ -1,5 +1,5 @@ ; -; jcgray.asm - grayscale colorspace conversion (SSE2) +; Grayscale colorspace conversion (SSE2) ; ; Copyright (C) 2011, 2016, 2024, D. R. Commander. ; diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcgryext-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jcgryext-avx2.asm index 70df8f80ba..847fa320fe 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcgryext-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcgryext-avx2.asm @@ -1,5 +1,5 @@ ; -; jcgryext.asm - grayscale colorspace conversion (AVX2) +; Grayscale colorspace conversion (AVX2) ; ; Copyright (C) 2011, 2016, 2024, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcgryext-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jcgryext-mmx.asm index dd90c3dfb0..c67aaec7c5 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcgryext-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcgryext-mmx.asm @@ -1,5 +1,5 @@ ; -; jcgryext.asm - grayscale colorspace conversion (MMX) +; Grayscale colorspace conversion (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2011, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcgryext-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jcgryext-sse2.asm index 227295f307..2965202d2c 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcgryext-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcgryext-sse2.asm @@ -1,5 +1,5 @@ ; -; jcgryext.asm - grayscale colorspace conversion (SSE2) +; Grayscale colorspace conversion (SSE2) ; ; Copyright (C) 2011, 2016, 2024, D. R. Commander. ; diff --git a/3rdparty/libjpeg-turbo/simd/i386/jchuff-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jchuff-sse2.asm index ed194dd383..c5c1426df8 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jchuff-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jchuff-sse2.asm @@ -1,5 +1,5 @@ ; -; jchuff-sse2.asm - Huffman entropy encoding (SSE2) +; Huffman entropy encoding (SSE2) ; ; Copyright (C) 2009-2011, 2014-2017, 2019, 2024, D. R. Commander. ; Copyright (C) 2015, Matthieu Darbois. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcphuff-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jcphuff-sse2.asm index 19a183fcd8..13d4838658 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcphuff-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcphuff-sse2.asm @@ -1,5 +1,5 @@ ; -; jcphuff-sse2.asm - prepare data for progressive Huffman encoding (SSE2) +; Prepare data for progressive Huffman encoding (SSE2) ; ; Copyright (C) 2016, 2018, Matthieu Darbois ; diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcsample-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jcsample-avx2.asm index 5019829c9a..0beeb4c709 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcsample-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcsample-avx2.asm @@ -1,5 +1,5 @@ ; -; jcsample.asm - downsampling (AVX2) +; Downsampling (AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcsample-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jcsample-mmx.asm index 94dd88870a..1bcd1eece4 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcsample-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcsample-mmx.asm @@ -1,5 +1,5 @@ ; -; jcsample.asm - downsampling (MMX) +; Downsampling (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jcsample-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jcsample-sse2.asm index eb8808bea8..70d02f4bca 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jcsample-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jcsample-sse2.asm @@ -1,5 +1,5 @@ ; -; jcsample.asm - downsampling (SSE2) +; Downsampling (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdcolext-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdcolext-avx2.asm index fd79b79568..9f24958e3a 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdcolext-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdcolext-avx2.asm @@ -1,5 +1,5 @@ ; -; jdcolext.asm - colorspace conversion (AVX2) +; Colorspace conversion (AVX2) ; ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2012, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdcolext-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jdcolext-mmx.asm index 636bd6d3fd..c7fe0c6d68 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdcolext-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdcolext-mmx.asm @@ -1,5 +1,5 @@ ; -; jdcolext.asm - colorspace conversion (MMX) +; Colorspace conversion (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdcolext-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdcolext-sse2.asm index 0150f2cb69..a87a98f9cf 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdcolext-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdcolext-sse2.asm @@ -1,5 +1,5 @@ ; -; jdcolext.asm - colorspace conversion (SSE2) +; Colorspace conversion (SSE2) ; ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2012, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdcolor-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdcolor-avx2.asm index d3a30d63a7..1071e048d1 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdcolor-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdcolor-avx2.asm @@ -1,5 +1,5 @@ ; -; jdcolor.asm - colorspace conversion (AVX2) +; Colorspace conversion (AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdcolor-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jdcolor-mmx.asm index 6e67e4b72e..535d115a01 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdcolor-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdcolor-mmx.asm @@ -1,5 +1,5 @@ ; -; jdcolor.asm - colorspace conversion (MMX) +; Colorspace conversion (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdcolor-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdcolor-sse2.asm index 79c9c6821a..c2a66bcce9 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdcolor-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdcolor-sse2.asm @@ -1,5 +1,5 @@ ; -; jdcolor.asm - colorspace conversion (SSE2) +; Colorspace conversion (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdmerge-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdmerge-avx2.asm index 90493fd023..9080aafd2b 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdmerge-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdmerge-avx2.asm @@ -1,5 +1,5 @@ ; -; jdmerge.asm - merged upsampling/color conversion (AVX2) +; Merged upsampling/color conversion (AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdmerge-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jdmerge-mmx.asm index 0dc204aa8b..9787546738 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdmerge-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdmerge-mmx.asm @@ -1,5 +1,5 @@ ; -; jdmerge.asm - merged upsampling/color conversion (MMX) +; Merged upsampling/color conversion (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdmerge-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdmerge-sse2.asm index 06f0762742..03fc475147 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdmerge-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdmerge-sse2.asm @@ -1,5 +1,5 @@ ; -; jdmerge.asm - merged upsampling/color conversion (SSE2) +; Merged upsampling/color conversion (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-avx2.asm index a7aa930e34..3bb932fa24 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-avx2.asm @@ -1,5 +1,5 @@ ; -; jdmrgext.asm - merged upsampling/color conversion (AVX2) +; Merged upsampling/color conversion (AVX2) ; ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2012, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-mmx.asm index 562758146c..eb8a82a87e 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-mmx.asm @@ -1,5 +1,5 @@ ; -; jdmrgext.asm - merged upsampling/color conversion (MMX) +; Merged upsampling/color conversion (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-sse2.asm index 13e7d980fa..ad24e10bbb 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdmrgext-sse2.asm @@ -1,5 +1,5 @@ ; -; jdmrgext.asm - merged upsampling/color conversion (SSE2) +; Merged upsampling/color conversion (SSE2) ; ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2012, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdsample-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdsample-avx2.asm index eba53ef757..158aa99c17 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdsample-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdsample-avx2.asm @@ -1,5 +1,5 @@ ; -; jdsample.asm - upsampling (AVX2) +; Upsampling (AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdsample-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jdsample-mmx.asm index 01d09e62d1..69afe63263 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdsample-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdsample-mmx.asm @@ -1,5 +1,5 @@ ; -; jdsample.asm - upsampling (MMX) +; Upsampling (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jdsample-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jdsample-sse2.asm index b10d922798..fcad5c15f2 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jdsample-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jdsample-sse2.asm @@ -1,5 +1,5 @@ ; -; jdsample.asm - upsampling (SSE2) +; Upsampling (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jfdctflt-3dn.asm b/3rdparty/libjpeg-turbo/simd/i386/jfdctflt-3dn.asm index 0cedc6caf4..cd29093a68 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jfdctflt-3dn.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jfdctflt-3dn.asm @@ -1,5 +1,5 @@ ; -; jfdctflt.asm - floating-point FDCT (3DNow!) +; Floating-point FDCT (3DNow!) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jfdctflt-sse.asm b/3rdparty/libjpeg-turbo/simd/i386/jfdctflt-sse.asm index 2cb9533586..4a10e489d9 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jfdctflt-sse.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jfdctflt-sse.asm @@ -1,5 +1,5 @@ ; -; jfdctflt.asm - floating-point FDCT (SSE) +; Floating-point FDCT (SSE) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jfdctfst-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jfdctfst-mmx.asm index fe16e83ee2..900fba53f2 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jfdctfst-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jfdctfst-mmx.asm @@ -1,5 +1,5 @@ ; -; jfdctfst.asm - fast integer FDCT (MMX) +; Fast integer FDCT (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jfdctfst-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jfdctfst-sse2.asm index 890482e006..dca57c77be 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jfdctfst-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jfdctfst-sse2.asm @@ -1,5 +1,5 @@ ; -; jfdctfst.asm - fast integer FDCT (SSE2) +; Fast integer FDCT (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jfdctint-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jfdctint-avx2.asm index 05ea865485..69b59773a1 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jfdctint-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jfdctint-avx2.asm @@ -1,5 +1,5 @@ ; -; jfdctint.asm - accurate integer FDCT (AVX2) +; Accurate integer FDCT (AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2018, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jfdctint-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jfdctint-mmx.asm index 7d4c61cd7d..d9c47ac663 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jfdctint-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jfdctint-mmx.asm @@ -1,5 +1,5 @@ ; -; jfdctint.asm - accurate integer FDCT (MMX) +; Accurate integer FDCT (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jfdctint-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jfdctint-sse2.asm index 7ed5c9501a..c98b8337ca 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jfdctint-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jfdctint-sse2.asm @@ -1,5 +1,5 @@ ; -; jfdctint.asm - accurate integer FDCT (SSE2) +; Accurate integer FDCT (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctflt-3dn.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctflt-3dn.asm index 8612eee3a5..cc66b85a26 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctflt-3dn.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctflt-3dn.asm @@ -1,5 +1,5 @@ ; -; jidctflt.asm - floating-point IDCT (3DNow! & MMX) +; Floating-point IDCT (3DNow! & MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctflt-sse.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctflt-sse.asm index caf636b510..d318267221 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctflt-sse.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctflt-sse.asm @@ -1,5 +1,5 @@ ; -; jidctflt.asm - floating-point IDCT (SSE & MMX) +; Floating-point IDCT (SSE & MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctflt-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctflt-sse2.asm index 42703a8efd..3ee4163e51 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctflt-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctflt-sse2.asm @@ -1,5 +1,5 @@ ; -; jidctflt.asm - floating-point IDCT (SSE & SSE2) +; Floating-point IDCT (SSE & SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctfst-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctfst-mmx.asm index 77d4613d23..9668bbdecf 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctfst-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctfst-mmx.asm @@ -1,5 +1,5 @@ ; -; jidctfst.asm - fast integer IDCT (MMX) +; Fast integer IDCT (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctfst-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctfst-sse2.asm index c2fe34ba8c..b40ac4ebb2 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctfst-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctfst-sse2.asm @@ -1,5 +1,5 @@ ; -; jidctfst.asm - fast integer IDCT (SSE2) +; Fast integer IDCT (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctint-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctint-avx2.asm index cb119d3f06..8f67d2bdb5 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctint-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctint-avx2.asm @@ -1,5 +1,5 @@ ; -; jidctint.asm - accurate integer IDCT (AVX2) +; Accurate integer IDCT (AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2018, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctint-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctint-mmx.asm index c2c17f441b..05766fb02b 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctint-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctint-mmx.asm @@ -1,5 +1,5 @@ ; -; jidctint.asm - accurate integer IDCT (MMX) +; Accurate integer IDCT (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctint-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctint-sse2.asm index 70516cadce..199311b632 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctint-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctint-sse2.asm @@ -1,5 +1,5 @@ ; -; jidctint.asm - accurate integer IDCT (SSE2) +; Accurate integer IDCT (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctred-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctred-mmx.asm index 96cda65713..1d4ab9d1d5 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctred-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctred-mmx.asm @@ -1,5 +1,5 @@ ; -; jidctred.asm - reduced-size IDCT (MMX) +; Reduced-size IDCT (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jidctred-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jidctred-sse2.asm index 1fe967db19..e1f759abae 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jidctred-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jidctred-sse2.asm @@ -1,5 +1,5 @@ ; -; jidctred.asm - reduced-size IDCT (SSE2) +; Reduced-size IDCT (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jquant-3dn.asm b/3rdparty/libjpeg-turbo/simd/i386/jquant-3dn.asm index 58e0011f70..6c5e2b9836 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jquant-3dn.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jquant-3dn.asm @@ -1,5 +1,5 @@ ; -; jquant.asm - sample data conversion and quantization (3DNow! & MMX) +; Sample data conversion and quantization (3DNow! & MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jquant-mmx.asm b/3rdparty/libjpeg-turbo/simd/i386/jquant-mmx.asm index 4eda95ce12..81f898cfbf 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jquant-mmx.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jquant-mmx.asm @@ -1,5 +1,5 @@ ; -; jquant.asm - sample data conversion and quantization (MMX) +; Sample data conversion and quantization (MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jquant-sse.asm b/3rdparty/libjpeg-turbo/simd/i386/jquant-sse.asm index 6cb5f79c21..ecba4db536 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jquant-sse.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jquant-sse.asm @@ -1,5 +1,5 @@ ; -; jquant.asm - sample data conversion and quantization (SSE & MMX) +; Sample data conversion and quantization (SSE & MMX) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jquantf-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jquantf-sse2.asm index 5668f8cb39..6782eea61f 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jquantf-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jquantf-sse2.asm @@ -1,5 +1,5 @@ ; -; jquantf.asm - sample data conversion and quantization (SSE & SSE2) +; Sample data conversion and quantization (SSE & SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jquanti-avx2.asm b/3rdparty/libjpeg-turbo/simd/i386/jquanti-avx2.asm index 60ae098e9c..1ad0f50af4 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jquanti-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jquanti-avx2.asm @@ -1,5 +1,5 @@ ; -; jquanti.asm - sample data conversion and quantization (AVX2) +; Sample data conversion and quantization (AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2018, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jquanti-sse2.asm b/3rdparty/libjpeg-turbo/simd/i386/jquanti-sse2.asm index c1edde996e..b3ac67a6cc 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jquanti-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jquanti-sse2.asm @@ -1,5 +1,5 @@ ; -; jquanti.asm - sample data conversion and quantization (SSE2) +; Sample data conversion and quantization (SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jsimd.c b/3rdparty/libjpeg-turbo/simd/i386/jsimd.c index d4786b155b..5df7b8c931 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jsimd.c +++ b/3rdparty/libjpeg-turbo/simd/i386/jsimd.c @@ -1,6 +1,4 @@ /* - * jsimd_i386.c - * * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, 2022-2024, D. R. Commander. * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. diff --git a/3rdparty/libjpeg-turbo/simd/i386/jsimdcpu.asm b/3rdparty/libjpeg-turbo/simd/i386/jsimdcpu.asm index df80f17f5f..4f23ea45e7 100644 --- a/3rdparty/libjpeg-turbo/simd/i386/jsimdcpu.asm +++ b/3rdparty/libjpeg-turbo/simd/i386/jsimdcpu.asm @@ -1,5 +1,5 @@ ; -; jsimdcpu.asm - SIMD instruction support check +; SIMD instruction support check ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/jsimd.h b/3rdparty/libjpeg-turbo/simd/jsimd.h index a28754adb9..0fd6b81b34 100644 --- a/3rdparty/libjpeg-turbo/simd/jsimd.h +++ b/3rdparty/libjpeg-turbo/simd/jsimd.h @@ -1,6 +1,4 @@ /* - * simd/jsimd.h - * * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2011, 2014-2016, 2018, 2020, 2022, D. R. Commander. * Copyright (C) 2013-2014, MIPS Technologies, Inc., California. diff --git a/3rdparty/libjpeg-turbo/simd/mips/jsimd.c b/3rdparty/libjpeg-turbo/simd/mips/jsimd.c index 6a1f1e0bcf..5841e9366d 100644 --- a/3rdparty/libjpeg-turbo/simd/mips/jsimd.c +++ b/3rdparty/libjpeg-turbo/simd/mips/jsimd.c @@ -1,6 +1,4 @@ /* - * jsimd_mips.c - * * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2009-2011, 2014, 2016, 2018, 2020, 2022, 2024, * D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/mips64/jsimd.c b/3rdparty/libjpeg-turbo/simd/mips64/jsimd.c index a2046d4620..3046e72726 100644 --- a/3rdparty/libjpeg-turbo/simd/mips64/jsimd.c +++ b/3rdparty/libjpeg-turbo/simd/mips64/jsimd.c @@ -1,6 +1,4 @@ /* - * jsimd_mips64.c - * * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2009-2011, 2014, 2016, 2018, 2022, 2024, D. R. Commander. * Copyright (C) 2013-2014, MIPS Technologies, Inc., California. diff --git a/3rdparty/libjpeg-turbo/simd/powerpc/jsimd.c b/3rdparty/libjpeg-turbo/simd/powerpc/jsimd.c index e8c6c51d94..85252873f5 100644 --- a/3rdparty/libjpeg-turbo/simd/powerpc/jsimd.c +++ b/3rdparty/libjpeg-turbo/simd/powerpc/jsimd.c @@ -1,6 +1,4 @@ /* - * jsimd_powerpc.c - * * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2009-2011, 2014-2016, 2018, 2022, 2024, D. R. Commander. * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jccolext-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jccolext-avx2.asm index aeeda0a682..eecd76f231 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jccolext-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jccolext-avx2.asm @@ -1,5 +1,5 @@ ; -; jccolext.asm - colorspace conversion (64-bit AVX2) +; Colorspace conversion (64-bit AVX2) ; ; Copyright (C) 2009, 2016, 2024, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jccolext-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jccolext-sse2.asm index f3a1244903..c3a4052fa4 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jccolext-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jccolext-sse2.asm @@ -1,5 +1,5 @@ ; -; jccolext.asm - colorspace conversion (64-bit SSE2) +; Colorspace conversion (64-bit SSE2) ; ; Copyright (C) 2009, 2016, 2024, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jccolor-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jccolor-avx2.asm index e262891733..003a4df1b5 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jccolor-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jccolor-avx2.asm @@ -1,5 +1,5 @@ ; -; jccolor.asm - colorspace conversion (64-bit AVX2) +; Colorspace conversion (64-bit AVX2) ; ; Copyright (C) 2009, 2016, 2024, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jccolor-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jccolor-sse2.asm index cc9edb4ceb..ced172ff4a 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jccolor-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jccolor-sse2.asm @@ -1,5 +1,5 @@ ; -; jccolor.asm - colorspace conversion (64-bit SSE2) +; Colorspace conversion (64-bit SSE2) ; ; Copyright (C) 2009, 2016, 2024, D. R. Commander. ; diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jcgray-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jcgray-avx2.asm index 267ec5142a..2e45b64e9e 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jcgray-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jcgray-avx2.asm @@ -1,5 +1,5 @@ ; -; jcgray.asm - grayscale colorspace conversion (64-bit AVX2) +; Grayscale colorspace conversion (64-bit AVX2) ; ; Copyright (C) 2011, 2016, 2024, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jcgray-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jcgray-sse2.asm index 4b94d7b8a2..c9bcf91348 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jcgray-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jcgray-sse2.asm @@ -1,5 +1,5 @@ ; -; jcgray.asm - grayscale colorspace conversion (64-bit SSE2) +; Grayscale colorspace conversion (64-bit SSE2) ; ; Copyright (C) 2011, 2016, 2024, D. R. Commander. ; diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jcgryext-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jcgryext-avx2.asm index 77e85f768f..83fcd1bd63 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jcgryext-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jcgryext-avx2.asm @@ -1,5 +1,5 @@ ; -; jcgryext.asm - grayscale colorspace conversion (64-bit AVX2) +; Grayscale colorspace conversion (64-bit AVX2) ; ; Copyright (C) 2011, 2016, 2024, D. R. Commander. ; Copyright (C) 2015, Intel Corporation. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jcgryext-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jcgryext-sse2.asm index 3e8087c39b..407b5084ab 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jcgryext-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jcgryext-sse2.asm @@ -1,5 +1,5 @@ ; -; jcgryext.asm - grayscale colorspace conversion (64-bit SSE2) +; Grayscale colorspace conversion (64-bit SSE2) ; ; Copyright (C) 2011, 2016, 2024, D. R. Commander. ; Copyright (C) 2018, Matthias Räncker. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jchuff-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jchuff-sse2.asm index b18b7f5d65..7e32ea3fb4 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jchuff-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jchuff-sse2.asm @@ -1,5 +1,5 @@ ; -; jchuff-sse2.asm - Huffman entropy encoding (64-bit SSE2) +; Huffman entropy encoding (64-bit SSE2) ; ; Copyright (C) 2009-2011, 2014-2016, 2019, 2021, 2023-2024, D. R. Commander. ; Copyright (C) 2015, Matthieu Darbois. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jcphuff-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jcphuff-sse2.asm index c9ac59f2f1..49d5414e2e 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jcphuff-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jcphuff-sse2.asm @@ -1,6 +1,5 @@ ; -; jcphuff-sse2.asm - prepare data for progressive Huffman encoding -; (64-bit SSE2) +; Prepare data for progressive Huffman encoding (64-bit SSE2) ; ; Copyright (C) 2016, 2018, Matthieu Darbois ; Copyright (C) 2023, Aliaksiej Kandracienka. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jcsample-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jcsample-avx2.asm index 53afc7d77f..38c662f920 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jcsample-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jcsample-avx2.asm @@ -1,5 +1,5 @@ ; -; jcsample.asm - downsampling (64-bit AVX2) +; Downsampling (64-bit AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jcsample-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jcsample-sse2.asm index d7ffa930e8..739f0c3675 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jcsample-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jcsample-sse2.asm @@ -1,5 +1,5 @@ ; -; jcsample.asm - downsampling (64-bit SSE2) +; Downsampling (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdcolext-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdcolext-avx2.asm index 7b8a084398..50990f5f99 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdcolext-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdcolext-avx2.asm @@ -1,5 +1,5 @@ ; -; jdcolext.asm - colorspace conversion (64-bit AVX2) +; Colorspace conversion (64-bit AVX2) ; ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2012, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdcolext-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdcolext-sse2.asm index 261f74da5d..9ab3652cb0 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdcolext-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdcolext-sse2.asm @@ -1,5 +1,5 @@ ; -; jdcolext.asm - colorspace conversion (64-bit SSE2) +; Colorspace conversion (64-bit SSE2) ; ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2012, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdcolor-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdcolor-avx2.asm index bd5aa00b95..5e7ba872c8 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdcolor-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdcolor-avx2.asm @@ -1,5 +1,5 @@ ; -; jdcolor.asm - colorspace conversion (64-bit AVX2) +; Colorspace conversion (64-bit AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdcolor-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdcolor-sse2.asm index 40343fe789..858cf346b1 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdcolor-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdcolor-sse2.asm @@ -1,5 +1,5 @@ ; -; jdcolor.asm - colorspace conversion (64-bit SSE2) +; Colorspace conversion (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdmerge-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdmerge-avx2.asm index 6a5f1daba5..e7550a75d1 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdmerge-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdmerge-avx2.asm @@ -1,5 +1,5 @@ ; -; jdmerge.asm - merged upsampling/color conversion (64-bit AVX2) +; Merged upsampling/color conversion (64-bit AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdmerge-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdmerge-sse2.asm index 8c269b83d8..42d1f6c775 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdmerge-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdmerge-sse2.asm @@ -1,5 +1,5 @@ ; -; jdmerge.asm - merged upsampling/color conversion (64-bit SSE2) +; Merged upsampling/color conversion (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdmrgext-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdmrgext-avx2.asm index 01826fb6ab..00fc1226de 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdmrgext-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdmrgext-avx2.asm @@ -1,5 +1,5 @@ ; -; jdmrgext.asm - merged upsampling/color conversion (64-bit AVX2) +; Merged upsampling/color conversion (64-bit AVX2) ; ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2012, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdmrgext-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdmrgext-sse2.asm index abd22e21a7..59c9100ae0 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdmrgext-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdmrgext-sse2.asm @@ -1,5 +1,5 @@ ; -; jdmrgext.asm - merged upsampling/color conversion (64-bit SSE2) +; Merged upsampling/color conversion (64-bit SSE2) ; ; Copyright 2009, 2012 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2012, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdsample-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdsample-avx2.asm index 6ae4cf812a..b0179a372b 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdsample-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdsample-avx2.asm @@ -1,5 +1,5 @@ ; -; jdsample.asm - upsampling (64-bit AVX2) +; Upsampling (64-bit AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jdsample-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jdsample-sse2.asm index 54c560fc28..224b1d5565 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jdsample-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jdsample-sse2.asm @@ -1,5 +1,5 @@ ; -; jdsample.asm - upsampling (64-bit SSE2) +; Upsampling (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jfdctflt-sse.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jfdctflt-sse.asm index 58a1f5570d..4954c8ae8e 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jfdctflt-sse.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jfdctflt-sse.asm @@ -1,5 +1,5 @@ ; -; jfdctflt.asm - floating-point FDCT (64-bit SSE) +; Floating-point FDCT (64-bit SSE) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jfdctfst-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jfdctfst-sse2.asm index 3b92d4edaa..60f972858f 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jfdctfst-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jfdctfst-sse2.asm @@ -1,5 +1,5 @@ ; -; jfdctfst.asm - fast integer FDCT (64-bit SSE2) +; Fast integer FDCT (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jfdctint-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jfdctint-avx2.asm index 0c4528612c..22611fc0b5 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jfdctint-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jfdctint-avx2.asm @@ -1,5 +1,5 @@ ; -; jfdctint.asm - accurate integer FDCT (64-bit AVX2) +; Accurate integer FDCT (64-bit AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2018, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jfdctint-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jfdctint-sse2.asm index 3a6be020cd..adc6b40a68 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jfdctint-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jfdctint-sse2.asm @@ -1,5 +1,5 @@ ; -; jfdctint.asm - accurate integer FDCT (64-bit SSE2) +; Accurate integer FDCT (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jidctflt-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jidctflt-sse2.asm index 1443734022..54aaf1162b 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jidctflt-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jidctflt-sse2.asm @@ -1,5 +1,5 @@ ; -; jidctflt.asm - floating-point IDCT (64-bit SSE & SSE2) +; Floating-point IDCT (64-bit SSE & SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jidctfst-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jidctfst-sse2.asm index cffabb8378..c4e8005457 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jidctfst-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jidctfst-sse2.asm @@ -1,5 +1,5 @@ ; -; jidctfst.asm - fast integer IDCT (64-bit SSE2) +; Fast integer IDCT (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jidctint-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jidctint-avx2.asm index be3b46888e..24b5958445 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jidctint-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jidctint-avx2.asm @@ -1,5 +1,5 @@ ; -; jidctint.asm - accurate integer IDCT (64-bit AVX2) +; Accurate integer IDCT (64-bit AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2018, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jidctint-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jidctint-sse2.asm index b186871ff2..5e1a5ac3b4 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jidctint-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jidctint-sse2.asm @@ -1,5 +1,5 @@ ; -; jidctint.asm - accurate integer IDCT (64-bit SSE2) +; Accurate integer IDCT (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2020, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jidctred-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jidctred-sse2.asm index 6fb7095612..9560e33e25 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jidctred-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jidctred-sse2.asm @@ -1,5 +1,5 @@ ; -; jidctred.asm - reduced-size IDCT (64-bit SSE2) +; Reduced-size IDCT (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jquantf-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jquantf-sse2.asm index 64763338f2..9a12419e75 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jquantf-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jquantf-sse2.asm @@ -1,5 +1,5 @@ ; -; jquantf.asm - sample data conversion and quantization (64-bit SSE & SSE2) +; Sample data conversion and quantization (64-bit SSE & SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jquanti-avx2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jquanti-avx2.asm index 7e126e88a8..46bdb83f48 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jquanti-avx2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jquanti-avx2.asm @@ -1,5 +1,5 @@ ; -; jquanti.asm - sample data conversion and quantization (64-bit AVX2) +; Sample data conversion and quantization (64-bit AVX2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2018, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jquanti-sse2.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jquanti-sse2.asm index 284b9fea71..797fb878bd 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jquanti-sse2.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jquanti-sse2.asm @@ -1,5 +1,5 @@ ; -; jquanti.asm - sample data conversion and quantization (64-bit SSE2) +; Sample data conversion and quantization (64-bit SSE2) ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2009, 2016, 2024, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jsimd.c b/3rdparty/libjpeg-turbo/simd/x86_64/jsimd.c index 038cf0f8a3..f783e6dccc 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jsimd.c +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jsimd.c @@ -1,6 +1,4 @@ /* - * jsimd_x86_64.c - * * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2009-2011, 2014, 2016, 2018, 2022-2024, D. R. Commander. * Copyright (C) 2015-2016, 2018, 2022, Matthieu Darbois. diff --git a/3rdparty/libjpeg-turbo/simd/x86_64/jsimdcpu.asm b/3rdparty/libjpeg-turbo/simd/x86_64/jsimdcpu.asm index b72f3b0b39..187498d543 100644 --- a/3rdparty/libjpeg-turbo/simd/x86_64/jsimdcpu.asm +++ b/3rdparty/libjpeg-turbo/simd/x86_64/jsimdcpu.asm @@ -1,5 +1,5 @@ ; -; jsimdcpu.asm - SIMD instruction support check +; SIMD instruction support check ; ; Copyright 2009 Pierre Ossman for Cendio AB ; Copyright (C) 2016, D. R. Commander. diff --git a/3rdparty/libjpeg-turbo/src/jcapimin.c b/3rdparty/libjpeg-turbo/src/jcapimin.c index eb4599fbfa..e99569035b 100644 --- a/3rdparty/libjpeg-turbo/src/jcapimin.c +++ b/3rdparty/libjpeg-turbo/src/jcapimin.c @@ -195,13 +195,19 @@ jpeg_finish_compress(j_compress_ptr cinfo) * all work is being done from the coefficient buffer. */ if (cinfo->data_precision <= 8) { + if (cinfo->coef->compress_data == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); if (!(*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE)NULL)) ERREXIT(cinfo, JERR_CANT_SUSPEND); } else if (cinfo->data_precision <= 12) { + if (cinfo->coef->compress_data_12 == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); if (!(*cinfo->coef->compress_data_12) (cinfo, (J12SAMPIMAGE)NULL)) ERREXIT(cinfo, JERR_CANT_SUSPEND); } else { #ifdef C_LOSSLESS_SUPPORTED + if (cinfo->coef->compress_data_16 == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); if (!(*cinfo->coef->compress_data_16) (cinfo, (J16SAMPIMAGE)NULL)) ERREXIT(cinfo, JERR_CANT_SUSPEND); #else diff --git a/3rdparty/libjpeg-turbo/src/jcapistd.c b/3rdparty/libjpeg-turbo/src/jcapistd.c index 2226094ba6..b61590df9d 100644 --- a/3rdparty/libjpeg-turbo/src/jcapistd.c +++ b/3rdparty/libjpeg-turbo/src/jcapistd.c @@ -130,6 +130,8 @@ _jpeg_write_scanlines(j_compress_ptr cinfo, _JSAMPARRAY scanlines, num_lines = rows_left; row_ctr = 0; + if (cinfo->main->_process_data == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); (*cinfo->main->_process_data) (cinfo, scanlines, &row_ctr, num_lines); cinfo->next_scanline += row_ctr; return row_ctr; @@ -187,6 +189,8 @@ _jpeg_write_raw_data(j_compress_ptr cinfo, _JSAMPIMAGE data, ERREXIT(cinfo, JERR_BUFFER_SIZE); /* Directly compress the row. */ + if (cinfo->coef->_compress_data == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); if (!(*cinfo->coef->_compress_data) (cinfo, data)) { /* If compressor did not consume the whole row, suspend processing. */ return 0; diff --git a/3rdparty/libjpeg-turbo/src/jccoefct.c b/3rdparty/libjpeg-turbo/src/jccoefct.c index 2a5dde2d07..4a47b3cbe5 100644 --- a/3rdparty/libjpeg-turbo/src/jccoefct.c +++ b/3rdparty/libjpeg-turbo/src/jccoefct.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2022, D. R. Commander. + * Copyright (C) 2022, 2024, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg * file. * @@ -414,6 +414,7 @@ _jinit_c_coef_controller(j_compress_ptr cinfo, boolean need_full_buffer) coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_coef_controller)); + memset(coef, 0, sizeof(my_coef_controller)); cinfo->coef = (struct jpeg_c_coef_controller *)coef; coef->pub.start_pass = start_pass_coef; diff --git a/3rdparty/libjpeg-turbo/src/jchuff.c b/3rdparty/libjpeg-turbo/src/jchuff.c index 8cdd5bd35d..781e1ca704 100644 --- a/3rdparty/libjpeg-turbo/src/jchuff.c +++ b/3rdparty/libjpeg-turbo/src/jchuff.c @@ -6,7 +6,7 @@ * Lossless JPEG Modifications: * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2014-2016, 2018-2024, D. R. Commander. + * Copyright (C) 2009-2011, 2014-2016, 2018-2025, D. R. Commander. * Copyright (C) 2015, Matthieu Darbois. * Copyright (C) 2018, Matthias Räncker. * Copyright (C) 2020, Arm Limited. @@ -55,7 +55,8 @@ typedef size_t bit_buf_type; * retain the old Huffman encoder behavior when using the GAS implementation. */ #if defined(WITH_SIMD) && !(defined(__arm__) || defined(__aarch64__) || \ - defined(_M_ARM) || defined(_M_ARM64)) + defined(_M_ARM) || defined(_M_ARM64) || \ + defined(_M_ARM64EC)) typedef unsigned long long simd_bit_buf_type; #else typedef bit_buf_type simd_bit_buf_type; diff --git a/3rdparty/libjpeg-turbo/src/jcmainct.c b/3rdparty/libjpeg-turbo/src/jcmainct.c index 954e94017c..b72f8deb6c 100644 --- a/3rdparty/libjpeg-turbo/src/jcmainct.c +++ b/3rdparty/libjpeg-turbo/src/jcmainct.c @@ -159,6 +159,7 @@ _jinit_c_main_controller(j_compress_ptr cinfo, boolean need_full_buffer) main_ptr = (my_main_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_main_controller)); + memset(main_ptr, 0, sizeof(my_main_controller)); cinfo->main = (struct jpeg_c_main_controller *)main_ptr; main_ptr->pub.start_pass = start_pass_main; diff --git a/3rdparty/libjpeg-turbo/src/jdapistd.c b/3rdparty/libjpeg-turbo/src/jdapistd.c index d0e5c0e5b9..813e21268e 100644 --- a/3rdparty/libjpeg-turbo/src/jdapistd.c +++ b/3rdparty/libjpeg-turbo/src/jdapistd.c @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2015-2020, 2022-2024, D. R. Commander. + * Copyright (C) 2010, 2015-2020, 2022-2025, D. R. Commander. * Copyright (C) 2015, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -128,19 +128,28 @@ output_pass_setup(j_decompress_ptr cinfo) } /* Process some data */ last_scanline = cinfo->output_scanline; - if (cinfo->data_precision <= 8) + if (cinfo->data_precision <= 8) { + if (cinfo->main->process_data == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); (*cinfo->main->process_data) (cinfo, (JSAMPARRAY)NULL, &cinfo->output_scanline, (JDIMENSION)0); - else if (cinfo->data_precision <= 12) + } else if (cinfo->data_precision <= 12) { + if (cinfo->main->process_data_12 == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); (*cinfo->main->process_data_12) (cinfo, (J12SAMPARRAY)NULL, &cinfo->output_scanline, (JDIMENSION)0); + } else { #ifdef D_LOSSLESS_SUPPORTED - else + if (cinfo->main->process_data_16 == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); (*cinfo->main->process_data_16) (cinfo, (J16SAMPARRAY)NULL, &cinfo->output_scanline, (JDIMENSION)0); +#else + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); #endif + } if (cinfo->output_scanline == last_scanline) return FALSE; /* No progress made, must suspend */ } @@ -345,6 +354,8 @@ _jpeg_read_scanlines(j_decompress_ptr cinfo, _JSAMPARRAY scanlines, /* Process some data */ row_ctr = 0; + if (cinfo->main->_process_data == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); (*cinfo->main->_process_data) (cinfo, scanlines, &row_ctr, max_lines); cinfo->output_scanline += row_ctr; return row_ctr; @@ -397,7 +408,8 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) void (*color_quantize) (j_decompress_ptr cinfo, _JSAMPARRAY input_buf, _JSAMPARRAY output_buf, int num_rows) = NULL; - if (cinfo->cconvert && cinfo->cconvert->_color_convert) { + if (!master->using_merged_upsample && cinfo->cconvert && + cinfo->cconvert->_color_convert) { color_convert = cinfo->cconvert->_color_convert; cinfo->cconvert->_color_convert = noop_convert; /* This just prevents UBSan from complaining about adding 0 to a NULL @@ -406,7 +418,8 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) scanlines = &dummy_row; } - if (cinfo->cquantize && cinfo->cquantize->_color_quantize) { + if (cinfo->quantize_colors && cinfo->cquantize && + cinfo->cquantize->_color_quantize) { color_quantize = cinfo->cquantize->_color_quantize; cinfo->cquantize->_color_quantize = noop_quantize; } @@ -691,6 +704,8 @@ _jpeg_read_raw_data(j_decompress_ptr cinfo, _JSAMPIMAGE data, ERREXIT(cinfo, JERR_BUFFER_SIZE); /* Decompress directly into user's buffer. */ + if (cinfo->coef->_decompress_data == NULL) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); if (!(*cinfo->coef->_decompress_data) (cinfo, data)) return 0; /* suspension forced, can do nothing more */ diff --git a/3rdparty/libjpeg-turbo/src/jdcoefct.c b/3rdparty/libjpeg-turbo/src/jdcoefct.c index 40ce27259b..194d5a5cb7 100644 --- a/3rdparty/libjpeg-turbo/src/jdcoefct.c +++ b/3rdparty/libjpeg-turbo/src/jdcoefct.c @@ -5,7 +5,7 @@ * Copyright (C) 1994-1997, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2010, 2015-2016, 2019-2020, 2022-2023, D. R. Commander. + * Copyright (C) 2010, 2015-2016, 2019-2020, 2022-2024, D. R. Commander. * Copyright (C) 2015, 2020, Google, Inc. * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -824,6 +824,7 @@ _jinit_d_coef_controller(j_decompress_ptr cinfo, boolean need_full_buffer) coef = (my_coef_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_coef_controller)); + memset(coef, 0, sizeof(my_coef_controller)); cinfo->coef = (struct jpeg_d_coef_controller *)coef; coef->pub.start_input_pass = start_input_pass; coef->pub.start_output_pass = start_output_pass; diff --git a/3rdparty/libjpeg-turbo/src/jdmainct.c b/3rdparty/libjpeg-turbo/src/jdmainct.c index fed1866ee9..fc97073e3d 100644 --- a/3rdparty/libjpeg-turbo/src/jdmainct.c +++ b/3rdparty/libjpeg-turbo/src/jdmainct.c @@ -450,6 +450,7 @@ _jinit_d_main_controller(j_decompress_ptr cinfo, boolean need_full_buffer) main_ptr = (my_main_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, sizeof(my_main_controller)); + memset(main_ptr, 0, sizeof(my_main_controller)); cinfo->main = (struct jpeg_d_main_controller *)main_ptr; main_ptr->pub.start_pass = start_pass_main; diff --git a/3rdparty/libjpeg-turbo/src/jdsample.c b/3rdparty/libjpeg-turbo/src/jdsample.c index e5a127de42..022471dc37 100644 --- a/3rdparty/libjpeg-turbo/src/jdsample.c +++ b/3rdparty/libjpeg-turbo/src/jdsample.c @@ -5,7 +5,7 @@ * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2010, 2015-2016, 2022, 2024, D. R. Commander. + * Copyright (C) 2010, 2015-2016, 2022, 2024-2025, D. R. Commander. * Copyright (C) 2014, MIPS Technologies, Inc., California. * Copyright (C) 2015, Google, Inc. * Copyright (C) 2019-2020, Arm Limited. @@ -501,7 +501,8 @@ _jinit_upsampler(j_decompress_ptr cinfo) v_in_group * 2 == v_out_group && do_fancy) { /* Non-fancy upsampling is handled by the generic method */ #if defined(WITH_SIMD) && (defined(__arm__) || defined(__aarch64__) || \ - defined(_M_ARM) || defined(_M_ARM64)) + defined(_M_ARM) || defined(_M_ARM64) || \ + defined(_M_ARM64EC)) if (jsimd_can_h1v2_fancy_upsample()) upsample->methods[ci] = jsimd_h1v2_fancy_upsample; else diff --git a/3rdparty/libjpeg-turbo/src/jpeg_nbits.c b/3rdparty/libjpeg-turbo/src/jpeg_nbits.c index c8ee6b056c..752a163653 100644 --- a/3rdparty/libjpeg-turbo/src/jpeg_nbits.c +++ b/3rdparty/libjpeg-turbo/src/jpeg_nbits.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024, D. R. Commander. + * Copyright (C) 2024-2025, D. R. Commander. * * For conditions of distribution and use, see the accompanying README.ijg * file. @@ -17,7 +17,7 @@ * encoders can reuse jpeg_nbits_table from the SSE2 baseline Huffman encoder. */ #if (defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || \ - defined(_M_X64)) && defined(WITH_SIMD) + (defined(_M_X64) && !defined(_M_ARM64EC))) && defined(WITH_SIMD) #undef INCLUDE_JPEG_NBITS_TABLE #endif diff --git a/3rdparty/libjpeg-turbo/src/jpeg_nbits.h b/3rdparty/libjpeg-turbo/src/jpeg_nbits.h index 6481a1228d..61666fd57a 100644 --- a/3rdparty/libjpeg-turbo/src/jpeg_nbits.h +++ b/3rdparty/libjpeg-turbo/src/jpeg_nbits.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014, 2021, 2024, D. R. Commander. + * Copyright (C) 2014, 2021, 2024-2025, D. R. Commander. * Copyright (C) 2014, Olle Liljenzin. * Copyright (C) 2020, Arm Limited. * @@ -23,7 +23,7 @@ /* NOTE: Both GCC and Clang define __GNUC__ */ #if (defined(__GNUC__) && (defined(__arm__) || defined(__aarch64__))) || \ - defined(_M_ARM) || defined(_M_ARM64) + defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) #if !defined(__thumb__) || defined(__thumb2__) #define USE_CLZ_INTRINSIC #endif diff --git a/3rdparty/libpng/CHANGES b/3rdparty/libpng/CHANGES index 3f37f3f393..ea43101538 100644 --- a/3rdparty/libpng/CHANGES +++ b/3rdparty/libpng/CHANGES @@ -6229,6 +6229,98 @@ Version 1.6.45 [January 7, 2025] Raised the minimum required CMake version from 3.6 to 3.14. Forked off a development branch for libpng version 1.8. +Version 1.6.46 [January 23, 2025] + Added support for the mDCV and cLLI chunks. + (Contributed by John Bowler) + Fixed a build issue affecting C89 compilers. + This was a regression introduced in libpng-1.6.45. + (Contributed by John Bowler) + Added makefile.c89, specifically for testing C89 compilers. + Cleaned up contrib/pngminus: corrected an old typo, removed an old + workaround, and updated the CMake file. + +Version 1.6.47 [February 18, 2025] + Modified the behaviour of colorspace chunks in order to adhere + to the new precedence rules formulated in the latest draft of + the PNG Specification. + (Contributed by John Bowler) + Fixed a latent bug in `png_write_iCCP`. + This would have been a read-beyond-end-of-malloc vulnerability, + introduced early in the libpng-1.6.0 development, yet (fortunately!) + it was inaccessible before the above-mentioned modification of the + colorspace precedence rules, due to pre-existing colorspace checks. + (Reported by Bob Friesenhahn; fixed by John Bowler) + +Version 1.6.48 [April 30, 2025] + Fixed the floating-point version of the mDCv setter `png_set_mDCv`. + (Reported by Mohit Bakshi; fixed by John Bowler) + Added #error directives to discourage the inclusion of private + libpng implementation header files in PNG-supporting applications. + Added the CMake build option `PNG_LIBCONF_HEADER`, to be used as an + alternative to `DFA_XTRA`. + Removed the Travis CI configuration files, with heartfelt thanks for + their generous support of our project over the past five years! + +Version 1.6.49 [June 12, 2025] + Added SIMD-optimized code for the RISC-V Vector Extension (RVV). + (Contributed by Manfred Schlaegl, Dragos Tiselice and Filip Wasil) + Added various fixes and improvements to the build scripts and to + the sample code. + +Version 1.6.50 [July 1, 2025] + Improved the detection of the RVV Extension on the RISC-V platform. + (Contributed by Filip Wasil) + Replaced inline ASM with C intrinsics in the RVV code. + (Contributed by Filip Wasil) + Fixed a decoder defect in which unknown chunks trailing IDAT, set + to go through the unknown chunk handler, incorrectly triggered + out-of-place IEND errors. + (Contributed by John Bowler) + Fixed the CMake file for cross-platform builds that require `libm`. + +Version 1.6.51 [November 21, 2025] + Fixed CVE-2025-64505 (moderate severity): + Heap buffer overflow in `png_do_quantize` via malformed palette index. + (Reported by Samsung; analyzed by Fabio Gritti.) + Fixed CVE-2025-64506 (moderate severity): + Heap buffer over-read in `png_write_image_8bit` with 8-bit input and + `convert_to_8bit` enabled. + (Reported by Samsung and ; + analyzed by Fabio Gritti.) + Fixed CVE-2025-64720 (high severity): + Buffer overflow in `png_image_read_composite` via incorrect palette + premultiplication. + (Reported by Samsung; analyzed by John Bowler.) + Fixed CVE-2025-65018 (high severity): + Heap buffer overflow in `png_combine_row` triggered via + `png_image_finish_read`. + (Reported by .) + Fixed a memory leak in `png_set_quantize`. + (Reported by Samsung; analyzed by Fabio Gritti.) + Removed the experimental and incomplete ERROR_NUMBERS code. + (Contributed by Tobias Stoeckmann.) + Improved the RISC-V vector extension support; required RVV 1.0 or newer. + (Contributed by Filip Wasil.) + Added GitHub Actions workflows for automated testing. + Performed various refactorings and cleanups. + +Version 1.6.52 [December 3, 2025] + Fixed CVE-2025-66293 (high severity): + Out-of-bounds read in `png_image_read_composite`. + (Reported by flyfish101 .) + Fixed the Paeth filter handling in the RISC-V RVV implementation. + (Reported by Filip Wasil; fixed by Liang Junzhao.) + Improved the performance of the RISC-V RVV implementation. + (Contributed by Liang Junzhao.) + Added allocation failure fuzzing to oss-fuzz. + (Contributed by Philippe Antoine.) + +Version 1.6.53 [December 5, 2025] + Fixed a build failure on RISC-V RVV caused by a misspelled intrinsic. + (Contributed by Alexander Smorkalov.) + Fixed a build failure with CMake 4.1 or newer, on Windows, when using + Visual C++ without MASM installed. + Send comments/corrections/commendations to png-mng-implement at lists.sf.net. Subscription is required; visit https://lists.sourceforge.net/lists/listinfo/png-mng-implement diff --git a/3rdparty/libpng/CMakeLists.txt b/3rdparty/libpng/CMakeLists.txt index 4749c67b18..f6f39a17ed 100644 --- a/3rdparty/libpng/CMakeLists.txt +++ b/3rdparty/libpng/CMakeLists.txt @@ -74,8 +74,13 @@ endif() # Set definitions and sources for PowerPC. if(TARGET_ARCH MATCHES "^(powerpc|ppc64)") + if (CPU_VSX_SUPPORTED) + set(CPU_VSX_SUPPORTED_STR "on") + else() + set(CPU_VSX_SUPPORTED_STR "off") + endif() set(PNG_POWERPC_VSX_POSSIBLE_VALUES on off) - set(PNG_POWERPC_VSX "on" + set(PNG_POWERPC_VSX ${CPU_VSX_SUPPORTED_STR} CACHE STRING "Enable POWERPC VSX optimizations: on|off; on is default") set_property(CACHE PNG_POWERPC_VSX PROPERTY STRINGS ${PNG_POWERPC_VSX_POSSIBLE_VALUES}) @@ -114,8 +119,13 @@ endif() # Set definitions and sources for MIPS. if(TARGET_ARCH MATCHES "^(mipsel|mips64el)") + if (CPU_MSA_SUPPORTED) + set(CPU_MSA_SUPPORTED_STR "on") + else() + set(CPU_MSA_SUPPORTED_STR "off") + endif() set(PNG_MIPS_MSA_POSSIBLE_VALUES on off) - set(PNG_MIPS_MSA "on" + set(PNG_MIPS_MSA ${CPU_MSA_SUPPORTED_STR} CACHE STRING "Enable MIPS_MSA optimizations: on|off; on is default") set_property(CACHE PNG_MIPS_MSA PROPERTY STRINGS ${PNG_MIPS_MSA_POSSIBLE_VALUES}) @@ -154,9 +164,14 @@ endif() # Set definitions and sources for LoongArch. if(TARGET_ARCH MATCHES "^(loongarch)") + if (CPU_LSX_SUPPORTED) + set(CPU_LSX_SUPPORTED_STR "on") + else() + set(CPU_LSX_SUPPORTED_STR "off") + endif() include(CheckCCompilerFlag) set(PNG_LOONGARCH_LSX_POSSIBLE_VALUES on off) - set(PNG_LOONGARCH_LSX "on" + set(PNG_LOONGARCH_LSX ${CPU_LSX_SUPPORTED_STR} CACHE STRING "Enable LOONGARCH_LSX optimizations: on|off; on is default") set_property(CACHE PNG_LOONGARCH_LSX PROPERTY STRINGS ${PNG_LOONGARCH_LSX_POSSIBLE_VALUES}) @@ -172,7 +187,7 @@ if(TARGET_ARCH MATCHES "^(loongarch)") set_source_files_properties(${libpng_loongarch_sources} PROPERTIES COMPILE_FLAGS "-mlsx") - list(APPEND lib_srcs ${libpng_loongarch_sources}) + list(APPEND lib_srcs ${libpng_loongarch_sources}) add_definitions(-DPNG_LOONGARCH_LSX_OPT=1) else() message(FATAL_ERROR "Compiler does not support -mlsx option") @@ -182,6 +197,47 @@ if(TARGET_ARCH MATCHES "^(loongarch)") endif() endif() +# Set definitions and sources for RISC-V. +if(TARGET_ARCH MATCHES "^(riscv)") + if (CPU_RVV_SUPPORTED) + set(CPU_RVV_SUPPORTED_STR "on") + else() + set(CPU_RVV_SUPPORTED_STR "off") + endif() + include(CheckCCompilerFlag) + set(PNG_RISCV_RVV_POSSIBLE_VALUES on off) + set(PNG_RISCV_RVV ${CPU_RVV_SUPPORTED_STR} + CACHE STRING "Enable RISC-V Vector optimizations: on|off; off is default") + set_property(CACHE PNG_RISCV_RVV + PROPERTY STRINGS ${PNG_RISCV_RVV_POSSIBLE_VALUES}) + list(FIND PNG_RISCV_RVV_POSSIBLE_VALUES ${PNG_RISCV_RVV} index) + if(index EQUAL -1) + message(FATAL_ERROR "PNG_RISCV_RVV must be one of [${PNG_RISCV_RVV_POSSIBLE_VALUES}]") + elseif(NOT PNG_RISCV_RVV STREQUAL "off") + check_c_source_compiles(" + #include + int main() { + const float src[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + uint64_t ptr[2] = {0x0908060504020100, 0xFFFFFFFF0E0D0C0A}; + vuint8m1_t a = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(ptr, 2)); + vfloat32m1_t val = __riscv_vle32_v_f32m1((const float*)(src), 4); + return (int)__riscv_vfmv_f_s_f32m1_f32(val); + }" COMPILER_SUPPORTS_RVV) + + if(NOT COMPILER_SUPPORTS_RVV) + message(FATAL_ERROR "Compiler does not support RISC-V Vector extension or its unable to detect it") + endif() + list(APPEND lib_srcs riscv/filter_rvv_intrinsics.c riscv/riscv_init.c) + if(PNG_RISCV_RVV STREQUAL "on") + add_definitions(-DPNG_RISCV_RVV_OPT=2) + else() + add_definitions(-DPNG_RISCV_RVV_OPT=0) + endif() + else() + add_definitions(-DPNG_RISCV_RVV_OPT=0) + endif() +endif() + else(PNG_HARDWARE_OPTIMIZATIONS) # Set definitions and sources for ARM. @@ -209,6 +265,11 @@ if(TARGET_ARCH MATCHES "^(loongarch)") add_definitions(-DPNG_LOONGARCH_LSX_OPT=0) endif() +# Set definitions and sources for RISC-V. +if(TARGET_ARCH MATCHES "^(riscv)") + add_definitions(-DPNG_RISCV_RVV_OPT=0) +endif() + endif(PNG_HARDWARE_OPTIMIZATIONS) # ---------------------------------------------------------------------------------- diff --git a/3rdparty/libpng/README b/3rdparty/libpng/README index 1f73eb58cf..4041ad86eb 100644 --- a/3rdparty/libpng/README +++ b/3rdparty/libpng/README @@ -1,4 +1,4 @@ -README for libpng version 1.6.45 +README for libpng version 1.6.53 ================================ See the note about version numbers near the top of `png.h`. @@ -147,6 +147,7 @@ Files included in this distribution loongarch/ => Optimized code for LoongArch LSX mips/ => Optimized code for MIPS MSA and MIPS MMI powerpc/ => Optimized code for PowerPC VSX + riscv/ => Optimized code for the RISC-V platform ci/ => Scripts for continuous integration contrib/ => External contributions arm-neon/ => Optimized code for the ARM-NEON platform @@ -162,6 +163,7 @@ Files included in this distribution programs demonstrating the use of pngusr.dfa pngminus/ => Simple pnm2png and png2pnm programs pngsuite/ => Test images + riscv-rvv/ => Optimized code for the RISC-V Vector platform testpngs/ => Test images tools/ => Various tools visupng/ => VisualPng, a Windows viewer for PNG images diff --git a/3rdparty/libpng/arm/arm_init.c b/3rdparty/libpng/arm/arm_init.c index 50376081a3..0ea9c836cc 100644 --- a/3rdparty/libpng/arm/arm_init.c +++ b/3rdparty/libpng/arm/arm_init.c @@ -35,14 +35,14 @@ #ifndef PNG_ARM_NEON_FILE # if defined(__aarch64__) || defined(_M_ARM64) /* ARM Neon is expected to be unconditionally available on ARM64. */ -# error "PNG_ARM_NEON_CHECK_SUPPORTED must not be defined on ARM64" +# error PNG_ARM_NEON_CHECK_SUPPORTED must not be defined on ARM64 # elif defined(__ARM_NEON__) || defined(__ARM_NEON) /* ARM Neon is expected to be available on the target CPU architecture. */ -# error "PNG_ARM_NEON_CHECK_SUPPORTED must not be defined on this CPU arch" +# error PNG_ARM_NEON_CHECK_SUPPORTED must not be defined on this CPU arch # elif defined(__linux__) # define PNG_ARM_NEON_FILE "contrib/arm-neon/linux.c" # else -# error "No support for run-time ARM Neon checking; use compile-time options" +# error No support for run-time ARM Neon checking; use compile-time options # endif #endif @@ -53,7 +53,7 @@ static int png_have_neon(png_structp png_ptr); #endif /* PNG_ARM_NEON_CHECK_SUPPORTED */ #ifndef PNG_ALIGNED_MEMORY_SUPPORTED -# error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED" +# error ALIGNED_MEMORY is required; please define PNG_ALIGNED_MEMORY_SUPPORTED #endif void diff --git a/3rdparty/libpng/mips/mips_init.c b/3rdparty/libpng/mips/mips_init.c index 143f0a3714..5f9346f9e3 100644 --- a/3rdparty/libpng/mips/mips_init.c +++ b/3rdparty/libpng/mips/mips_init.c @@ -48,7 +48,7 @@ static int png_have_msa(png_structp png_ptr); #include PNG_MIPS_MSA_FILE #else /* PNG_MIPS_MSA_FILE */ -# error "PNG_MIPS_MSA_FILE undefined: no support for run-time MIPS MSA checks" +# error PNG_MIPS_MSA_FILE undefined: no support for run-time MIPS MSA checks #endif /* PNG_MIPS_MSA_FILE */ #endif /* PNG_MIPS_MSA_CHECK_SUPPORTED */ @@ -66,12 +66,12 @@ static int png_have_mmi(); #include PNG_MIPS_MMI_FILE #else /* PNG_MIPS_MMI_FILE */ -# error "PNG_MIPS_MMI_FILE undefined: no support for run-time MIPS MMI checks" +# error PNG_MIPS_MMI_FILE undefined: no support for run-time MIPS MMI checks #endif /* PNG_MIPS_MMI_FILE */ #endif /* PNG_MIPS_MMI_CHECK_SUPPORTED*/ #ifndef PNG_ALIGNED_MEMORY_SUPPORTED -# error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED" +# error ALIGNED_MEMORY is required; please define PNG_ALIGNED_MEMORY_SUPPORTED #endif /* MIPS supports two optimizations: MMI and MSA. The appropriate diff --git a/3rdparty/libpng/patches/riscv_rvv_fix.patch b/3rdparty/libpng/patches/riscv_rvv_fix.patch new file mode 100644 index 0000000000..77b125a899 --- /dev/null +++ b/3rdparty/libpng/patches/riscv_rvv_fix.patch @@ -0,0 +1,13 @@ +diff --git a/3rdparty/libpng/riscv/filter_rvv_intrinsics.c b/3rdparty/libpng/riscv/filter_rvv_intrinsics.c +index 8d277d14cd..7e61fb89fb 100644 +--- a/3rdparty/libpng/riscv/filter_rvv_intrinsics.c ++++ b/3rdparty/libpng/riscv/filter_rvv_intrinsics.c +@@ -142,7 +142,7 @@ png_read_filter_row_avg_rvv(size_t len, size_t bpp, unsigned char* row, + x = __riscv_vle8_v_u8m1(row, vl); + + /* a = (a + b) / 2, round to zero with vxrm = 2 */ +- a = __riscv_vaaddu_wx_u8m1(a, b, 2, vl); ++ a = __riscv_vaaddu_vv_u8m1(a, b, 2, vl); + + /* a += x */ + a = __riscv_vadd_vv_u8m1(a, x, vl); diff --git a/3rdparty/libpng/png.c b/3rdparty/libpng/png.c index 466af7d992..85b4949652 100644 --- a/3rdparty/libpng/png.c +++ b/3rdparty/libpng/png.c @@ -13,7 +13,34 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_45 Your_png_h_is_not_version_1_6_45; +typedef png_libpng_version_1_6_53 Your_png_h_is_not_version_1_6_53; + +/* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the + * corresponding macro definitions. This causes a compile time failure if + * something is wrong but generates no code. + * + * (1) The first check is that the PNG_CHUNK(cHNK, index) 'index' values must + * increment from 0 to the last value. + */ +#define PNG_CHUNK(cHNK, index) != (index) || ((index)+1) + +#if 0 PNG_KNOWN_CHUNKS < 0 +# error PNG_KNOWN_CHUNKS chunk definitions are not in order +#endif + +#undef PNG_CHUNK + +/* (2) The chunk name macros, png_cHNK, must all be valid and defined. Since + * this is a preprocessor test undefined pp-tokens come out as zero and will + * fail this test. + */ +#define PNG_CHUNK(cHNK, index) !PNG_CHUNK_NAME_VALID(png_ ## cHNK) || + +#if PNG_KNOWN_CHUNKS 0 +# error png_cHNK not defined for some known cHNK +#endif + +#undef PNG_CHUNK /* Tells libpng that we have already handled the first "num_bytes" bytes * of the PNG file signature. If the PNG data is embedded into another @@ -81,10 +108,16 @@ png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED) if (png_ptr == NULL) return NULL; - if (items >= (~(png_alloc_size_t)0)/size) + /* This check against overflow is vestigial, dating back from + * the old times when png_zalloc used to be an exported function. + * We're still keeping it here for now, as an extra-cautious + * prevention against programming errors inside zlib, although it + * should rather be a debug-time assertion instead. + */ + if (size != 0 && items >= (~(png_alloc_size_t)0) / size) { - png_warning (png_voidcast(png_structrp, png_ptr), - "Potential overflow in png_zalloc()"); + png_warning(png_voidcast(png_structrp, png_ptr), + "Potential overflow in png_zalloc()"); return NULL; } @@ -211,10 +244,6 @@ png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver) png_warning(png_ptr, m); #endif -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags = 0; -#endif - return 0; } @@ -241,21 +270,23 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, */ memset(&create_struct, 0, (sizeof create_struct)); - /* Added at libpng-1.2.6 */ # ifdef PNG_USER_LIMITS_SUPPORTED create_struct.user_width_max = PNG_USER_WIDTH_MAX; create_struct.user_height_max = PNG_USER_HEIGHT_MAX; # ifdef PNG_USER_CHUNK_CACHE_MAX - /* Added at libpng-1.2.43 and 1.4.0 */ create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; # endif -# ifdef PNG_USER_CHUNK_MALLOC_MAX - /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists - * in png_struct regardless. - */ +# if PNG_USER_CHUNK_MALLOC_MAX > 0 /* default to compile-time limit */ create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX; + + /* No compile-time limit, so initialize to the system limit: */ +# elif defined PNG_MAX_MALLOC_64K /* legacy system limit */ + create_struct.user_chunk_malloc_max = 65536U; + +# else /* modern system limit SIZE_MAX (C99) */ + create_struct.user_chunk_malloc_max = PNG_SIZE_MAX; # endif # endif @@ -597,13 +628,6 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, /* Free any eXIf entry */ if (((mask & PNG_FREE_EXIF) & info_ptr->free_me) != 0) { -# ifdef PNG_READ_eXIf_SUPPORTED - if (info_ptr->eXIf_buf) - { - png_free(png_ptr, info_ptr->eXIf_buf); - info_ptr->eXIf_buf = NULL; - } -# endif if (info_ptr->exif) { png_free(png_ptr, info_ptr->exif); @@ -678,7 +702,7 @@ png_get_io_ptr(png_const_structrp png_ptr) * function of your own because "FILE *" isn't necessarily available. */ void PNGAPI -png_init_io(png_structrp png_ptr, png_FILE_p fp) +png_init_io(png_structrp png_ptr, FILE *fp) { png_debug(1, "in png_init_io"); @@ -793,7 +817,7 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.45" PNG_STRING_NEWLINE \ + "libpng version 1.6.53" PNG_STRING_NEWLINE \ "Copyright (c) 2018-2025 Cosmin Truta" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ PNG_STRING_NEWLINE \ @@ -1038,169 +1062,6 @@ png_zstream_error(png_structrp png_ptr, int ret) } } -/* png_convert_size: a PNGAPI but no longer in png.h, so deleted - * at libpng 1.5.5! - */ - -/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ -#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */ -static int -png_colorspace_check_gamma(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_fixed_point gAMA, int from) - /* This is called to check a new gamma value against an existing one. The - * routine returns false if the new gamma value should not be written. - * - * 'from' says where the new gamma value comes from: - * - * 0: the new gamma value is the libpng estimate for an ICC profile - * 1: the new gamma value comes from a gAMA chunk - * 2: the new gamma value comes from an sRGB chunk - */ -{ - png_fixed_point gtest; - - if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && - (png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) == 0 || - png_gamma_significant(gtest) != 0)) - { - /* Either this is an sRGB image, in which case the calculated gamma - * approximation should match, or this is an image with a profile and the - * value libpng calculates for the gamma of the profile does not match the - * value recorded in the file. The former, sRGB, case is an error, the - * latter is just a warning. - */ - if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2) - { - png_chunk_report(png_ptr, "gamma value does not match sRGB", - PNG_CHUNK_ERROR); - /* Do not overwrite an sRGB value */ - return from == 2; - } - - else /* sRGB tag not involved */ - { - png_chunk_report(png_ptr, "gamma value does not match libpng estimate", - PNG_CHUNK_WARNING); - return from == 1; - } - } - - return 1; -} - -void /* PRIVATE */ -png_colorspace_set_gamma(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_fixed_point gAMA) -{ - /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't - * occur. Since the fixed point representation is asymmetrical it is - * possible for 1/gamma to overflow the limit of 21474 and this means the - * gamma value must be at least 5/100000 and hence at most 20000.0. For - * safety the limits here are a little narrower. The values are 0.00016 to - * 6250.0, which are truly ridiculous gamma values (and will produce - * displays that are all black or all white.) - * - * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk - * handling code, which only required the value to be >0. - */ - png_const_charp errmsg; - - if (gAMA < 16 || gAMA > 625000000) - errmsg = "gamma value out of range"; - -# ifdef PNG_READ_gAMA_SUPPORTED - /* Allow the application to set the gamma value more than once */ - else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && - (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0) - errmsg = "duplicate"; -# endif - - /* Do nothing if the colorspace is already invalid */ - else if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) - return; - - else - { - if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, - 1/*from gAMA*/) != 0) - { - /* Store this gamma value. */ - colorspace->gamma = gAMA; - colorspace->flags |= - (PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA); - } - - /* At present if the check_gamma test fails the gamma of the colorspace is - * not updated however the colorspace is not invalidated. This - * corresponds to the case where the existing gamma comes from an sRGB - * chunk or profile. An error message has already been output. - */ - return; - } - - /* Error exit - errmsg has been set. */ - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR); -} - -void /* PRIVATE */ -png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) -{ - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) - { - /* Everything is invalid */ - info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| - PNG_INFO_iCCP); - -# ifdef PNG_COLORSPACE_SUPPORTED - /* Clean up the iCCP profile now if it won't be used. */ - png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/); -# else - PNG_UNUSED(png_ptr) -# endif - } - - else - { -# ifdef PNG_COLORSPACE_SUPPORTED - /* Leave the INFO_iCCP flag set if the pngset.c code has already set - * it; this allows a PNG to contain a profile which matches sRGB and - * yet still have that profile retrievable by the application. - */ - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) != 0) - info_ptr->valid |= PNG_INFO_sRGB; - - else - info_ptr->valid &= ~PNG_INFO_sRGB; - - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) - info_ptr->valid |= PNG_INFO_cHRM; - - else - info_ptr->valid &= ~PNG_INFO_cHRM; -# endif - - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0) - info_ptr->valid |= PNG_INFO_gAMA; - - else - info_ptr->valid &= ~PNG_INFO_gAMA; - } -} - -#ifdef PNG_READ_SUPPORTED -void /* PRIVATE */ -png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr) -{ - if (info_ptr == NULL) /* reduce code size; check here not in the caller */ - return; - - info_ptr->colorspace = png_ptr->colorspace; - png_colorspace_sync_info(png_ptr, info_ptr); -} -#endif -#endif /* GAMMA */ - #ifdef PNG_COLORSPACE_SUPPORTED static png_int_32 png_fp_add(png_int_32 addend0, png_int_32 addend1, int *error) @@ -1269,9 +1130,10 @@ png_safe_add(png_int_32 *addend0_and_result, png_int_32 addend1, * non-zero on a parameter error. The X, Y and Z values are required to be * positive and less than 1.0. */ -static int +int /* PRIVATE */ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ) { + /* NOTE: returns 0 on success, 1 means error. */ png_int_32 d, dred, dgreen, dblue, dwhite, whiteX, whiteY; /* 'd' in each of the blocks below is just X+Y+Z for each component, @@ -1334,9 +1196,10 @@ png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ) return 0; } -static int +int /* PRIVATE */ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) { + /* NOTE: returns 0 on success, 1 means error. */ png_fixed_point red_inverse, green_inverse, blue_scale; png_fixed_point left, right, denominator; @@ -1544,56 +1407,59 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) * Adobe Wide Gamut RGB * 0.258728243040113 0.724682314948566 0.016589442011321 */ - int error = 0; + { + int error = 0; - /* By the argument above overflow should be impossible here, however the - * code now simply returns a failure code. The xy subtracts in the arguments - * to png_muldiv are *not* checked for overflow because the checks at the - * start guarantee they are in the range 0..110000 and png_fixed_point is a - * 32-bit signed number. - */ - if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 8) == 0) - return 1; - if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 8) == 0) - return 1; - denominator = png_fp_sub(left, right, &error); - if (error) return 1; + /* By the argument above overflow should be impossible here, however the + * code now simply returns a failure code. The xy subtracts in the + * arguments to png_muldiv are *not* checked for overflow because the + * checks at the start guarantee they are in the range 0..110000 and + * png_fixed_point is a 32-bit signed number. + */ + if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 8) == 0) + return 1; + if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 8) == + 0) + return 1; + denominator = png_fp_sub(left, right, &error); + if (error) return 1; - /* Now find the red numerator. */ - if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 8) == 0) - return 1; - if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 8) == 0) - return 1; + /* Now find the red numerator. */ + if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 8) == 0) + return 1; + if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 8) == + 0) + return 1; - /* Overflow is possible here and it indicates an extreme set of PNG cHRM - * chunk values. This calculation actually returns the reciprocal of the - * scale value because this allows us to delay the multiplication of white-y - * into the denominator, which tends to produce a small number. - */ - if (png_muldiv(&red_inverse, xy->whitey, denominator, - png_fp_sub(left, right, &error)) == 0 || error || - red_inverse <= xy->whitey /* r+g+b scales = white scale */) - return 1; + /* Overflow is possible here and it indicates an extreme set of PNG cHRM + * chunk values. This calculation actually returns the reciprocal of the + * scale value because this allows us to delay the multiplication of + * white-y into the denominator, which tends to produce a small number. + */ + if (png_muldiv(&red_inverse, xy->whitey, denominator, + png_fp_sub(left, right, &error)) == 0 || error || + red_inverse <= xy->whitey /* r+g+b scales = white scale */) + return 1; - /* Similarly for green_inverse: */ - if (png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 8) == 0) - return 1; - if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 8) == 0) - return 1; - if (png_muldiv(&green_inverse, xy->whitey, denominator, - png_fp_sub(left, right, &error)) == 0 || error || - green_inverse <= xy->whitey) - return 1; - - /* And the blue scale, the checks above guarantee this can't overflow but it - * can still produce 0 for extreme cHRM values. - */ - blue_scale = png_fp_sub(png_fp_sub(png_reciprocal(xy->whitey), - png_reciprocal(red_inverse), &error), - png_reciprocal(green_inverse), &error); - if (error || blue_scale <= 0) - return 1; + /* Similarly for green_inverse: */ + if (png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 8) == 0) + return 1; + if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 8) == 0) + return 1; + if (png_muldiv(&green_inverse, xy->whitey, denominator, + png_fp_sub(left, right, &error)) == 0 || error || + green_inverse <= xy->whitey) + return 1; + /* And the blue scale, the checks above guarantee this can't overflow but + * it can still produce 0 for extreme cHRM values. + */ + blue_scale = png_fp_sub(png_fp_sub(png_reciprocal(xy->whitey), + png_reciprocal(red_inverse), &error), + png_reciprocal(green_inverse), &error); + if (error || blue_scale <= 0) + return 1; + } /* And fill in the png_XYZ. Again the subtracts are safe because of the * checks on the xy values at the start (the subtracts just calculate the @@ -1625,239 +1491,9 @@ png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) return 0; /*success*/ } +#endif /* COLORSPACE */ -static int -png_XYZ_normalize(png_XYZ *XYZ) -{ - png_int_32 Y, Ytemp; - - /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1. */ - Ytemp = XYZ->red_Y; - if (png_safe_add(&Ytemp, XYZ->green_Y, XYZ->blue_Y)) - return 1; - - Y = Ytemp; - - if (Y != PNG_FP_1) - { - if (png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y) == 0) - return 1; - - if (png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y) == 0) - return 1; - - if (png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y) == 0) - return 1; - if (png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y) == 0) - return 1; - } - - return 0; -} - -static int -png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta) -{ - /* Allow an error of +/-0.01 (absolute value) on each chromaticity */ - if (PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) || - PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) || - PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) || - PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) || - PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) || - PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) || - PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) || - PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta)) - return 0; - return 1; -} - -/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM - * chunk chromaticities. Earlier checks used to simply look for the overflow - * condition (where the determinant of the matrix to solve for XYZ ends up zero - * because the chromaticity values are not all distinct.) Despite this it is - * theoretically possible to produce chromaticities that are apparently valid - * but that rapidly degrade to invalid, potentially crashing, sets because of - * arithmetic inaccuracies when calculations are performed on them. The new - * check is to round-trip xy -> XYZ -> xy and then check that the result is - * within a small percentage of the original. - */ -static int -png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy) -{ - int result; - png_xy xy_test; - - /* As a side-effect this routine also returns the XYZ endpoints. */ - result = png_XYZ_from_xy(XYZ, xy); - if (result != 0) - return result; - - result = png_xy_from_XYZ(&xy_test, XYZ); - if (result != 0) - return result; - - if (png_colorspace_endpoints_match(xy, &xy_test, - 5/*actually, the math is pretty accurate*/) != 0) - return 0; - - /* Too much slip */ - return 1; -} - -/* This is the check going the other way. The XYZ is modified to normalize it - * (another side-effect) and the xy chromaticities are returned. - */ -static int -png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ) -{ - int result; - png_XYZ XYZtemp; - - result = png_XYZ_normalize(XYZ); - if (result != 0) - return result; - - result = png_xy_from_XYZ(xy, XYZ); - if (result != 0) - return result; - - XYZtemp = *XYZ; - return png_colorspace_check_xy(&XYZtemp, xy); -} - -/* Used to check for an endpoint match against sRGB */ -static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */ -{ - /* color x y */ - /* red */ 64000, 33000, - /* green */ 30000, 60000, - /* blue */ 15000, 6000, - /* white */ 31270, 32900 -}; - -static int -png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr, - png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ, - int preferred) -{ - if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) - return 0; - - /* The consistency check is performed on the chromaticities; this factors out - * variations because of the normalization (or not) of the end point Y - * values. - */ - if (preferred < 2 && - (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) - { - /* The end points must be reasonably close to any we already have. The - * following allows an error of up to +/-.001 - */ - if (png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, - 100) == 0) - { - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_benign_error(png_ptr, "inconsistent chromaticities"); - return 0; /* failed */ - } - - /* Only overwrite with preferred values */ - if (preferred == 0) - return 1; /* ok, but no change */ - } - - colorspace->end_points_xy = *xy; - colorspace->end_points_XYZ = *XYZ; - colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS; - - /* The end points are normally quoted to two decimal digits, so allow +/-0.01 - * on this test. - */ - if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000) != 0) - colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB; - - else - colorspace->flags &= PNG_COLORSPACE_CANCEL( - PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); - - return 2; /* ok and changed */ -} - -int /* PRIVATE */ -png_colorspace_set_chromaticities(png_const_structrp png_ptr, - png_colorspacerp colorspace, const png_xy *xy, int preferred) -{ - /* We must check the end points to ensure they are reasonable - in the past - * color management systems have crashed as a result of getting bogus - * colorant values, while this isn't the fault of libpng it is the - * responsibility of libpng because PNG carries the bomb and libpng is in a - * position to protect against it. - */ - png_XYZ XYZ; - - switch (png_colorspace_check_xy(&XYZ, xy)) - { - case 0: /* success */ - return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ, - preferred); - - case 1: - /* We can't invert the chromaticities so we can't produce value XYZ - * values. Likely as not a color management system will fail too. - */ - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_benign_error(png_ptr, "invalid chromaticities"); - break; - - default: - /* libpng is broken; this should be a warning but if it happens we - * want error reports so for the moment it is an error. - */ - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_error(png_ptr, "internal error checking chromaticities"); - } - - return 0; /* failed */ -} - -int /* PRIVATE */ -png_colorspace_set_endpoints(png_const_structrp png_ptr, - png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred) -{ - png_XYZ XYZ = *XYZ_in; - png_xy xy; - - switch (png_colorspace_check_XYZ(&xy, &XYZ)) - { - case 0: - return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ, - preferred); - - case 1: - /* End points are invalid. */ - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_benign_error(png_ptr, "invalid end points"); - break; - - default: - colorspace->flags |= PNG_COLORSPACE_INVALID; - png_error(png_ptr, "internal error checking chromaticities"); - } - - return 0; /* failed */ -} - -#if defined(PNG_sRGB_SUPPORTED) || defined(PNG_iCCP_SUPPORTED) +#ifdef PNG_READ_iCCP_SUPPORTED /* Error message generation */ static char png_icc_tag_char(png_uint_32 byte) @@ -1897,15 +1533,12 @@ is_ICC_signature(png_alloc_size_t it) } static int -png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_alloc_size_t value, png_const_charp reason) +png_icc_profile_error(png_const_structrp png_ptr, png_const_charp name, + png_alloc_size_t value, png_const_charp reason) { size_t pos; char message[196]; /* see below for calculation */ - if (colorspace != NULL) - colorspace->flags |= PNG_COLORSPACE_INVALID; - pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */ pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */ pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */ @@ -1932,109 +1565,11 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, pos = png_safecat(message, (sizeof message), pos, reason); PNG_UNUSED(pos) - /* This is recoverable, but make it unconditionally an app_error on write to - * avoid writing invalid ICC profiles into PNG files (i.e., we handle them - * on read, with a warning, but on write unless the app turns off - * application errors the PNG won't be written.) - */ - png_chunk_report(png_ptr, message, - (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR); + png_chunk_benign_error(png_ptr, message); return 0; } -#endif /* sRGB || iCCP */ -#ifdef PNG_sRGB_SUPPORTED -int /* PRIVATE */ -png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, - int intent) -{ - /* sRGB sets known gamma, end points and (from the chunk) intent. */ - /* IMPORTANT: these are not necessarily the values found in an ICC profile - * because ICC profiles store values adapted to a D50 environment; it is - * expected that the ICC profile mediaWhitePointTag will be D50; see the - * checks and code elsewhere to understand this better. - * - * These XYZ values, which are accurate to 5dp, produce rgb to gray - * coefficients of (6968,23435,2366), which are reduced (because they add up - * to 32769 not 32768) to (6968,23434,2366). These are the values that - * libpng has traditionally used (and are the best values given the 15bit - * algorithm used by the rgb to gray code.) - */ - static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */ - { - /* color X Y Z */ - /* red */ 41239, 21264, 1933, - /* green */ 35758, 71517, 11919, - /* blue */ 18048, 7219, 95053 - }; - - /* Do nothing if the colorspace is already invalidated. */ - if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) - return 0; - - /* Check the intent, then check for existing settings. It is valid for the - * PNG file to have cHRM or gAMA chunks along with sRGB, but the values must - * be consistent with the correct values. If, however, this function is - * called below because an iCCP chunk matches sRGB then it is quite - * conceivable that an older app recorded incorrect gAMA and cHRM because of - * an incorrect calculation based on the values in the profile - this does - * *not* invalidate the profile (though it still produces an error, which can - * be ignored.) - */ - if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) - return png_icc_profile_error(png_ptr, colorspace, "sRGB", - (png_alloc_size_t)intent, "invalid sRGB rendering intent"); - - if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 && - colorspace->rendering_intent != intent) - return png_icc_profile_error(png_ptr, colorspace, "sRGB", - (png_alloc_size_t)intent, "inconsistent rendering intents"); - - if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0) - { - png_benign_error(png_ptr, "duplicate sRGB information ignored"); - return 0; - } - - /* If the standard sRGB cHRM chunk does not match the one from the PNG file - * warn but overwrite the value with the correct one. - */ - if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 && - !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy, - 100)) - png_chunk_report(png_ptr, "cHRM chunk does not match sRGB", - PNG_CHUNK_ERROR); - - /* This check is just done for the error reporting - the routine always - * returns true when the 'from' argument corresponds to sRGB (2). - */ - (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE, - 2/*from sRGB*/); - - /* intent: bugs in GCC force 'int' to be used as the parameter type. */ - colorspace->rendering_intent = (png_uint_16)intent; - colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT; - - /* endpoints */ - colorspace->end_points_xy = sRGB_xy; - colorspace->end_points_XYZ = sRGB_XYZ; - colorspace->flags |= - (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); - - /* gamma */ - colorspace->gamma = PNG_GAMMA_sRGB_INVERSE; - colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA; - - /* Finally record that we have an sRGB profile */ - colorspace->flags |= - (PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB); - - return 1; /* set */ -} -#endif /* sRGB */ - -#ifdef PNG_iCCP_SUPPORTED /* Encoded value of D50 as an ICC XYZNumber. From the ICC 2010 spec the value * is XYZ(0.9642,1.0,0.8249), which scales to: * @@ -2044,21 +1579,19 @@ static const png_byte D50_nCIEXYZ[12] = { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d }; static int /* bool */ -icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length) +icc_check_length(png_const_structrp png_ptr, png_const_charp name, + png_uint_32 profile_length) { if (profile_length < 132) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "too short"); + return png_icc_profile_error(png_ptr, name, profile_length, "too short"); return 1; } -#ifdef PNG_READ_iCCP_SUPPORTED int /* PRIVATE */ -png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length) +png_icc_check_length(png_const_structrp png_ptr, png_const_charp name, + png_uint_32 profile_length) { - if (!icc_check_length(png_ptr, colorspace, name, profile_length)) + if (!icc_check_length(png_ptr, name, profile_length)) return 0; /* This needs to be here because the 'normal' check is in @@ -2067,30 +1600,17 @@ png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, * the caller supplies the profile buffer so libpng doesn't allocate it. See * the call to icc_check_length below (the write case). */ -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - else if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < profile_length) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "exceeds application limits"); -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - else if (PNG_USER_CHUNK_MALLOC_MAX < profile_length) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "exceeds libpng limits"); -# else /* !SET_USER_LIMITS */ - /* This will get compiled out on all 32-bit and better systems. */ - else if (PNG_SIZE_MAX < profile_length) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, - "exceeds system limits"); -# endif /* !SET_USER_LIMITS */ + if (profile_length > png_chunk_max(png_ptr)) + return png_icc_profile_error(png_ptr, name, profile_length, + "profile too long"); return 1; } -#endif /* READ_iCCP */ int /* PRIVATE */ -png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length, - png_const_bytep profile/* first 132 bytes only */, int color_type) +png_icc_check_header(png_const_structrp png_ptr, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile/* first 132 bytes only */, int color_type) { png_uint_32 temp; @@ -2101,18 +1621,18 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, */ temp = png_get_uint_32(profile); if (temp != profile_length) - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "length does not match profile"); temp = (png_uint_32) (*(profile+8)); if (temp > 3 && (profile_length & 3)) - return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + return png_icc_profile_error(png_ptr, name, profile_length, "invalid length"); temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */ if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */ profile_length < 132+12*temp) /* truncated tag table */ - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "tag count too large"); /* The 'intent' must be valid or we can't store it, ICC limits the intent to @@ -2120,14 +1640,14 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, */ temp = png_get_uint_32(profile+64); if (temp >= 0xffff) /* The ICC limit */ - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "invalid rendering intent"); /* This is just a warning because the profile may be valid in future * versions. */ if (temp >= PNG_sRGB_INTENT_LAST) - (void)png_icc_profile_error(png_ptr, NULL, name, temp, + (void)png_icc_profile_error(png_ptr, name, temp, "intent outside defined range"); /* At this point the tag table can't be checked because it hasn't necessarily @@ -2144,7 +1664,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, */ temp = png_get_uint_32(profile+36); /* signature 'ascp' */ if (temp != 0x61637370) - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "invalid signature"); /* Currently the PCS illuminant/adopted white point (the computational @@ -2155,7 +1675,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, * following is just a warning. */ if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0) - (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/, + (void)png_icc_profile_error(png_ptr, name, 0/*no tag value*/, "PCS illuminant is not D50"); /* The PNG spec requires this: @@ -2183,18 +1703,18 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, { case 0x52474220: /* 'RGB ' */ if ((color_type & PNG_COLOR_MASK_COLOR) == 0) - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "RGB color space not permitted on grayscale PNG"); break; case 0x47524159: /* 'GRAY' */ if ((color_type & PNG_COLOR_MASK_COLOR) != 0) - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "Gray color space not permitted on RGB PNG"); break; default: - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "invalid ICC profile color space"); } @@ -2219,7 +1739,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, case 0x61627374: /* 'abst' */ /* May not be embedded in an image */ - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "invalid embedded Abstract ICC profile"); case 0x6c696e6b: /* 'link' */ @@ -2229,7 +1749,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, * therefore a DeviceLink profile should not be found embedded in a * PNG. */ - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "unexpected DeviceLink ICC profile class"); case 0x6e6d636c: /* 'nmcl' */ @@ -2237,7 +1757,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, * contain an AToB0 tag that is open to misinterpretation. Almost * certainly it will fail the tests below. */ - (void)png_icc_profile_error(png_ptr, NULL, name, temp, + (void)png_icc_profile_error(png_ptr, name, temp, "unexpected NamedColor ICC profile class"); break; @@ -2247,7 +1767,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, * tag content to ensure they are backward compatible with one of the * understood profiles. */ - (void)png_icc_profile_error(png_ptr, NULL, name, temp, + (void)png_icc_profile_error(png_ptr, name, temp, "unrecognized ICC profile class"); break; } @@ -2263,7 +1783,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, break; default: - return png_icc_profile_error(png_ptr, colorspace, name, temp, + return png_icc_profile_error(png_ptr, name, temp, "unexpected ICC PCS encoding"); } @@ -2271,9 +1791,9 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, } int /* PRIVATE */ -png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length, - png_const_bytep profile /* header plus whole tag table */) +png_icc_check_tag_table(png_const_structrp png_ptr, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */) { png_uint_32 tag_count = png_get_uint_32(profile+128); png_uint_32 itag; @@ -2299,7 +1819,7 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, * profile. */ if (tag_start > profile_length || tag_length > profile_length - tag_start) - return png_icc_profile_error(png_ptr, colorspace, name, tag_id, + return png_icc_profile_error(png_ptr, name, tag_id, "ICC profile tag outside profile"); if ((tag_start & 3) != 0) @@ -2308,307 +1828,132 @@ png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, * only a warning here because libpng does not care about the * alignment. */ - (void)png_icc_profile_error(png_ptr, NULL, name, tag_id, + (void)png_icc_profile_error(png_ptr, name, tag_id, "ICC profile tag start not a multiple of 4"); } } return 1; /* success, maybe with warnings */ } - -#ifdef PNG_sRGB_SUPPORTED -#if PNG_sRGB_PROFILE_CHECKS >= 0 -/* Information about the known ICC sRGB profiles */ -static const struct -{ - png_uint_32 adler, crc, length; - png_uint_32 md5[4]; - png_byte have_md5; - png_byte is_broken; - png_uint_16 intent; - -# define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0) -# define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\ - { adler, crc, length, md5, broke, intent }, - -} png_sRGB_checks[] = -{ - /* This data comes from contrib/tools/checksum-icc run on downloads of - * all four ICC sRGB profiles from www.color.org. - */ - /* adler32, crc32, MD5[4], intent, date, length, file-name */ - PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9, - PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0, - "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc") - - /* ICC sRGB v2 perceptual no black-compensation: */ - PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21, - PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0, - "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc") - - PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae, - PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0, - "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc") - - /* ICC sRGB v4 perceptual */ - PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812, - PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0, - "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc") - - /* The following profiles have no known MD5 checksum. If there is a match - * on the (empty) MD5 the other fields are used to attempt a match and - * a warning is produced. The first two of these profiles have a 'cprt' tag - * which suggests that they were also made by Hewlett Packard. - */ - PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce, - PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0, - "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc") - - /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not - * match the D50 PCS illuminant in the header (it is in fact the D65 values, - * so the white point is recorded as the un-adapted value.) The profiles - * below only differ in one byte - the intent - and are basically the same as - * the previous profile except for the mediaWhitePointTag error and a missing - * chromaticAdaptationTag. - */ - PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552, - PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/, - "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual") - - PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d, - PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/, - "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative") -}; - -static int -png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, - png_const_bytep profile, uLong adler) -{ - /* The quick check is to verify just the MD5 signature and trust the - * rest of the data. Because the profile has already been verified for - * correctness this is safe. png_colorspace_set_sRGB will check the 'intent' - * field too, so if the profile has been edited with an intent not defined - * by sRGB (but maybe defined by a later ICC specification) the read of - * the profile will fail at that point. - */ - - png_uint_32 length = 0; - png_uint_32 intent = 0x10000; /* invalid */ -#if PNG_sRGB_PROFILE_CHECKS > 1 - uLong crc = 0; /* the value for 0 length data */ -#endif - unsigned int i; - -#ifdef PNG_SET_OPTION_SUPPORTED - /* First see if PNG_SKIP_sRGB_CHECK_PROFILE has been set to "on" */ - if (((png_ptr->options >> PNG_SKIP_sRGB_CHECK_PROFILE) & 3) == - PNG_OPTION_ON) - return 0; -#endif - - for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i) - { - if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] && - png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] && - png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] && - png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3]) - { - /* This may be one of the old HP profiles without an MD5, in that - * case we can only use the length and Adler32 (note that these - * are not used by default if there is an MD5!) - */ -# if PNG_sRGB_PROFILE_CHECKS == 0 - if (png_sRGB_checks[i].have_md5 != 0) - return 1+png_sRGB_checks[i].is_broken; -# endif - - /* Profile is unsigned or more checks have been configured in. */ - if (length == 0) - { - length = png_get_uint_32(profile); - intent = png_get_uint_32(profile+64); - } - - /* Length *and* intent must match */ - if (length == (png_uint_32) png_sRGB_checks[i].length && - intent == (png_uint_32) png_sRGB_checks[i].intent) - { - /* Now calculate the adler32 if not done already. */ - if (adler == 0) - { - adler = adler32(0, NULL, 0); - adler = adler32(adler, profile, length); - } - - if (adler == png_sRGB_checks[i].adler) - { - /* These basic checks suggest that the data has not been - * modified, but if the check level is more than 1 perform - * our own crc32 checksum on the data. - */ -# if PNG_sRGB_PROFILE_CHECKS > 1 - if (crc == 0) - { - crc = crc32(0, NULL, 0); - crc = crc32(crc, profile, length); - } - - /* So this check must pass for the 'return' below to happen. - */ - if (crc == png_sRGB_checks[i].crc) -# endif - { - if (png_sRGB_checks[i].is_broken != 0) - { - /* These profiles are known to have bad data that may cause - * problems if they are used, therefore attempt to - * discourage their use, skip the 'have_md5' warning below, - * which is made irrelevant by this error. - */ - png_chunk_report(png_ptr, "known incorrect sRGB profile", - PNG_CHUNK_ERROR); - } - - /* Warn that this being done; this isn't even an error since - * the profile is perfectly valid, but it would be nice if - * people used the up-to-date ones. - */ - else if (png_sRGB_checks[i].have_md5 == 0) - { - png_chunk_report(png_ptr, - "out-of-date sRGB profile with no signature", - PNG_CHUNK_WARNING); - } - - return 1+png_sRGB_checks[i].is_broken; - } - } - -# if PNG_sRGB_PROFILE_CHECKS > 0 - /* The signature matched, but the profile had been changed in some - * way. This probably indicates a data error or uninformed hacking. - * Fall through to "no match". - */ - png_chunk_report(png_ptr, - "Not recognizing known sRGB profile that has been edited", - PNG_CHUNK_WARNING); - break; -# endif - } - } - } - - return 0; /* no match */ -} - -void /* PRIVATE */ -png_icc_set_sRGB(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_bytep profile, uLong adler) -{ - /* Is this profile one of the known ICC sRGB profiles? If it is, just set - * the sRGB information. - */ - if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler) != 0) - (void)png_colorspace_set_sRGB(png_ptr, colorspace, - (int)/*already checked*/png_get_uint_32(profile+64)); -} -#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */ -#endif /* sRGB */ - -int /* PRIVATE */ -png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_charp name, png_uint_32 profile_length, png_const_bytep profile, - int color_type) -{ - if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0) - return 0; - - if (icc_check_length(png_ptr, colorspace, name, profile_length) != 0 && - png_icc_check_header(png_ptr, colorspace, name, profile_length, profile, - color_type) != 0 && - png_icc_check_tag_table(png_ptr, colorspace, name, profile_length, - profile) != 0) - { -# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0 - /* If no sRGB support, don't try storing sRGB information */ - png_icc_set_sRGB(png_ptr, colorspace, profile, 0); -# endif - return 1; - } - - /* Failure case */ - return 0; -} -#endif /* iCCP */ +#endif /* READ_iCCP */ #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -void /* PRIVATE */ -png_colorspace_set_rgb_coefficients(png_structrp png_ptr) +#if (defined PNG_READ_mDCV_SUPPORTED) || (defined PNG_READ_cHRM_SUPPORTED) +static int +have_chromaticities(png_const_structrp png_ptr) { - /* Set the rgb_to_gray coefficients from the colorspace. */ - if (png_ptr->rgb_to_gray_coefficients_set == 0 && - (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + /* Handle new PNGv3 chunks and the precedence rules to determine whether + * png_struct::chromaticities must be processed. Only required for RGB to + * gray. + * + * mDCV: this is the mastering colour space and it is independent of the + * encoding so it needs to be used regardless of the encoded space. + * + * cICP: first in priority but not yet implemented - the chromaticities come + * from the 'primaries'. + * + * iCCP: not supported by libpng (so ignored) + * + * sRGB: the defaults match sRGB + * + * cHRM: calculate the coefficients + */ +# ifdef PNG_READ_mDCV_SUPPORTED + if (png_has_chunk(png_ptr, mDCV)) + return 1; +# define check_chromaticities 1 +# endif /*mDCV*/ + +# ifdef PNG_READ_sRGB_SUPPORTED + if (png_has_chunk(png_ptr, sRGB)) + return 0; +# endif /*sRGB*/ + +# ifdef PNG_READ_cHRM_SUPPORTED + if (png_has_chunk(png_ptr, cHRM)) + return 1; +# define check_chromaticities 1 +# endif /*cHRM*/ + + return 0; /* sRGB defaults */ +} +#endif /* READ_mDCV || READ_cHRM */ + +void /* PRIVATE */ +png_set_rgb_coefficients(png_structrp png_ptr) +{ + /* Set the rgb_to_gray coefficients from the colorspace if available. Note + * that '_set' means that png_rgb_to_gray was called **and** it successfully + * set up the coefficients. + */ + if (png_ptr->rgb_to_gray_coefficients_set == 0) { - /* png_set_background has not been called, get the coefficients from the Y - * values of the colorspace colorants. - */ - png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y; - png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y; - png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y; - png_fixed_point total = r+g+b; +# if check_chromaticities + png_XYZ xyz; - if (total > 0 && - r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && - g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && - b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 && - r+g+b <= 32769) + if (have_chromaticities(png_ptr) && + png_XYZ_from_xy(&xyz, &png_ptr->chromaticities) == 0) { - /* We allow 0 coefficients here. r+g+b may be 32769 if two or - * all of the coefficients were rounded up. Handle this by - * reducing the *largest* coefficient by 1; this matches the - * approach used for the default coefficients in pngrtran.c + /* png_set_rgb_to_gray has not set the coefficients, get them from the + * Y * values of the colorspace colorants. */ - int add = 0; + png_fixed_point r = xyz.red_Y; + png_fixed_point g = xyz.green_Y; + png_fixed_point b = xyz.blue_Y; + png_fixed_point total = r+g+b; - if (r+g+b > 32768) - add = -1; - else if (r+g+b < 32768) - add = 1; - - if (add != 0) + if (total > 0 && + r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && + g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && + b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 && + r+g+b <= 32769) { - if (g >= r && g >= b) - g += add; - else if (r >= g && r >= b) - r += add; + /* We allow 0 coefficients here. r+g+b may be 32769 if two or + * all of the coefficients were rounded up. Handle this by + * reducing the *largest* coefficient by 1; this matches the + * approach used for the default coefficients in pngrtran.c + */ + int add = 0; + + if (r+g+b > 32768) + add = -1; + else if (r+g+b < 32768) + add = 1; + + if (add != 0) + { + if (g >= r && g >= b) + g += add; + else if (r >= g && r >= b) + r += add; + else + b += add; + } + + /* Check for an internal error. */ + if (r+g+b != 32768) + png_error(png_ptr, + "internal error handling cHRM coefficients"); + else - b += add; - } - - /* Check for an internal error. */ - if (r+g+b != 32768) - png_error(png_ptr, - "internal error handling cHRM coefficients"); - - else - { - png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r; - png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g; + { + png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r; + png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g; + } } } - - /* This is a png_error at present even though it could be ignored - - * it should never happen, but it is important that if it does, the - * bug is fixed. - */ else - png_error(png_ptr, "internal error handling cHRM->XYZ"); +# endif /* check_chromaticities */ + { + /* Use the historical REC 709 (etc) values: */ + png_ptr->rgb_to_gray_red_coeff = 6968; + png_ptr->rgb_to_gray_green_coeff = 23434; + /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ + } } } #endif /* READ_RGB_TO_GRAY */ -#endif /* COLORSPACE */ - void /* PRIVATE */ png_check_IHDR(png_const_structrp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, @@ -3390,7 +2735,27 @@ png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text) } #endif -#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_COLORSPACE_SUPPORTED) ||\ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_cLLI_SUPPORTED) || defined(PNG_mDCV_SUPPORTED)) +png_uint_32 +png_fixed_ITU(png_const_structrp png_ptr, double fp, png_const_charp text) +{ + double r = floor(10000 * fp + .5); + + if (r > 2147483647. || r < 0) + png_fixed_error(png_ptr, text); + +# ifndef PNG_ERROR_TEXT_SUPPORTED + PNG_UNUSED(text) +# endif + + return (png_uint_32)r; +} +#endif + + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_COLORSPACE_SUPPORTED) ||\ defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) /* muldiv functions */ /* This API takes signed arguments and rounds the result to the nearest @@ -3398,7 +2763,7 @@ png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text) * the nearest .00001). Overflow and divide by zero are signalled in * the result, a boolean - true on success, false on overflow. */ -int +int /* PRIVATE */ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, png_int_32 divisor) { @@ -3512,27 +2877,7 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, return 0; } -#endif /* READ_GAMMA || INCH_CONVERSIONS */ -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) -/* The following is for when the caller doesn't much care about the - * result. - */ -png_fixed_point -png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times, - png_int_32 divisor) -{ - png_fixed_point result; - - if (png_muldiv(&result, a, times, divisor) != 0) - return result; - - png_warning(png_ptr, "fixed point overflow ignored"); - return 0; -} -#endif - -#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */ /* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ png_fixed_point png_reciprocal(png_fixed_point a) @@ -3551,26 +2896,38 @@ png_reciprocal(png_fixed_point a) return 0; /* error/overflow */ } +#endif /* READ_GAMMA || COLORSPACE || INCH_CONVERSIONS || READ_pHYS */ +#ifdef PNG_READ_GAMMA_SUPPORTED /* This is the shared test on whether a gamma value is 'significant' - whether * it is worth doing gamma correction. */ int /* PRIVATE */ png_gamma_significant(png_fixed_point gamma_val) { + /* sRGB: 1/2.2 == 0.4545(45) + * AdobeRGB: 1/(2+51/256) ~= 0.45471 5dp + * + * So the correction from AdobeRGB to sRGB (output) is: + * + * 2.2/(2+51/256) == 1.00035524 + * + * I.e. vanishly small (<4E-4) but still detectable in 16-bit linear (+/- + * 23). Note that the Adobe choice seems to be something intended to give an + * exact number with 8 binary fractional digits - it is the closest to 2.2 + * that is possible a base 2 .8p representation. + */ return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED || gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED; } -#endif -#ifdef PNG_READ_GAMMA_SUPPORTED -#ifdef PNG_16BIT_SUPPORTED +#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED /* A local convenience routine. */ static png_fixed_point png_product2(png_fixed_point a, png_fixed_point b) { - /* The required result is 1/a * 1/b; the following preserves accuracy. */ -#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + /* The required result is a * b; the following preserves accuracy. */ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED /* Should now be unused */ double r = a * 1E-5; r *= b; r = floor(r+.5); @@ -3586,9 +2943,8 @@ png_product2(png_fixed_point a, png_fixed_point b) return 0; /* overflow */ } -#endif /* 16BIT */ +#endif /* FLOATING_ARITHMETIC */ -/* The inverse of the above. */ png_fixed_point png_reciprocal2(png_fixed_point a, png_fixed_point b) { @@ -4241,10 +3597,27 @@ png_destroy_gamma_table(png_structrp png_ptr) * tables, we don't make a full table if we are reducing to 8-bit in * the future. Note also how the gamma_16 tables are segmented so that * we don't need to allocate > 64K chunks for a full 16-bit table. + * + * TODO: move this to pngrtran.c and make it static. Better yet create + * pngcolor.c and put all the PNG_COLORSPACE stuff in there. */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +# define GAMMA_TRANSFORMS 1 /* #ifdef CSE */ +#else +# define GAMMA_TRANSFORMS 0 +#endif + void /* PRIVATE */ png_build_gamma_table(png_structrp png_ptr, int bit_depth) { + png_fixed_point file_gamma, screen_gamma; + png_fixed_point correction; +# if GAMMA_TRANSFORMS + png_fixed_point file_to_linear, linear_to_screen; +# endif + png_debug(1, "in png_build_gamma_table"); /* Remove any existing table; this copes with multiple calls to @@ -4259,27 +3632,44 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) png_destroy_gamma_table(png_ptr); } + /* The following fields are set, finally, in png_init_read_transformations. + * If file_gamma is 0 (unset) nothing can be done otherwise if screen_gamma + * is 0 (unset) there is no gamma correction but to/from linear is possible. + */ + file_gamma = png_ptr->file_gamma; + screen_gamma = png_ptr->screen_gamma; +# if GAMMA_TRANSFORMS + file_to_linear = png_reciprocal(file_gamma); +# endif + + if (screen_gamma > 0) + { +# if GAMMA_TRANSFORMS + linear_to_screen = png_reciprocal(screen_gamma); +# endif + correction = png_reciprocal2(screen_gamma, file_gamma); + } + else /* screen gamma unknown */ + { +# if GAMMA_TRANSFORMS + linear_to_screen = file_gamma; +# endif + correction = PNG_FP_1; + } + if (bit_depth <= 8) { - png_build_8bit_table(png_ptr, &png_ptr->gamma_table, - png_ptr->screen_gamma > 0 ? - png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma) : PNG_FP_1); + png_build_8bit_table(png_ptr, &png_ptr->gamma_table, correction); -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#if GAMMA_TRANSFORMS if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0) { - png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, - png_reciprocal(png_ptr->colorspace.gamma)); + png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, file_to_linear); png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1, - png_ptr->screen_gamma > 0 ? - png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + linear_to_screen); } -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +#endif /* GAMMA_TRANSFORMS */ } #ifdef PNG_16BIT_SUPPORTED else @@ -4345,32 +3735,26 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) * reduced to 8 bits. */ if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0) - png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift, - png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma) : PNG_FP_1); - + png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift, + png_reciprocal(correction)); else - png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift, - png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, - png_ptr->screen_gamma) : PNG_FP_1); + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift, + correction); -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +# if GAMMA_TRANSFORMS if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0) { png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift, - png_reciprocal(png_ptr->colorspace.gamma)); + file_to_linear); /* Notice that the '16 from 1' table should be full precision, however * the lookup on this table still uses gamma_shift, so it can't be. * TODO: fix this. */ png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift, - png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : - png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + linear_to_screen); } -#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +#endif /* GAMMA_TRANSFORMS */ } #endif /* 16BIT */ } @@ -4585,7 +3969,7 @@ png_image_free_function(png_voidp argument) # ifdef PNG_STDIO_SUPPORTED if (cp->owned_file != 0) { - FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr); + FILE *fp = png_voidcast(FILE *, cp->png_ptr->io_ptr); cp->owned_file = 0; /* Ignore errors here. */ diff --git a/3rdparty/libpng/png.h b/3rdparty/libpng/png.h index d25fbe9ed3..bdcd243dea 100644 --- a/3rdparty/libpng/png.h +++ b/3rdparty/libpng/png.h @@ -1,6 +1,6 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.45 + * libpng version 1.6.53 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson @@ -14,7 +14,7 @@ * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger * libpng versions 0.97, January 1998, through 1.6.35, July 2018: * Glenn Randers-Pehrson - * libpng versions 1.6.36, December 2018, through 1.6.45, January 2025: + * libpng versions 1.6.36, December 2018, through 1.6.53, December 2025: * Cosmin Truta * See also "Contributing Authors", below. */ @@ -238,7 +238,7 @@ * ... * 1.5.30 15 10530 15.so.15.30[.0] * ... - * 1.6.45 16 10645 16.so.16.45[.0] + * 1.6.53 16 10651 16.so.16.53[.0] * * Henceforth the source version will match the shared-library major and * minor numbers; the shared-library major version number will be used for @@ -274,7 +274,7 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.6.45" +#define PNG_LIBPNG_VER_STRING "1.6.53" #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n" /* The versions of shared library builds should stay in sync, going forward */ @@ -285,7 +285,7 @@ /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ #define PNG_LIBPNG_VER_MAJOR 1 #define PNG_LIBPNG_VER_MINOR 6 -#define PNG_LIBPNG_VER_RELEASE 45 +#define PNG_LIBPNG_VER_RELEASE 53 /* This should be zero for a public release, or non-zero for a * development version. @@ -316,7 +316,7 @@ * From version 1.0.1 it is: * XXYYZZ, where XX=major, YY=minor, ZZ=release */ -#define PNG_LIBPNG_VER 10645 /* 1.6.45 */ +#define PNG_LIBPNG_VER 10653 /* 1.6.53 */ /* Library configuration: these options cannot be changed after * the library has been built. @@ -426,7 +426,7 @@ extern "C" { /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ -typedef char* png_libpng_version_1_6_45; +typedef char* png_libpng_version_1_6_53; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * @@ -744,7 +744,21 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; #define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ #define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ #define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ -#define PNG_INFO_cICP 0x20000U +#define PNG_INFO_cICP 0x20000U /* PNGv3: 1.6.45 */ +#define PNG_INFO_cLLI 0x40000U /* PNGv3: 1.6.45 */ +#define PNG_INFO_mDCV 0x80000U /* PNGv3: 1.6.45 */ +/* APNG: these chunks are stored as unknown, these flags are never set + * however they are provided as a convenience for implementors of APNG and + * avoids any merge conflicts. + * + * Private chunks: these chunk names violate the chunk name recommendations + * because the chunk definitions have no signature and because the private + * chunks with these names have been reserved. Private definitions should + * avoid them. + */ +#define PNG_INFO_acTL 0x100000U /* PNGv3: 1.6.45: unknown */ +#define PNG_INFO_fcTL 0x200000U /* PNGv3: 1.6.45: unknown */ +#define PNG_INFO_fdAT 0x400000U /* PNGv3: 1.6.45: unknown */ /* This is used for the transformation routines, as some of them * change these values for the row. It also should enable using @@ -1556,7 +1570,7 @@ PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, #ifdef PNG_STDIO_SUPPORTED /* Initialize the input/output for the PNG file to the default functions. */ -PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, FILE *fp)); #endif /* Replace the (error and abort), and warning functions with user @@ -1976,15 +1990,44 @@ PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, #ifdef PNG_cICP_SUPPORTED PNG_EXPORT(250, png_uint_32, png_get_cICP, (png_const_structrp png_ptr, - png_inforp info_ptr, png_bytep colour_primaries, + png_const_inforp info_ptr, png_bytep colour_primaries, png_bytep transfer_function, png_bytep matrix_coefficients, png_bytep video_full_range_flag)); +#endif + +#ifdef PNG_cICP_SUPPORTED PNG_EXPORT(251, void, png_set_cICP, (png_const_structrp png_ptr, png_inforp info_ptr, png_byte colour_primaries, png_byte transfer_function, png_byte matrix_coefficients, png_byte video_full_range_flag)); #endif +#ifdef PNG_cLLI_SUPPORTED +PNG_FP_EXPORT(252, png_uint_32, png_get_cLLI, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *maximum_content_light_level, + double *maximum_frame_average_light_level)) +PNG_FIXED_EXPORT(253, png_uint_32, png_get_cLLI_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + /* The values below are in cd/m2 (nits) and are scaled by 10,000; not + * 100,000 as in the case of png_fixed_point. + */ + png_uint_32p maximum_content_light_level_scaled_by_10000, + png_uint_32p maximum_frame_average_light_level_scaled_by_10000)) +#endif + +#ifdef PNG_cLLI_SUPPORTED +PNG_FP_EXPORT(254, void, png_set_cLLI, (png_const_structrp png_ptr, + png_inforp info_ptr, double maximum_content_light_level, + double maximum_frame_average_light_level)) +PNG_FIXED_EXPORT(255, void, png_set_cLLI_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, + /* The values below are in cd/m2 (nits) and are scaled by 10,000; not + * 100,000 as in the case of png_fixed_point. + */ + png_uint_32 maximum_content_light_level_scaled_by_10000, + png_uint_32 maximum_frame_average_light_level_scaled_by_10000)) +#endif + #ifdef PNG_eXIf_SUPPORTED PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *exif)); @@ -2029,6 +2072,60 @@ PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, int color_type, int interlace_method, int compression_method, int filter_method)); +#ifdef PNG_mDCV_SUPPORTED +PNG_FP_EXPORT(256, png_uint_32, png_get_mDCV, (png_const_structrp png_ptr, + png_const_inforp info_ptr, + /* The chromaticities of the mastering display. As cHRM, but independent of + * the encoding endpoints in cHRM, or cICP, or iCCP. These values will + * always be in the range 0 to 1.3107. + */ + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y, + /* Mastering display luminance in cd/m2 (nits). */ + double *mastering_display_maximum_luminance, + double *mastering_display_minimum_luminance)) + +PNG_FIXED_EXPORT(257, png_uint_32, png_get_mDCV_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y, + /* Mastering display luminance in cd/m2 (nits) multiplied (scaled) by + * 10,000. + */ + png_uint_32p mastering_display_maximum_luminance_scaled_by_10000, + png_uint_32p mastering_display_minimum_luminance_scaled_by_10000)) +#endif + +#ifdef PNG_mDCV_SUPPORTED +PNG_FP_EXPORT(258, void, png_set_mDCV, (png_const_structrp png_ptr, + png_inforp info_ptr, + /* The chromaticities of the mastering display. As cHRM, but independent of + * the encoding endpoints in cHRM, or cICP, or iCCP. + */ + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y, + /* Mastering display luminance in cd/m2 (nits). */ + double mastering_display_maximum_luminance, + double mastering_display_minimum_luminance)) + +PNG_FIXED_EXPORT(259, void, png_set_mDCV_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, + /* The admissible range of these values is not the full range of a PNG + * fixed point value. Negative values cannot be encoded and the maximum + * value is about 1.3 */ + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, + png_fixed_point int_green_x, png_fixed_point int_green_y, + png_fixed_point int_blue_x, png_fixed_point int_blue_y, + /* These are PNG unsigned 4 byte values: 31-bit unsigned values. The MSB + * must be zero. + */ + png_uint_32 mastering_display_maximum_luminance_scaled_by_10000, + png_uint_32 mastering_display_minimum_luminance_scaled_by_10000)) +#endif + #ifdef PNG_oFFs_SUPPORTED PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, @@ -2991,7 +3088,7 @@ PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, */ PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, - FILE* file)); + FILE *file)); /* The PNG header is read from the stdio FILE object. */ #endif /* STDIO */ @@ -3066,7 +3163,7 @@ PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, int convert_to_8_bit, const void *buffer, png_int_32 row_stride, const void *colormap)); - /* Write the image to the given (FILE*). */ + /* Write the image to the given FILE object. */ #endif /* SIMPLIFIED_WRITE_STDIO */ /* With all write APIs if image is in one of the linear formats with 16-bit @@ -3206,26 +3303,45 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, * selected at run time. */ #ifdef PNG_SET_OPTION_SUPPORTED + +/* HARDWARE: ARM Neon SIMD instructions supported */ #ifdef PNG_ARM_NEON_API_SUPPORTED -# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ -#endif -#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ -#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ -#ifdef PNG_MIPS_MSA_API_SUPPORTED -# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ -#endif -#ifdef PNG_DISABLE_ADLER32_CHECK_SUPPORTED -# define PNG_IGNORE_ADLER32 8 /* SOFTWARE: disable Adler32 check on IDAT */ -#endif -#ifdef PNG_POWERPC_VSX_API_SUPPORTED -# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions - * supported */ -#endif -#ifdef PNG_MIPS_MMI_API_SUPPORTED -# define PNG_MIPS_MMI 12 /* HARDWARE: MIPS MMI SIMD instructions supported */ +# define PNG_ARM_NEON 0 #endif -#define PNG_OPTION_NEXT 14 /* Next option - numbers must be even */ +/* SOFTWARE: Force maximum window */ +#define PNG_MAXIMUM_INFLATE_WINDOW 2 + +/* SOFTWARE: Check ICC profile for sRGB */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 + +/* HARDWARE: MIPS MSA SIMD instructions supported */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 +#endif + +/* SOFTWARE: Disable Adler32 check on IDAT */ +#ifdef PNG_DISABLE_ADLER32_CHECK_SUPPORTED +# define PNG_IGNORE_ADLER32 8 +#endif + +/* HARDWARE: PowerPC VSX SIMD instructions supported */ +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 +#endif + +/* HARDWARE: MIPS MMI SIMD instructions supported */ +#ifdef PNG_MIPS_MMI_API_SUPPORTED +# define PNG_MIPS_MMI 12 +#endif + +/* HARDWARE: RISC-V RVV SIMD instructions supported */ +#ifdef PNG_RISCV_RVV_API_SUPPORTED +# define PNG_RISCV_RVV 14 +#endif + +/* Next option - numbers must be even */ +#define PNG_OPTION_NEXT 16 /* Return values: NOTE: there are four values and 'off' is *not* zero */ #define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ @@ -3249,7 +3365,7 @@ PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, * one to use is one more than this.) */ #ifdef PNG_EXPORT_LAST_ORDINAL - PNG_EXPORT_LAST_ORDINAL(251); + PNG_EXPORT_LAST_ORDINAL(259); #endif #ifdef __cplusplus diff --git a/3rdparty/libpng/pngconf.h b/3rdparty/libpng/pngconf.h index 11a40b8d81..f4ff19209c 100644 --- a/3rdparty/libpng/pngconf.h +++ b/3rdparty/libpng/pngconf.h @@ -1,6 +1,6 @@ /* pngconf.h - machine-configurable file for libpng * - * libpng version 1.6.45 + * libpng version 1.6.53 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson @@ -219,25 +219,13 @@ /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ # if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) -# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# error PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed # endif -# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ - (defined(__BORLANDC__) && __BORLANDC__ < 0x500) - /* older Borland and MSC - * compilers used '__export' and required this to be after - * the type. - */ -# ifndef PNG_EXPORT_TYPE -# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP -# endif -# define PNG_DLL_EXPORT __export -# else /* newer compiler */ -# define PNG_DLL_EXPORT __declspec(dllexport) -# ifndef PNG_DLL_IMPORT -# define PNG_DLL_IMPORT __declspec(dllimport) -# endif -# endif /* compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif #else /* !Windows */ # if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) @@ -479,7 +467,7 @@ #if CHAR_BIT == 8 && UCHAR_MAX == 255 typedef unsigned char png_byte; #else -# error "libpng requires 8-bit bytes" +# error libpng requires 8-bit bytes #endif #if INT_MIN == -32768 && INT_MAX == 32767 @@ -487,7 +475,7 @@ #elif SHRT_MIN == -32768 && SHRT_MAX == 32767 typedef short png_int_16; #else -# error "libpng requires a signed 16-bit type" +# error libpng requires a signed 16-bit integer type #endif #if UINT_MAX == 65535 @@ -495,7 +483,7 @@ #elif USHRT_MAX == 65535 typedef unsigned short png_uint_16; #else -# error "libpng requires an unsigned 16-bit type" +# error libpng requires an unsigned 16-bit integer type #endif #if INT_MIN < -2147483646 && INT_MAX > 2147483646 @@ -503,7 +491,7 @@ #elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 typedef long int png_int_32; #else -# error "libpng requires a signed 32-bit (or more) type" +# error libpng requires a signed 32-bit (or longer) integer type #endif #if UINT_MAX > 4294967294U @@ -511,7 +499,7 @@ #elif ULONG_MAX > 4294967294U typedef unsigned long int png_uint_32; #else -# error "libpng requires an unsigned 32-bit (or more) type" +# error libpng requires an unsigned 32-bit (or longer) integer type #endif /* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. @@ -592,10 +580,6 @@ typedef const png_fixed_point * png_const_fixed_point_p; typedef size_t * png_size_tp; typedef const size_t * png_const_size_tp; -#ifdef PNG_STDIO_SUPPORTED -typedef FILE * png_FILE_p; -#endif - #ifdef PNG_FLOATING_POINT_SUPPORTED typedef double * png_doublep; typedef const double * png_const_doublep; @@ -617,6 +601,15 @@ typedef double * * png_doublepp; /* Pointers to pointers to pointers; i.e., pointer to array */ typedef char * * * png_charppp; +#ifdef PNG_STDIO_SUPPORTED +/* With PNG_STDIO_SUPPORTED it was possible to use I/O streams that were + * not necessarily stdio FILE streams, to allow building Windows applications + * before Win32 and Windows CE applications before WinCE 3.0, but that kind + * of support has long been discontinued. + */ +typedef FILE * png_FILE_p; /* [Deprecated] */ +#endif + #endif /* PNG_BUILDING_SYMBOL_TABLE */ #endif /* PNGCONF_H */ diff --git a/3rdparty/libpng/pngdebug.h b/3rdparty/libpng/pngdebug.h index ab9ea632d9..0337918aec 100644 --- a/3rdparty/libpng/pngdebug.h +++ b/3rdparty/libpng/pngdebug.h @@ -1,6 +1,6 @@ -/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c +/* pngdebug.h - internal debugging macros for libpng * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -10,6 +10,10 @@ * and license in png.h */ +#ifndef PNGPRIV_H +# error This file must not be included by applications; please include +#endif + /* Define PNG_DEBUG at compile time for debugging information. Higher * numbers for PNG_DEBUG mean more debugging information. This has * only been added since version 0.95 so it is not implemented throughout @@ -34,9 +38,6 @@ #define PNGDEBUG_H /* These settings control the formatting of messages in png.c and pngerror.c */ /* Moved to pngdebug.h at 1.5.0 */ -# ifndef PNG_LITERAL_SHARP -# define PNG_LITERAL_SHARP 0x23 -# endif # ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET # define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b # endif diff --git a/3rdparty/libpng/pngerror.c b/3rdparty/libpng/pngerror.c index aa0ae58e15..044fa2eb68 100644 --- a/3rdparty/libpng/pngerror.c +++ b/3rdparty/libpng/pngerror.c @@ -1,6 +1,6 @@ /* pngerror.c - stub functions for i/o and memory allocation * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -39,46 +39,6 @@ PNG_FUNCTION(void,PNGAPI png_error,(png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN) { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - char msg[16]; - if (png_ptr != NULL) - { - if ((png_ptr->flags & - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) - { - if (*error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - for (offset = 1; offset<15; offset++) - if (error_message[offset] == ' ') - break; - - if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) - { - int i; - for (i = 0; i < offset - 1; i++) - msg[i] = error_message[i + 1]; - msg[i - 1] = '\0'; - error_message = msg; - } - - else - error_message += offset; - } - - else - { - if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) - { - msg[0] = '0'; - msg[1] = '\0'; - error_message = msg; - } - } - } - } -#endif if (png_ptr != NULL && png_ptr->error_fn != NULL) (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), error_message); @@ -216,21 +176,6 @@ void PNGAPI png_warning(png_const_structrp png_ptr, png_const_charp warning_message) { int offset = 0; - if (png_ptr != NULL) - { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - if ((png_ptr->flags & - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) -#endif - { - if (*warning_message == PNG_LITERAL_SHARP) - { - for (offset = 1; offset < 15; offset++) - if (warning_message[offset] == ' ') - break; - } - } - } if (png_ptr != NULL && png_ptr->warning_fn != NULL) (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr), warning_message + offset); @@ -712,42 +657,9 @@ png_default_error,(png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN) { #ifdef PNG_CONSOLE_IO_SUPPORTED -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - /* Check on NULL only added in 1.5.4 */ - if (error_message != NULL && *error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - char error_number[16]; - for (offset = 0; offset<15; offset++) - { - error_number[offset] = error_message[offset + 1]; - if (error_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - error_number[offset - 1] = '\0'; - fprintf(stderr, "libpng error no. %s: %s", - error_number, error_message + offset + 1); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng error: %s, offset=%d", - error_message, offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -#endif - { - fprintf(stderr, "libpng error: %s", error_message ? error_message : - "undefined"); - fprintf(stderr, PNG_STRING_NEWLINE); - } + fprintf(stderr, "libpng error: %s", error_message ? error_message : + "undefined"); + fprintf(stderr, PNG_STRING_NEWLINE); #else PNG_UNUSED(error_message) /* Make compiler happy */ #endif @@ -785,40 +697,8 @@ static void /* PRIVATE */ png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message) { #ifdef PNG_CONSOLE_IO_SUPPORTED -# ifdef PNG_ERROR_NUMBERS_SUPPORTED - if (*warning_message == PNG_LITERAL_SHARP) - { - int offset; - char warning_number[16]; - for (offset = 0; offset < 15; offset++) - { - warning_number[offset] = warning_message[offset + 1]; - if (warning_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - warning_number[offset + 1] = '\0'; - fprintf(stderr, "libpng warning no. %s: %s", - warning_number, warning_message + offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng warning: %s", - warning_message); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -# endif - - { - fprintf(stderr, "libpng warning: %s", warning_message); - fprintf(stderr, PNG_STRING_NEWLINE); - } + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); #else PNG_UNUSED(warning_message) /* Make compiler happy */ #endif @@ -866,12 +746,8 @@ png_get_error_ptr(png_const_structrp png_ptr) void PNGAPI png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) { - if (png_ptr != NULL) - { - png_ptr->flags &= - ((~(PNG_FLAG_STRIP_ERROR_NUMBERS | - PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); - } + PNG_UNUSED(png_ptr) + PNG_UNUSED(strip_mode) } #endif @@ -935,23 +811,37 @@ png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message) int /* PRIVATE */ png_safe_execute(png_imagep image, int (*function)(png_voidp), png_voidp arg) { - png_voidp saved_error_buf = image->opaque->error_buf; + const png_voidp saved_error_buf = image->opaque->error_buf; jmp_buf safe_jmpbuf; - int result; /* Safely execute function(arg), with png_error returning back here. */ if (setjmp(safe_jmpbuf) == 0) { + int result; + image->opaque->error_buf = safe_jmpbuf; result = function(arg); image->opaque->error_buf = saved_error_buf; - return result; + + if (result) + return 1; /* success */ } - /* On png_error, return via longjmp, pop the jmpbuf, and free the image. */ + /* The function failed either because of a caught png_error and a regular + * return of false above or because of an uncaught png_error from the + * function itself. Ensure that the error_buf is always set back to the + * value saved above: + */ image->opaque->error_buf = saved_error_buf; - png_image_free(image); - return 0; + + /* On the final false return, when about to return control to the caller, the + * image is freed (png_image_free does this check but it is duplicated here + * for clarity: + */ + if (saved_error_buf == NULL) + png_image_free(image); + + return 0; /* failure */ } #endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */ #endif /* READ || WRITE */ diff --git a/3rdparty/libpng/pngget.c b/3rdparty/libpng/pngget.c index 9be8814322..1ebb1144ff 100644 --- a/3rdparty/libpng/pngget.c +++ b/3rdparty/libpng/pngget.c @@ -1,6 +1,6 @@ /* pngget.c - retrieval of values from info struct * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -380,7 +380,13 @@ png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns) * Notice that this can overflow - a warning is output and 0 is * returned. */ - return png_muldiv_warn(png_ptr, microns, 500, 127); + png_fixed_point result; + + if (png_muldiv(&result, microns, 500, 127) != 0) + return result; + + png_warning(png_ptr, "fixed point overflow ignored"); + return 0; } png_fixed_point PNGAPI @@ -390,7 +396,7 @@ png_get_x_offset_inches_fixed(png_const_structrp png_ptr, return png_fixed_inches_from_microns(png_ptr, png_get_x_offset_microns(png_ptr, info_ptr)); } -#endif +#endif /* FIXED_POINT */ #ifdef PNG_FIXED_POINT_SUPPORTED png_fixed_point PNGAPI @@ -518,44 +524,31 @@ png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, # ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr, - double *white_x, double *white_y, double *red_x, double *red_y, - double *green_x, double *green_y, double *blue_x, double *blue_y) + double *whitex, double *whitey, double *redx, double *redy, + double *greenx, double *greeny, double *bluex, double *bluey) { png_debug1(1, "in %s retrieval function", "cHRM"); - /* Quiet API change: this code used to only return the end points if a cHRM - * chunk was present, but the end points can also come from iCCP or sRGB - * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and - * the png_set_ APIs merely check that set end points are mutually - * consistent. - */ + /* PNGv3: this just returns the values store from the cHRM, if any. */ if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + (info_ptr->valid & PNG_INFO_cHRM) != 0) { - if (white_x != NULL) - *white_x = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.whitex, "cHRM white X"); - if (white_y != NULL) - *white_y = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y"); - if (red_x != NULL) - *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx, - "cHRM red X"); - if (red_y != NULL) - *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy, - "cHRM red Y"); - if (green_x != NULL) - *green_x = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.greenx, "cHRM green X"); - if (green_y != NULL) - *green_y = png_float(png_ptr, - info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y"); - if (blue_x != NULL) - *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex, - "cHRM blue X"); - if (blue_y != NULL) - *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey, - "cHRM blue Y"); + if (whitex != NULL) + *whitex = png_float(png_ptr, info_ptr->cHRM.whitex, "cHRM wx"); + if (whitey != NULL) + *whitey = png_float(png_ptr, info_ptr->cHRM.whitey, "cHRM wy"); + if (redx != NULL) + *redx = png_float(png_ptr, info_ptr->cHRM.redx, "cHRM rx"); + if (redy != NULL) + *redy = png_float(png_ptr, info_ptr->cHRM.redy, "cHRM ry"); + if (greenx != NULL) + *greenx = png_float(png_ptr, info_ptr->cHRM.greenx, "cHRM gx"); + if (greeny != NULL) + *greeny = png_float(png_ptr, info_ptr->cHRM.greeny, "cHRM gy"); + if (bluex != NULL) + *bluex = png_float(png_ptr, info_ptr->cHRM.bluex, "cHRM bx"); + if (bluey != NULL) + *bluey = png_float(png_ptr, info_ptr->cHRM.bluey, "cHRM by"); return PNG_INFO_cHRM; } @@ -568,38 +561,31 @@ png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr, double *green_Y, double *green_Z, double *blue_X, double *blue_Y, double *blue_Z) { + png_XYZ XYZ; png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + (info_ptr->valid & PNG_INFO_cHRM) != 0 && + png_XYZ_from_xy(&XYZ, &info_ptr->cHRM) == 0) { if (red_X != NULL) - *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X, - "cHRM red X"); + *red_X = png_float(png_ptr, XYZ.red_X, "cHRM red X"); if (red_Y != NULL) - *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y, - "cHRM red Y"); + *red_Y = png_float(png_ptr, XYZ.red_Y, "cHRM red Y"); if (red_Z != NULL) - *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z, - "cHRM red Z"); + *red_Z = png_float(png_ptr, XYZ.red_Z, "cHRM red Z"); if (green_X != NULL) - *green_X = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X"); + *green_X = png_float(png_ptr, XYZ.green_X, "cHRM green X"); if (green_Y != NULL) - *green_Y = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y"); + *green_Y = png_float(png_ptr, XYZ.green_Y, "cHRM green Y"); if (green_Z != NULL) - *green_Z = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z"); + *green_Z = png_float(png_ptr, XYZ.green_Z, "cHRM green Z"); if (blue_X != NULL) - *blue_X = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X"); + *blue_X = png_float(png_ptr, XYZ.blue_X, "cHRM blue X"); if (blue_Y != NULL) - *blue_Y = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y"); + *blue_Y = png_float(png_ptr, XYZ.blue_Y, "cHRM blue Y"); if (blue_Z != NULL) - *blue_Z = png_float(png_ptr, - info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z"); + *blue_Z = png_float(png_ptr, XYZ.blue_Z, "cHRM blue Z"); return PNG_INFO_cHRM; } @@ -616,29 +602,22 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, png_fixed_point *int_blue_Z) { + png_XYZ XYZ; png_debug1(1, "in %s retrieval function", "cHRM_XYZ"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + (info_ptr->valid & PNG_INFO_cHRM) != 0U && + png_XYZ_from_xy(&XYZ, &info_ptr->cHRM) == 0) { - if (int_red_X != NULL) - *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X; - if (int_red_Y != NULL) - *int_red_Y = info_ptr->colorspace.end_points_XYZ.red_Y; - if (int_red_Z != NULL) - *int_red_Z = info_ptr->colorspace.end_points_XYZ.red_Z; - if (int_green_X != NULL) - *int_green_X = info_ptr->colorspace.end_points_XYZ.green_X; - if (int_green_Y != NULL) - *int_green_Y = info_ptr->colorspace.end_points_XYZ.green_Y; - if (int_green_Z != NULL) - *int_green_Z = info_ptr->colorspace.end_points_XYZ.green_Z; - if (int_blue_X != NULL) - *int_blue_X = info_ptr->colorspace.end_points_XYZ.blue_X; - if (int_blue_Y != NULL) - *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y; - if (int_blue_Z != NULL) - *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z; + if (int_red_X != NULL) *int_red_X = XYZ.red_X; + if (int_red_Y != NULL) *int_red_Y = XYZ.red_Y; + if (int_red_Z != NULL) *int_red_Z = XYZ.red_Z; + if (int_green_X != NULL) *int_green_X = XYZ.green_X; + if (int_green_Y != NULL) *int_green_Y = XYZ.green_Y; + if (int_green_Z != NULL) *int_green_Z = XYZ.green_Z; + if (int_blue_X != NULL) *int_blue_X = XYZ.blue_X; + if (int_blue_Y != NULL) *int_blue_Y = XYZ.blue_Y; + if (int_blue_Z != NULL) *int_blue_Z = XYZ.blue_Z; return PNG_INFO_cHRM; } @@ -647,31 +626,24 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 PNGAPI png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, - png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, - png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, - png_fixed_point *blue_x, png_fixed_point *blue_y) + png_fixed_point *whitex, png_fixed_point *whitey, png_fixed_point *redx, + png_fixed_point *redy, png_fixed_point *greenx, png_fixed_point *greeny, + png_fixed_point *bluex, png_fixed_point *bluey) { png_debug1(1, "in %s retrieval function", "cHRM"); + /* PNGv3: this just returns the values store from the cHRM, if any. */ if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + (info_ptr->valid & PNG_INFO_cHRM) != 0) { - if (white_x != NULL) - *white_x = info_ptr->colorspace.end_points_xy.whitex; - if (white_y != NULL) - *white_y = info_ptr->colorspace.end_points_xy.whitey; - if (red_x != NULL) - *red_x = info_ptr->colorspace.end_points_xy.redx; - if (red_y != NULL) - *red_y = info_ptr->colorspace.end_points_xy.redy; - if (green_x != NULL) - *green_x = info_ptr->colorspace.end_points_xy.greenx; - if (green_y != NULL) - *green_y = info_ptr->colorspace.end_points_xy.greeny; - if (blue_x != NULL) - *blue_x = info_ptr->colorspace.end_points_xy.bluex; - if (blue_y != NULL) - *blue_y = info_ptr->colorspace.end_points_xy.bluey; + if (whitex != NULL) *whitex = info_ptr->cHRM.whitex; + if (whitey != NULL) *whitey = info_ptr->cHRM.whitey; + if (redx != NULL) *redx = info_ptr->cHRM.redx; + if (redy != NULL) *redy = info_ptr->cHRM.redy; + if (greenx != NULL) *greenx = info_ptr->cHRM.greenx; + if (greeny != NULL) *greeny = info_ptr->cHRM.greeny; + if (bluex != NULL) *bluex = info_ptr->cHRM.bluex; + if (bluey != NULL) *bluey = info_ptr->cHRM.bluey; return PNG_INFO_cHRM; } @@ -688,11 +660,11 @@ png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "gAMA"); + /* PNGv3 compatibility: only report gAMA if it is really present. */ if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && - file_gamma != NULL) + (info_ptr->valid & PNG_INFO_gAMA) != 0) { - *file_gamma = info_ptr->colorspace.gamma; + if (file_gamma != NULL) *file_gamma = info_ptr->gamma; return PNG_INFO_gAMA; } @@ -707,12 +679,13 @@ png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "gAMA(float)"); + /* PNGv3 compatibility: only report gAMA if it is really present. */ if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && - file_gamma != NULL) + (info_ptr->valid & PNG_INFO_gAMA) != 0) { - *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma, - "png_get_gAMA"); + if (file_gamma != NULL) + *file_gamma = png_float(png_ptr, info_ptr->gamma, "gAMA"); + return PNG_INFO_gAMA; } @@ -729,9 +702,10 @@ png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr, png_debug1(1, "in %s retrieval function", "sRGB"); if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->valid & PNG_INFO_sRGB) != 0 && file_srgb_intent != NULL) + (info_ptr->valid & PNG_INFO_sRGB) != 0) { - *file_srgb_intent = info_ptr->colorspace.rendering_intent; + if (file_srgb_intent != NULL) + *file_srgb_intent = info_ptr->rendering_intent; return PNG_INFO_sRGB; } @@ -787,7 +761,7 @@ png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr, #ifdef PNG_cICP_SUPPORTED png_uint_32 PNGAPI png_get_cICP(png_const_structrp png_ptr, - png_inforp info_ptr, png_bytep colour_primaries, + png_const_inforp info_ptr, png_bytep colour_primaries, png_bytep transfer_function, png_bytep matrix_coefficients, png_bytep video_full_range_flag) { @@ -805,10 +779,115 @@ png_get_cICP(png_const_structrp png_ptr, return (PNG_INFO_cICP); } - return (0); + return 0; } #endif +#ifdef PNG_cLLI_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cLLI_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32p maxCLL, + png_uint_32p maxFALL) +{ + png_debug1(1, "in %s retrieval function", "cLLI"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_cLLI) != 0) + { + if (maxCLL != NULL) *maxCLL = info_ptr->maxCLL; + if (maxFALL != NULL) *maxFALL = info_ptr->maxFALL; + return PNG_INFO_cLLI; + } + + return 0; +} +# endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cLLI(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *maxCLL, double *maxFALL) +{ + png_debug1(1, "in %s retrieval function", "cLLI(float)"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_cLLI) != 0) + { + if (maxCLL != NULL) *maxCLL = info_ptr->maxCLL * .0001; + if (maxFALL != NULL) *maxFALL = info_ptr->maxFALL * .0001; + return PNG_INFO_cLLI; + } + + return 0; +} +# endif +#endif /* cLLI */ + +#ifdef PNG_mDCV_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_mDCV_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, + png_fixed_point *red_x, png_fixed_point *red_y, + png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y, + png_uint_32p mastering_maxDL, png_uint_32p mastering_minDL) +{ + png_debug1(1, "in %s retrieval function", "mDCV"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_mDCV) != 0) + { + if (white_x != NULL) *white_x = info_ptr->mastering_white_x * 2; + if (white_y != NULL) *white_y = info_ptr->mastering_white_y * 2; + if (red_x != NULL) *red_x = info_ptr->mastering_red_x * 2; + if (red_y != NULL) *red_y = info_ptr->mastering_red_y * 2; + if (green_x != NULL) *green_x = info_ptr->mastering_green_x * 2; + if (green_y != NULL) *green_y = info_ptr->mastering_green_y * 2; + if (blue_x != NULL) *blue_x = info_ptr->mastering_blue_x * 2; + if (blue_y != NULL) *blue_y = info_ptr->mastering_blue_y * 2; + if (mastering_maxDL != NULL) *mastering_maxDL = info_ptr->mastering_maxDL; + if (mastering_minDL != NULL) *mastering_minDL = info_ptr->mastering_minDL; + return PNG_INFO_mDCV; + } + + return 0; +} +# endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_mDCV(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y, + double *mastering_maxDL, double *mastering_minDL) +{ + png_debug1(1, "in %s retrieval function", "mDCV(float)"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_mDCV) != 0) + { + if (white_x != NULL) *white_x = info_ptr->mastering_white_x * .00002; + if (white_y != NULL) *white_y = info_ptr->mastering_white_y * .00002; + if (red_x != NULL) *red_x = info_ptr->mastering_red_x * .00002; + if (red_y != NULL) *red_y = info_ptr->mastering_red_y * .00002; + if (green_x != NULL) *green_x = info_ptr->mastering_green_x * .00002; + if (green_y != NULL) *green_y = info_ptr->mastering_green_y * .00002; + if (blue_x != NULL) *blue_x = info_ptr->mastering_blue_x * .00002; + if (blue_y != NULL) *blue_y = info_ptr->mastering_blue_y * .00002; + if (mastering_maxDL != NULL) + *mastering_maxDL = info_ptr->mastering_maxDL * .0001; + if (mastering_minDL != NULL) + *mastering_minDL = info_ptr->mastering_minDL * .0001; + return PNG_INFO_mDCV; + } + + return 0; +} +# endif /* FLOATING_POINT */ +#endif /* mDCV */ + #ifdef PNG_eXIf_SUPPORTED png_uint_32 PNGAPI png_get_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, diff --git a/3rdparty/libpng/pnginfo.h b/3rdparty/libpng/pnginfo.h index e85420c1ad..584a42f956 100644 --- a/3rdparty/libpng/pnginfo.h +++ b/3rdparty/libpng/pnginfo.h @@ -1,6 +1,6 @@ -/* pnginfo.h - header file for PNG reference library +/* pnginfo.h - internal structures for libpng * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2013,2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -10,43 +10,20 @@ * and license in png.h */ - /* png_info is a structure that holds the information in a PNG file so - * that the application can find out the characteristics of the image. - * If you are reading the file, this structure will tell you what is - * in the PNG file. If you are writing the file, fill in the information - * you want to put into the PNG file, using png_set_*() functions, then - * call png_write_info(). +#ifndef PNGPRIV_H +# error This file must not be included by applications; please include +#endif + +/* INTERNAL, PRIVATE definition of a PNG. * - * The names chosen should be very close to the PNG specification, so - * consult that document for information about the meaning of each field. + * png_info is a modifiable description of a PNG datastream. The fields inside + * this structure are accessed through png_get_() functions and modified + * using png_set_() functions. * - * With libpng < 0.95, it was only possible to directly set and read the - * the values in the png_info_struct, which meant that the contents and - * order of the values had to remain fixed. With libpng 0.95 and later, - * however, there are now functions that abstract the contents of - * png_info_struct from the application, so this makes it easier to use - * libpng with dynamic libraries, and even makes it possible to use - * libraries that don't have all of the libpng ancillary chunk-handing - * functionality. In libpng-1.5.0 this was moved into a separate private - * file that is not visible to applications. - * - * The following members may have allocated storage attached that should be - * cleaned up before the structure is discarded: palette, trans, text, - * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile, - * splt_palettes, scal_unit, row_pointers, and unknowns. By default, these - * are automatically freed when the info structure is deallocated, if they were - * allocated internally by libpng. This behavior can be changed by means - * of the png_data_freer() function. - * - * More allocation details: all the chunk-reading functions that - * change these members go through the corresponding png_set_* - * functions. A function to clear these members is available: see - * png_free_data(). The png_set_* functions do not depend on being - * able to point info structure members to any of the storage they are - * passed (they make their own copies), EXCEPT that the png_set_text - * functions use the same storage passed to them in the text_ptr or - * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns - * functions do not make their own copies. + * Some functions in libpng do directly access members of png_info. However, + * this should be avoided. png_struct objects contain members which hold + * caches, sometimes optimised, of the values from png_info objects, and + * png_info is not passed to the functions which read and write image data. */ #ifndef PNGINFO_H #define PNGINFO_H @@ -86,20 +63,6 @@ struct png_info_def * and initialize the appropriate fields below. */ -#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) - /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are - * defined. When COLORSPACE is switched on all the colorspace-defining - * chunks should be enabled, when GAMMA is switched on all the gamma-defining - * chunks should be enabled. If this is not done it becomes possible to read - * inconsistent PNG files and assign a probably incorrect interpretation to - * the information. (In other words, by carefully choosing which chunks to - * recognize the system configuration can select an interpretation for PNG - * files containing ambiguous data and this will result in inconsistent - * behavior between different libpng builds!) - */ - png_colorspace colorspace; -#endif - #ifdef PNG_cICP_SUPPORTED /* cICP chunk data */ png_byte cicp_colour_primaries; @@ -115,6 +78,24 @@ struct png_info_def png_uint_32 iccp_proflen; /* ICC profile data length */ #endif +#ifdef PNG_cLLI_SUPPORTED + png_uint_32 maxCLL; /* cd/m2 (nits) * 10,000 */ + png_uint_32 maxFALL; +#endif + +#ifdef PNG_mDCV_SUPPORTED + png_uint_16 mastering_red_x; /* CIE (xy) x * 50,000 */ + png_uint_16 mastering_red_y; + png_uint_16 mastering_green_x; + png_uint_16 mastering_green_y; + png_uint_16 mastering_blue_x; + png_uint_16 mastering_blue_y; + png_uint_16 mastering_white_x; + png_uint_16 mastering_white_y; + png_uint_32 mastering_maxDL; /* cd/m2 (nits) * 10,000 */ + png_uint_32 mastering_minDL; +#endif + #ifdef PNG_TEXT_SUPPORTED /* The tEXt, and zTXt chunks contain human-readable textual data in * uncompressed, compressed, and optionally compressed forms, respectively. @@ -193,11 +174,8 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) #endif #ifdef PNG_eXIf_SUPPORTED - int num_exif; /* Added at libpng-1.6.31 */ + png_uint_32 num_exif; /* Added at libpng-1.6.31 */ png_bytep exif; -# ifdef PNG_READ_eXIf_SUPPORTED - png_bytep eXIf_buf; /* Added at libpng-1.6.32 */ -# endif #endif #ifdef PNG_hIST_SUPPORTED @@ -270,5 +248,16 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) png_bytepp row_pointers; /* the image bits */ #endif +#ifdef PNG_cHRM_SUPPORTED + png_xy cHRM; +#endif + +#ifdef PNG_gAMA_SUPPORTED + png_fixed_point gamma; +#endif + +#ifdef PNG_sRGB_SUPPORTED + int rendering_intent; +#endif }; #endif /* PNGINFO_H */ diff --git a/3rdparty/libpng/pngmem.c b/3rdparty/libpng/pngmem.c index d391c13ff4..71e61c99f7 100644 --- a/3rdparty/libpng/pngmem.c +++ b/3rdparty/libpng/pngmem.c @@ -1,6 +1,6 @@ /* pngmem.c - stub functions for memory allocation * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2014,2016 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -72,30 +72,29 @@ png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size), * to implement a user memory handler. This checks to be sure it isn't * called with big numbers. */ -#ifndef PNG_USER_MEM_SUPPORTED - PNG_UNUSED(png_ptr) -#endif +# ifdef PNG_MAX_MALLOC_64K + /* This is support for legacy systems which had segmented addressing + * limiting the maximum allocation size to 65536. It takes precedence + * over PNG_SIZE_MAX which is set to 65535 on true 16-bit systems. + * + * TODO: libpng-1.8: finally remove both cases. + */ + if (size > 65536U) return NULL; +# endif - /* Some compilers complain that this is always true. However, it - * can be false when integer overflow happens. + /* This is checked too because the system malloc call below takes a (size_t). */ - if (size > 0 && size <= PNG_SIZE_MAX -# ifdef PNG_MAX_MALLOC_64K - && size <= 65536U -# endif - ) - { -#ifdef PNG_USER_MEM_SUPPORTED + if (size > PNG_SIZE_MAX) return NULL; + +# ifdef PNG_USER_MEM_SUPPORTED if (png_ptr != NULL && png_ptr->malloc_fn != NULL) return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size); +# else + PNG_UNUSED(png_ptr) +# endif - else -#endif - return malloc((size_t)size); /* checked for truncation above */ - } - - else - return NULL; + /* Use the system malloc */ + return malloc((size_t)/*SAFE*/size); /* checked for truncation above */ } #if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\ diff --git a/3rdparty/libpng/pngpread.c b/3rdparty/libpng/pngpread.c index 1bf880eabb..37aa432aec 100644 --- a/3rdparty/libpng/pngpread.c +++ b/3rdparty/libpng/pngpread.c @@ -1,6 +1,6 @@ /* pngpread.c - read a png file in push mode * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -193,17 +193,8 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) */ if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0) { - png_byte chunk_length[4]; - png_byte chunk_tag[4]; - PNG_PUSH_SAVE_BUFFER_IF_LT(8) - png_push_fill_buffer(png_ptr, chunk_length, 4); - png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); - png_reset_crc(png_ptr); - png_crc_read(png_ptr, chunk_tag, 4); - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); - png_check_chunk_name(png_ptr, png_ptr->chunk_name); - png_check_chunk_length(png_ptr, png_ptr->push_length); + png_ptr->push_length = png_read_chunk_header(png_ptr); png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; } @@ -238,19 +229,27 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) png_benign_error(png_ptr, "Too many IDATs found"); } + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + /* These flags must be set consistently for all non-IDAT chunks, + * including the unknown chunks. + */ + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT | PNG_AFTER_IDAT; + } + if (chunk_name == png_IHDR) { if (png_ptr->push_length != 13) png_error(png_ptr, "Invalid IHDR length"); PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + png_handle_chunk(png_ptr, info_ptr, png_ptr->push_length); } else if (chunk_name == png_IEND) { PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + png_handle_chunk(png_ptr, info_ptr, png_ptr->push_length); png_ptr->process_mode = PNG_READ_DONE_MODE; png_push_have_end(png_ptr, info_ptr); @@ -267,12 +266,6 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) } #endif - else if (chunk_name == png_PLTE) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); - } - else if (chunk_name == png_IDAT) { png_ptr->idat_size = png_ptr->push_length; @@ -285,163 +278,10 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) return; } -#ifdef PNG_READ_gAMA_SUPPORTED - else if (png_ptr->chunk_name == png_gAMA) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sBIT_SUPPORTED - else if (png_ptr->chunk_name == png_sBIT) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_cHRM_SUPPORTED - else if (png_ptr->chunk_name == png_cHRM) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_cICP_SUPPORTED - else if (png_ptr->chunk_name == png_cICP) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_cICP(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_eXIf_SUPPORTED - else if (png_ptr->chunk_name == png_eXIf) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_eXIf(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_iCCP_SUPPORTED - else if (png_ptr->chunk_name == png_iCCP) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); - } -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); - } - -#endif -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - { - PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); - } -#endif - else { PNG_PUSH_SAVE_BUFFER_IF_FULL - png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, - PNG_HANDLE_CHUNK_AS_DEFAULT); + png_handle_chunk(png_ptr, info_ptr, png_ptr->push_length); } png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; diff --git a/3rdparty/libpng/pngpriv.h b/3rdparty/libpng/pngpriv.h index 84f77c3508..fc8d461cf5 100644 --- a/3rdparty/libpng/pngpriv.h +++ b/3rdparty/libpng/pngpriv.h @@ -1,6 +1,6 @@ /* pngpriv.h - private declarations for use inside libpng * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -19,8 +19,20 @@ * they should be well aware of the issues that may arise from doing so. */ + +/* pngpriv.h must be included first in each translation unit inside libpng. + * On the other hand, it must not be included at all, directly or indirectly, + * by any application code that uses the libpng API. + */ #ifndef PNGPRIV_H -#define PNGPRIV_H +# define PNGPRIV_H +#else +# error Duplicate inclusion of pngpriv.h; please check the libpng source files +#endif + +#if defined(PNG_H) || defined(PNGCONF_H) || defined(PNGLCONF_H) +# error This file must not be included by applications; please include +#endif /* Feature Test Macros. The following are defined here to ensure that correctly * implemented libraries reveal the APIs libpng needs to build and hide those @@ -57,7 +69,6 @@ */ #if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H) # include - /* Pick up the definition of 'restrict' from config.h if it was read: */ # define PNG_RESTRICT restrict #endif @@ -67,9 +78,7 @@ * are not internal definitions may be required. This is handled below just * before png.h is included, but load the configuration now if it is available. */ -#ifndef PNGLCONF_H -# include "pnglibconf.h" -#endif +#include "pnglibconf.h" /* Local renames may change non-exported API functions from png.h */ #if defined(PNG_PREFIX) && !defined(PNGPREFIX_H) @@ -134,6 +143,20 @@ # endif #endif +#ifndef PNG_RISCV_RVV_OPT + /* RISCV_RVV optimizations are being controlled by the compiler settings, + * typically the target compiler will define __riscv but the rvv extension + * availability has to be explicitly stated. This is why if no + * PNG_RISCV_RVV_OPT was defined then a runtime check will be executed. + * + * To enable RISCV_RVV optimizations unconditionally, and compile the + * associated code, pass --enable-riscv-rvv=yes or --enable-riscv-rvv=on + * to configure or put -DPNG_RISCV_RVV_OPT=2 in CPPFLAGS. + */ + +# define PNG_RISCV_RVV_OPT 0 +#endif + #if PNG_ARM_NEON_OPT > 0 /* NEON optimizations are to be at least considered by libpng, so enable the * callbacks to do this. @@ -279,6 +302,16 @@ # define PNG_LOONGARCH_LSX_IMPLEMENTATION 0 #endif +#if PNG_RISCV_RVV_OPT > 0 && __riscv_v >= 1000000 +# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_rvv +# ifndef PNG_RISCV_RVV_IMPLEMENTATION + /* Use the intrinsics code by default. */ +# define PNG_RISCV_RVV_IMPLEMENTATION 1 +# endif +#else +# define PNG_RISCV_RVV_IMPLEMENTATION 0 +#endif /* PNG_RISCV_RVV_OPT > 0 && __riscv_v >= 1000000 */ + /* Is this a build of a DLL where compilation of the object modules requires * different preprocessor settings to those required for a simple library? If * so PNG_BUILD_DLL must be set. @@ -671,13 +704,13 @@ #define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200U #define PNG_FLAG_CRC_CRITICAL_USE 0x0400U #define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800U -#define PNG_FLAG_ASSUME_sRGB 0x1000U /* Added to libpng-1.5.4 */ +/* PNG_FLAG_ASSUME_sRGB unused 0x1000U * Added to libpng-1.5.4 */ #define PNG_FLAG_OPTIMIZE_ALPHA 0x2000U /* Added to libpng-1.5.4 */ #define PNG_FLAG_DETECT_UNINITIALIZED 0x4000U /* Added to libpng-1.5.4 */ /* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000U */ /* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000U */ #define PNG_FLAG_LIBRARY_MISMATCH 0x20000U -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000U + /* 0x40000U unused */ #define PNG_FLAG_STRIP_ERROR_TEXT 0x80000U #define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000U /* Added to libpng-1.4.0 */ #define PNG_FLAG_APP_WARNINGS_WARN 0x200000U /* Added to libpng-1.6.0 */ @@ -782,6 +815,8 @@ #ifdef PNG_FIXED_POINT_MACRO_SUPPORTED #define png_fixed(png_ptr, fp, s) ((fp) <= 21474 && (fp) >= -21474 ?\ ((png_fixed_point)(100000 * (fp))) : (png_fixed_error(png_ptr, s),0)) +#define png_fixed_ITU(png_ptr, fp, s) ((fp) <= 214748 && (fp) >= 0 ?\ + ((png_uint_32)(10000 * (fp))) : (png_fixed_error(png_ptr, s),0)) #endif /* else the corresponding function is defined below, inside the scope of the * cplusplus test. @@ -800,11 +835,31 @@ * * PNG_32b correctly produces a value shifted by up to 24 bits, even on * architectures where (int) is only 16 bits. + * + * 1.6.47: PNG_32b was made into a preprocessor evaluable macro by replacing the + * static_cast with a promoting binary operation using a guaranteed 32-bit + * (minimum) unsigned value. */ -#define PNG_32b(b,s) ((png_uint_32)(b) << (s)) +#define PNG_32b(b,s) (((0xFFFFFFFFU)&(b)) << (s)) #define PNG_U32(b1,b2,b3,b4) \ (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0)) +/* Chunk name validation. When using these macros all the arguments should be + * constants, otherwise code bloat may well occur. The macros are provided + * primarily for use in #if checks. + * + * PNG_32to8 produces a byte value with the right shift; used to extract the + * byte value from a chunk name. + */ +#define PNG_32to8(cn,s) (((cn) >> (s)) & 0xffU) +#define PNG_CN_VALID_UPPER(b) ((b) >= 65 && (b) <= 90) /* upper-case ASCII */ +#define PNG_CN_VALID_ASCII(b) PNG_CN_VALID_UPPER((b) & ~32U) +#define PNG_CHUNK_NAME_VALID(cn) (\ + PNG_CN_VALID_ASCII(PNG_32to8(cn,24)) && /* critical, !ancillary */\ + PNG_CN_VALID_ASCII(PNG_32to8(cn,16)) && /* public, !privately defined */\ + PNG_CN_VALID_UPPER(PNG_32to8(cn, 8)) && /* VALID, !reserved */\ + PNG_CN_VALID_ASCII(PNG_32to8(cn, 0)) /* data-dependent, !copy ok */) + /* Constants for known chunk types. * * MAINTAINERS: If you need to add a chunk, define the name here. @@ -832,10 +887,14 @@ #define png_IEND PNG_U32( 73, 69, 78, 68) #define png_IHDR PNG_U32( 73, 72, 68, 82) #define png_PLTE PNG_U32( 80, 76, 84, 69) +#define png_acTL PNG_U32( 97, 99, 84, 76) /* PNGv3: APNG */ #define png_bKGD PNG_U32( 98, 75, 71, 68) #define png_cHRM PNG_U32( 99, 72, 82, 77) -#define png_cICP PNG_U32( 99, 73, 67, 80) +#define png_cICP PNG_U32( 99, 73, 67, 80) /* PNGv3 */ +#define png_cLLI PNG_U32( 99, 76, 76, 73) /* PNGv3 */ #define png_eXIf PNG_U32(101, 88, 73, 102) /* registered July 2017 */ +#define png_fcTL PNG_U32(102, 99, 84, 76) /* PNGv3: APNG */ +#define png_fdAT PNG_U32(102, 100, 65, 84) /* PNGv3: APNG */ #define png_fRAc PNG_U32(102, 82, 65, 99) /* registered, not defined */ #define png_gAMA PNG_U32(103, 65, 77, 65) #define png_gIFg PNG_U32(103, 73, 70, 103) @@ -844,6 +903,7 @@ #define png_hIST PNG_U32(104, 73, 83, 84) #define png_iCCP PNG_U32(105, 67, 67, 80) #define png_iTXt PNG_U32(105, 84, 88, 116) +#define png_mDCV PNG_U32(109, 68, 67, 86) /* PNGv3 */ #define png_oFFs PNG_U32(111, 70, 70, 115) #define png_pCAL PNG_U32(112, 67, 65, 76) #define png_pHYs PNG_U32(112, 72, 89, 115) @@ -884,11 +944,74 @@ #define PNG_CHUNK_RESERVED(c) (1 & ((c) >> 13)) #define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >> 5)) +/* Known chunks. All supported chunks must be listed here. The macro PNG_CHUNK + * contains the four character ASCII name by which the chunk is identified. The + * macro is implemented as required to build tables or switch statements which + * require entries for every known chunk. The macro also contains an index + * value which should be in order (this is checked in png.c). + * + * Notice that "known" does not require "SUPPORTED"; tables should be built in + * such a way that chunks unsupported in a build require no more than the table + * entry (which should be small.) In particular function pointers for + * unsupported chunks should be NULL. + * + * At present these index values are not exported (not part of the public API) + * so can be changed at will. For convenience the names are in lexical sort + * order but with the critical chunks at the start in the order of occurence in + * a PNG. + * + * PNG_INFO_ values do not exist for every one of these chunk handles; for + * example PNG_INFO_{IDAT,IEND,tEXt,iTXt,zTXt} and possibly other chunks in the + * future. + */ +#define PNG_KNOWN_CHUNKS\ + PNG_CHUNK(IHDR, 0)\ + PNG_CHUNK(PLTE, 1)\ + PNG_CHUNK(IDAT, 2)\ + PNG_CHUNK(IEND, 3)\ + PNG_CHUNK(acTL, 4)\ + PNG_CHUNK(bKGD, 5)\ + PNG_CHUNK(cHRM, 6)\ + PNG_CHUNK(cICP, 7)\ + PNG_CHUNK(cLLI, 8)\ + PNG_CHUNK(eXIf, 9)\ + PNG_CHUNK(fcTL, 10)\ + PNG_CHUNK(fdAT, 11)\ + PNG_CHUNK(gAMA, 12)\ + PNG_CHUNK(hIST, 13)\ + PNG_CHUNK(iCCP, 14)\ + PNG_CHUNK(iTXt, 15)\ + PNG_CHUNK(mDCV, 16)\ + PNG_CHUNK(oFFs, 17)\ + PNG_CHUNK(pCAL, 18)\ + PNG_CHUNK(pHYs, 19)\ + PNG_CHUNK(sBIT, 20)\ + PNG_CHUNK(sCAL, 21)\ + PNG_CHUNK(sPLT, 22)\ + PNG_CHUNK(sRGB, 23)\ + PNG_CHUNK(tEXt, 24)\ + PNG_CHUNK(tIME, 25)\ + PNG_CHUNK(tRNS, 26)\ + PNG_CHUNK(zTXt, 27) + /* Gamma values (new at libpng-1.5.4): */ #define PNG_GAMMA_MAC_OLD 151724 /* Assume '1.8' is really 2.2/1.45! */ #define PNG_GAMMA_MAC_INVERSE 65909 #define PNG_GAMMA_sRGB_INVERSE 45455 +/* gamma sanity check. libpng cannot implement gamma transforms outside a + * certain limit because of its use of 16-bit fixed point intermediate values. + * Gamma values that are too large or too small will zap the 16-bit values all + * to 0 or 65535 resulting in an obvious 'bad' image. + * + * In libpng 1.6.0 the limits were changed from 0.07..3 to 0.01..100 to + * accommodate the optimal 16-bit gamma of 36 and its reciprocal. + * + * These are png_fixed_point integral values: + */ +#define PNG_LIB_GAMMA_MIN 1000 +#define PNG_LIB_GAMMA_MAX 10000000 + /* Almost everything below is C specific; the #defines above can be used in * non-C code (so long as it is C-preprocessed) the rest of this stuff cannot. */ @@ -901,17 +1024,15 @@ * must match that used in the build, or we must be using pnglibconf.h.prebuilt: */ #if PNG_ZLIB_VERNUM != 0 && PNG_ZLIB_VERNUM != ZLIB_VERNUM -# error ZLIB_VERNUM != PNG_ZLIB_VERNUM \ - "-I (include path) error: see the notes in pngpriv.h" - /* This means that when pnglibconf.h was built the copy of zlib.h that it - * used is not the same as the one being used here. Because the build of - * libpng makes decisions to use inflateInit2 and inflateReset2 based on the - * zlib version number and because this affects handling of certain broken - * PNG files the -I directives must match. +# error The include path of is incorrect + /* When pnglibconf.h was built, the copy of zlib.h that it used was not the + * same as the one being used here. Considering how libpng makes decisions + * to use the zlib API based on the zlib version number, the -I options must + * match. * - * The most likely explanation is that you passed a -I in CFLAGS. This will - * not work; all the preprocessor directives and in particular all the -I - * directives must be in CPPFLAGS. + * A possible cause of this mismatch is that you passed an -I option in + * CFLAGS, which is unlikely to work. All the preprocessor options, and all + * the -I options in particular, should be in CPPFLAGS. */ #endif @@ -952,7 +1073,6 @@ extern "C" { * * All of these functions must be declared with PNG_INTERNAL_FUNCTION. */ - /* Zlib support */ #define PNG_UNEXPECTED_ZLIB_RETURN (-7) PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret), @@ -971,6 +1091,7 @@ PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr, !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_mDCV_SUPPORTED) || \ defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \ (defined(PNG_sCAL_SUPPORTED) && \ defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)) @@ -978,12 +1099,38 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr, double fp, png_const_charp text),PNG_EMPTY); #endif +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_cLLI_SUPPORTED) || defined(PNG_mDCV_SUPPORTED)) +PNG_INTERNAL_FUNCTION(png_uint_32,png_fixed_ITU,(png_const_structrp png_ptr, + double fp, png_const_charp text),PNG_EMPTY); +#endif + /* Check the user version string for compatibility, returns false if the version * numbers aren't compatible. */ PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr, png_const_charp user_png_ver),PNG_EMPTY); +#ifdef PNG_READ_SUPPORTED /* should only be used on read */ +/* Security: read limits on the largest allocations while reading a PNG. This + * avoids very large allocations caused by PNG files with damaged or altered + * chunk 'length' fields. + */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED /* run-time limit */ +# define png_chunk_max(png_ptr) ((png_ptr)->user_chunk_malloc_max) + +#elif PNG_USER_CHUNK_MALLOC_MAX > 0 /* compile-time limit */ +# define png_chunk_max(png_ptr) ((void)png_ptr, PNG_USER_CHUNK_MALLOC_MAX) + +#elif (defined PNG_MAX_MALLOC_64K) /* legacy system limit */ +# define png_chunk_max(png_ptr) ((void)png_ptr, 65536U) + +#else /* modern system limit SIZE_MAX (C99) */ +# define png_chunk_max(png_ptr) ((void)png_ptr, PNG_SIZE_MAX) +#endif +#endif /* READ */ + /* Internal base allocator - no messages, NULL on failure to allocate. This * does, however, call the application provided allocator and that could call * png_error (although that would be a bug in the application implementation.) @@ -1083,9 +1230,6 @@ PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf, PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr, png_uint_32 skip),PNG_EMPTY); -/* Read the CRC from the file and compare it to the libpng calculated CRC */ -PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY); - /* Calculate the CRC over a section of data. Note that we are only * passing a maximum of 64K on systems that have this as a memory limit, * since this is the maximum buffer size we can specify. @@ -1137,6 +1281,20 @@ PNG_INTERNAL_FUNCTION(void,png_write_cICP,(png_structrp png_ptr, png_byte matrix_coefficients, png_byte video_full_range_flag), PNG_EMPTY); #endif +#ifdef PNG_WRITE_cLLI_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_cLLI_fixed,(png_structrp png_ptr, + png_uint_32 maxCLL, png_uint_32 maxFALL), PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_mDCV_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_mDCV_fixed,(png_structrp png_ptr, + png_uint_16 red_x, png_uint_16 red_y, + png_uint_16 green_x, png_uint_16 green_y, + png_uint_16 blue_x, png_uint_16 blue_y, + png_uint_16 white_x, png_uint_16 white_y, + png_uint_32 maxDL, png_uint_32 minDL), PNG_EMPTY); +#endif + #ifdef PNG_WRITE_sRGB_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr, int intent),PNG_EMPTY); @@ -1149,10 +1307,10 @@ PNG_INTERNAL_FUNCTION(void,png_write_eXIf,(png_structrp png_ptr, #ifdef PNG_WRITE_iCCP_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr, - png_const_charp name, png_const_bytep profile), PNG_EMPTY); - /* The profile must have been previously validated for correctness, the - * length comes from the first four bytes. Only the base, deflate, - * compression is supported. + png_const_charp name, png_const_bytep profile, png_uint_32 proflen), + PNG_EMPTY); + /* Writes a previously 'set' profile. The profile argument is **not** + * compressed. */ #endif @@ -1388,6 +1546,23 @@ PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_lsx,(png_row_infop row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); #endif +#if PNG_RISCV_RVV_IMPLEMENTATION == 1 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +#endif + /* Choose the best filter to use and filter the row data */ PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); @@ -1461,124 +1636,36 @@ PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info, /* The following decodes the appropriate chunks, and does error correction, * then calls the appropriate callback for the chunk if it is valid. */ +typedef enum +{ + /* Result of a call to png_handle_chunk made to handle the current chunk + * png_struct::chunk_name on read. Always informational, either the stream + * is read for the next chunk or the routine will call png_error. + * + * NOTE: order is important internally. handled_saved and above are regarded + * as handling the chunk. + */ + handled_error = 0, /* bad crc or known and bad format or too long */ + handled_discarded, /* not saved in the unknown chunk list */ + handled_saved, /* saved in the unknown chunk list */ + handled_ok /* known, supported and handled without error */ +} png_handle_result_code; -/* Decode the IHDR chunk */ -PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); - -#ifdef PNG_READ_bKGD_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_cHRM_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_cICP_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_cICP,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_eXIf_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_eXIf,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_gAMA_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif /* READ_iCCP */ - -#ifdef PNG_READ_iTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif /* READ_sPLT */ - -#ifdef PNG_READ_sRGB_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -#endif - -PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_const_structrp png_ptr, - png_uint_32 chunk_name),PNG_EMPTY); - -PNG_INTERNAL_FUNCTION(void,png_check_chunk_length,(png_const_structrp png_ptr, - png_uint_32 chunk_length),PNG_EMPTY); - -PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_unknown, + (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length, int keep), + PNG_EMPTY); /* This is the function that gets called for unknown chunks. The 'keep' * argument is either non-zero for a known chunk that has been set to be * handled as unknown or zero for an unknown chunk. By default the function * just skips the chunk or errors out if it is critical. */ +PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_chunk, + (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); + /* This handles the current chunk png_ptr->chunk_name with unread + * data[length] and returns one of the above result codes. + */ + #if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\ defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED) PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling, @@ -1618,8 +1705,6 @@ PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr, png_bytep buffer, size_t buffer_length),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr), PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr, png_inforp info_ptr),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr, @@ -1632,109 +1717,28 @@ PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr, png_inforp info_ptr),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr), PNG_EMPTY); -# ifdef PNG_READ_tEXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif -# ifdef PNG_READ_zTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif -# ifdef PNG_READ_iTXt_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr, - png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr, - png_inforp info_ptr),PNG_EMPTY); -# endif - #endif /* PROGRESSIVE_READ */ -/* Added at libpng version 1.6.0 */ -#ifdef PNG_GAMMA_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY); - /* Set the colorspace gamma with a value provided by the application or by - * the gAMA chunk on read. The value will override anything set by an ICC - * profile. - */ - -PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr, - png_inforp info_ptr), PNG_EMPTY); - /* Synchronize the info 'valid' flags with the colorspace */ - -PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr, - png_inforp info_ptr), PNG_EMPTY); - /* Copy the png_struct colorspace to the info_struct and call the above to - * synchronize the flags. Checks for NULL info_ptr and does nothing. - */ -#endif - -/* Added at libpng version 1.4.0 */ -#ifdef PNG_COLORSPACE_SUPPORTED -/* These internal functions are for maintaining the colorspace structure within - * a png_info or png_struct (or, indeed, both). - */ -PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities, - (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy, - int preferred), PNG_EMPTY); - -PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints, - (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ, - int preferred), PNG_EMPTY); - -#ifdef PNG_sRGB_SUPPORTED -PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr, - png_colorspacerp colorspace, int intent), PNG_EMPTY); - /* This does set the colorspace gAMA and cHRM values too, but doesn't set the - * flags to write them, if it returns false there was a problem and an error - * message has already been output (but the colorspace may still need to be - * synced to record the invalid flag). - */ -#endif /* sRGB */ - #ifdef PNG_iCCP_SUPPORTED -PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length, png_const_bytep profile, int color_type), - PNG_EMPTY); - /* The 'name' is used for information only */ - /* Routines for checking parts of an ICC profile. */ #ifdef PNG_READ_iCCP_SUPPORTED PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length), PNG_EMPTY); + png_const_charp name, png_uint_32 profile_length), PNG_EMPTY); #endif /* READ_iCCP */ PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length, + png_const_charp name, png_uint_32 profile_length, png_const_bytep profile /* first 132 bytes only */, int color_type), PNG_EMPTY); PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr, - png_colorspacerp colorspace, png_const_charp name, - png_uint_32 profile_length, + png_const_charp name, png_uint_32 profile_length, png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY); -#ifdef PNG_sRGB_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,( - png_const_structrp png_ptr, png_colorspacerp colorspace, - png_const_bytep profile, uLong adler), PNG_EMPTY); - /* 'adler' is the Adler32 checksum of the uncompressed profile data. It may - * be zero to indicate that it is not available. It is used, if provided, - * as a fast check on the profile when checking to see if it is sRGB. - */ -#endif #endif /* iCCP */ #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients, - (png_structrp png_ptr), PNG_EMPTY); - /* Set the rgb_to_gray coefficients from the colorspace Y values */ +PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients, (png_structrp png_ptr), + PNG_EMPTY); + /* Set the rgb_to_gray coefficients from the cHRM Y values (if unset) */ #endif /* READ_RGB_TO_GRAY */ -#endif /* COLORSPACE */ /* Added at libpng version 1.4.0 */ PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr, @@ -1996,8 +2000,10 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, size_t size),PNG_EMPTY); #endif /* pCAL || sCAL */ -#if defined(PNG_GAMMA_SUPPORTED) ||\ - defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) +#if defined(PNG_READ_GAMMA_SUPPORTED) ||\ + defined(PNG_COLORSPACE_SUPPORTED) ||\ + defined(PNG_INCH_CONVERSIONS_SUPPORTED) ||\ + defined(PNG_READ_pHYs_SUPPORTED) /* Added at libpng version 1.5.0 */ /* This is a utility to provide a*times/div (rounded) and indicate * if there is an overflow. The result is a boolean - false (0) @@ -2006,22 +2012,14 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, */ PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a, png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY); -#endif -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) -/* Same deal, but issue a warning on overflow and return 0. */ -PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn, - (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by, - png_int_32 divided_by),PNG_EMPTY); -#endif - -#ifdef PNG_GAMMA_SUPPORTED /* Calculate a reciprocal - used for gamma values. This returns * 0 if the argument is 0 in order to maintain an undefined value; * there are no warnings. */ PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a), PNG_EMPTY); +#endif #ifdef PNG_READ_GAMMA_SUPPORTED /* The same but gives a reciprocal of the product of two fixed point @@ -2030,14 +2028,22 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a), */ PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a, png_fixed_point b),PNG_EMPTY); -#endif /* Return true if the gamma value is significantly different from 1.0 */ PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value), PNG_EMPTY); -#endif -#ifdef PNG_READ_GAMMA_SUPPORTED +/* PNGv3: 'resolve' the file gamma according to the new PNGv3 rules for colour + * space information. + * + * NOTE: this uses precisely those chunks that libpng supports. For example it + * doesn't use iCCP and it can only use cICP for known and manageable + * transforms. For this reason a gamma specified by png_set_gamma always takes + * precedence. + */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_resolve_file_gamma, + (png_const_structrp png_ptr),PNG_EMPTY); + /* Internal fixed point gamma correction. These APIs are called as * required to convert single values - they don't need to be fast, * they are not used when processing image pixel values. @@ -2055,6 +2061,22 @@ PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr), PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr, int bit_depth),PNG_EMPTY); +#endif /* READ_GAMMA */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Set the RGB coefficients if not already set by png_set_rgb_to_gray */ +PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients,(png_structrp png_ptr), + PNG_EMPTY); +#endif + +#if defined(PNG_cHRM_SUPPORTED) || defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +PNG_INTERNAL_FUNCTION(int,png_XYZ_from_xy,(png_XYZ *XYZ, const png_xy *xy), + PNG_EMPTY); +#endif /* cHRM || READ_RGB_TO_GRAY */ + +#ifdef PNG_COLORSPACE_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_xy_from_XYZ,(png_xy *xy, const png_XYZ *XYZ), + PNG_EMPTY); #endif /* SIMPLIFIED READ/WRITE SUPPORT */ @@ -2153,6 +2175,11 @@ PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_lsx, (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); #endif +# if PNG_RISCV_RVV_IMPLEMENTATION == 1 +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_rvv, + (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); +#endif + PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr, png_const_charp key, png_bytep new_key), PNG_EMPTY); @@ -2188,4 +2215,3 @@ PNG_INTERNAL_FUNCTION(int, #endif #endif /* PNG_VERSION_INFO_ONLY */ -#endif /* PNGPRIV_H */ diff --git a/3rdparty/libpng/pngread.c b/3rdparty/libpng/pngread.c index 49e19a4960..13a93deedc 100644 --- a/3rdparty/libpng/pngread.c +++ b/3rdparty/libpng/pngread.c @@ -131,14 +131,11 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr) png_ptr->mode |= PNG_AFTER_IDAT; } - /* This should be a binary subdivision search or a hash for - * matching the chunk name rather than a linear search. - */ if (chunk_name == png_IHDR) - png_handle_IHDR(png_ptr, info_ptr, length); + png_handle_chunk(png_ptr, info_ptr, length); else if (chunk_name == png_IEND) - png_handle_IEND(png_ptr, info_ptr, length); + png_handle_chunk(png_ptr, info_ptr, length); #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) @@ -155,8 +152,6 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr) } } #endif - else if (chunk_name == png_PLTE) - png_handle_PLTE(png_ptr, info_ptr, length); else if (chunk_name == png_IDAT) { @@ -164,104 +159,8 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr) break; } -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - png_handle_bKGD(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_cHRM_SUPPORTED - else if (chunk_name == png_cHRM) - png_handle_cHRM(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_cICP_SUPPORTED - else if (chunk_name == png_cICP) - png_handle_cICP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_eXIf_SUPPORTED - else if (chunk_name == png_eXIf) - png_handle_eXIf(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_gAMA_SUPPORTED - else if (chunk_name == png_gAMA) - png_handle_gAMA(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - png_handle_hIST(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - png_handle_oFFs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - png_handle_pCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - png_handle_sCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - png_handle_pHYs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED - else if (chunk_name == png_sBIT) - png_handle_sBIT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - png_handle_sRGB(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED - else if (chunk_name == png_iCCP) - png_handle_iCCP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - png_handle_sPLT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - png_handle_tEXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - png_handle_tIME(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - png_handle_tRNS(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - png_handle_zTXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - png_handle_iTXt(png_ptr, info_ptr, length); -#endif - else - png_handle_unknown(png_ptr, info_ptr, length, - PNG_HANDLE_CHUNK_AS_DEFAULT); + png_handle_chunk(png_ptr, info_ptr, length); } } #endif /* SEQUENTIAL_READ */ @@ -803,13 +702,18 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr) png_uint_32 chunk_name = png_ptr->chunk_name; if (chunk_name != png_IDAT) - png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + { + /* These flags must be set consistently for all non-IDAT chunks, + * including the unknown chunks. + */ + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT | PNG_AFTER_IDAT; + } if (chunk_name == png_IEND) - png_handle_IEND(png_ptr, info_ptr, length); + png_handle_chunk(png_ptr, info_ptr, length); else if (chunk_name == png_IHDR) - png_handle_IHDR(png_ptr, info_ptr, length); + png_handle_chunk(png_ptr, info_ptr, length); else if (info_ptr == NULL) png_crc_finish(png_ptr, length); @@ -843,107 +747,9 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr) png_crc_finish(png_ptr, length); } - else if (chunk_name == png_PLTE) - png_handle_PLTE(png_ptr, info_ptr, length); - -#ifdef PNG_READ_bKGD_SUPPORTED - else if (chunk_name == png_bKGD) - png_handle_bKGD(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_cHRM_SUPPORTED - else if (chunk_name == png_cHRM) - png_handle_cHRM(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_cICP_SUPPORTED - else if (chunk_name == png_cICP) - png_handle_cICP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_eXIf_SUPPORTED - else if (chunk_name == png_eXIf) - png_handle_eXIf(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_gAMA_SUPPORTED - else if (chunk_name == png_gAMA) - png_handle_gAMA(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_hIST_SUPPORTED - else if (chunk_name == png_hIST) - png_handle_hIST(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_oFFs_SUPPORTED - else if (chunk_name == png_oFFs) - png_handle_oFFs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pCAL_SUPPORTED - else if (chunk_name == png_pCAL) - png_handle_pCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sCAL_SUPPORTED - else if (chunk_name == png_sCAL) - png_handle_sCAL(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_pHYs_SUPPORTED - else if (chunk_name == png_pHYs) - png_handle_pHYs(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sBIT_SUPPORTED - else if (chunk_name == png_sBIT) - png_handle_sBIT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sRGB_SUPPORTED - else if (chunk_name == png_sRGB) - png_handle_sRGB(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iCCP_SUPPORTED - else if (chunk_name == png_iCCP) - png_handle_iCCP(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_sPLT_SUPPORTED - else if (chunk_name == png_sPLT) - png_handle_sPLT(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tEXt_SUPPORTED - else if (chunk_name == png_tEXt) - png_handle_tEXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tIME_SUPPORTED - else if (chunk_name == png_tIME) - png_handle_tIME(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_tRNS_SUPPORTED - else if (chunk_name == png_tRNS) - png_handle_tRNS(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_zTXt_SUPPORTED - else if (chunk_name == png_zTXt) - png_handle_zTXt(png_ptr, info_ptr, length); -#endif - -#ifdef PNG_READ_iTXt_SUPPORTED - else if (chunk_name == png_iTXt) - png_handle_iTXt(png_ptr, info_ptr, length); -#endif else - png_handle_unknown(png_ptr, info_ptr, length, - PNG_HANDLE_CHUNK_AS_DEFAULT); + png_handle_chunk(png_ptr, info_ptr, length); } while ((png_ptr->mode & PNG_HAVE_IEND) == 0); } #endif /* SEQUENTIAL_READ */ @@ -1008,7 +814,8 @@ png_read_destroy(png_structrp png_ptr) #endif #if defined(PNG_READ_EXPAND_SUPPORTED) && \ - defined(PNG_ARM_NEON_IMPLEMENTATION) + (defined(PNG_ARM_NEON_IMPLEMENTATION) || \ + defined(PNG_RISCV_RVV_IMPLEMENTATION)) png_free(png_ptr, png_ptr->riffled_palette); png_ptr->riffled_palette = NULL; #endif @@ -1394,6 +1201,31 @@ png_image_format(png_structrp png_ptr) return format; } +static int +chromaticities_match_sRGB(const png_xy *xy) +{ +# define sRGB_TOLERANCE 1000 + static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */ + { + /* color x y */ + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000, + /* white */ 31270, 32900 + }; + + if (PNG_OUT_OF_RANGE(xy->whitex, sRGB_xy.whitex,sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->whitey, sRGB_xy.whitey,sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->redx, sRGB_xy.redx, sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->redy, sRGB_xy.redy, sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->greenx, sRGB_xy.greenx,sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->greeny, sRGB_xy.greeny,sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->bluex, sRGB_xy.bluex, sRGB_TOLERANCE) || + PNG_OUT_OF_RANGE(xy->bluey, sRGB_xy.bluey, sRGB_TOLERANCE)) + return 0; + return 1; +} + /* Is the given gamma significantly different from sRGB? The test is the same * one used in pngrtran.c when deciding whether to do gamma correction. The * arithmetic optimizes the division by using the fact that the inverse of the @@ -1402,22 +1234,44 @@ png_image_format(png_structrp png_ptr) static int png_gamma_not_sRGB(png_fixed_point g) { - if (g < PNG_FP_1) - { - /* An uninitialized gamma is assumed to be sRGB for the simplified API. */ - if (g == 0) - return 0; + /* 1.6.47: use the same sanity checks as used in pngrtran.c */ + if (g < PNG_LIB_GAMMA_MIN || g > PNG_LIB_GAMMA_MAX) + return 0; /* Includes the uninitialized value 0 */ - return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */); - } - - return 1; + return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */); } /* Do the main body of a 'png_image_begin_read' function; read the PNG file * header and fill in all the information. This is executed in a safe context, * unlike the init routine above. */ +static int +png_image_is_not_sRGB(png_const_structrp png_ptr) +{ + /* Does the colorspace **not** match sRGB? The flag is only set if the + * answer can be determined reliably. + * + * png_struct::chromaticities always exists since the simplified API + * requires rgb-to-gray. The mDCV, cICP and cHRM chunks may all set it to + * a non-sRGB value, so it needs to be checked but **only** if one of + * those chunks occured in the file. + */ + /* Highest priority: check to be safe. */ + if (png_has_chunk(png_ptr, cICP) || png_has_chunk(png_ptr, mDCV)) + return !chromaticities_match_sRGB(&png_ptr->chromaticities); + + /* If the image is marked as sRGB then it is... */ + if (png_has_chunk(png_ptr, sRGB)) + return 0; + + /* Last stop: cHRM, must check: */ + if (png_has_chunk(png_ptr, cHRM)) + return !chromaticities_match_sRGB(&png_ptr->chromaticities); + + /* Else default to sRGB */ + return 0; +} + static int png_image_read_header(png_voidp argument) { @@ -1439,17 +1293,13 @@ png_image_read_header(png_voidp argument) image->format = format; -#ifdef PNG_COLORSPACE_SUPPORTED - /* Does the colorspace match sRGB? If there is no color endpoint - * (colorant) information assume yes, otherwise require the - * 'ENDPOINTS_MATCHP_sRGB' colorspace flag to have been set. If the - * colorspace has been determined to be invalid ignore it. + /* Greyscale images don't (typically) have colour space information and + * using it is pretty much impossible, so use sRGB for grayscale (it + * doesn't matter r==g==b so the transform is irrelevant.) */ - if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags - & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB| - PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS)) + if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && + png_image_is_not_sRGB(png_ptr)) image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; -#endif } /* We need the maximum number of entries regardless of the format the @@ -1484,7 +1334,7 @@ png_image_read_header(png_voidp argument) #ifdef PNG_STDIO_SUPPORTED int PNGAPI -png_image_begin_read_from_stdio(png_imagep image, FILE* file) +png_image_begin_read_from_stdio(png_imagep image, FILE *file) { if (image != NULL && image->version == PNG_IMAGE_VERSION) { @@ -1637,21 +1487,18 @@ png_image_skip_unused_chunks(png_structrp png_ptr) * potential vulnerability to security problems in the unused chunks. * * At present the iCCP chunk data isn't used, so iCCP chunk can be ignored - * too. This allows the simplified API to be compiled without iCCP support, - * however if the support is there the chunk is still checked to detect - * errors (which are unfortunately quite common.) + * too. This allows the simplified API to be compiled without iCCP support. */ { static const png_byte chunks_to_process[] = { 98, 75, 71, 68, '\0', /* bKGD */ 99, 72, 82, 77, '\0', /* cHRM */ + 99, 73, 67, 80, '\0', /* cICP */ 103, 65, 77, 65, '\0', /* gAMA */ -# ifdef PNG_READ_iCCP_SUPPORTED - 105, 67, 67, 80, '\0', /* iCCP */ -# endif + 109, 68, 67, 86, '\0', /* mDCV */ 115, 66, 73, 84, '\0', /* sBIT */ 115, 82, 71, 66, '\0', /* sRGB */ - }; + }; /* Ignore unknown chunks and all other chunks except for the * IHDR, PLTE, tRNS, IDAT, and IEND chunks. @@ -1680,7 +1527,15 @@ png_image_skip_unused_chunks(png_structrp png_ptr) static void set_file_encoding(png_image_read_control *display) { - png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; + png_structrp png_ptr = display->image->opaque->png_ptr; + png_fixed_point g = png_resolve_file_gamma(png_ptr); + + /* PNGv3: the result may be 0 however the 'default_gamma' should have been + * set before this is called so zero is an error: + */ + if (g == 0) + png_error(png_ptr, "internal: default gamma not set"); + if (png_gamma_significant(g) != 0) { if (png_gamma_not_sRGB(g) != 0) @@ -2168,24 +2023,18 @@ png_image_read_colormap(png_voidp argument) /* Default the input file gamma if required - this is necessary because * libpng assumes that if no gamma information is present the data is in the * output format, but the simplified API deduces the gamma from the input - * format. + * format. The 'default' gamma value is also set by png_set_alpha_mode, but + * this is happening before any such call, so: + * + * TODO: should be an internal API and all this code should be copied into a + * single common gamma+colorspace file. */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) == 0) - { - /* Do this directly, not using the png_colorspace functions, to ensure - * that it happens even if the colorspace is invalid (though probably if - * it is the setting will be ignored) Note that the same thing can be - * achieved at the application interface with png_set_gAMA. - */ - if (png_ptr->bit_depth == 16 && - (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) - png_ptr->colorspace.gamma = PNG_GAMMA_LINEAR; + if (png_ptr->bit_depth == 16 && + (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) + png_ptr->default_gamma = PNG_GAMMA_LINEAR; - else - png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE; - - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - } + else + png_ptr->default_gamma = PNG_GAMMA_sRGB_INVERSE; /* Decide what to do based on the PNG color type of the input data. The * utility function png_create_colormap_entry deals with most aspects of the @@ -2563,6 +2412,8 @@ png_image_read_colormap(png_voidp argument) else { + const png_fixed_point gamma = png_resolve_file_gamma(png_ptr); + /* Either the input or the output has no alpha channel, so there * will be no non-opaque pixels in the color-map; it will just be * grayscale. @@ -2577,10 +2428,13 @@ png_image_read_colormap(png_voidp argument) * this case and doing it in the palette; this will result in * duplicate palette entries, but that's better than the * alternative of double gamma correction. + * + * NOTE: PNGv3: check the resolved result of all the potentially + * different colour space chunks. */ if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || png_ptr->num_trans > 0) && - png_gamma_not_sRGB(png_ptr->colorspace.gamma) != 0) + png_gamma_not_sRGB(gamma) != 0) { cmap_entries = (unsigned int)make_gray_file_colormap(display); data_encoding = P_FILE; @@ -2612,8 +2466,8 @@ png_image_read_colormap(png_voidp argument) if (output_encoding == P_sRGB) gray = png_sRGB_table[gray]; /* now P_LINEAR */ - gray = PNG_DIV257(png_gamma_16bit_correct(gray, - png_ptr->colorspace.gamma)); /* now P_FILE */ + gray = PNG_DIV257(png_gamma_16bit_correct(gray, gamma)); + /* now P_FILE */ /* And make sure the corresponding palette entry contains * exactly the required sRGB value. @@ -3275,6 +3129,54 @@ png_image_read_colormapped(png_voidp argument) } } +/* Row reading for interlaced 16-to-8 bit depth conversion with local buffer. */ +static int +png_image_read_direct_scaled(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_bytep local_row = png_voidcast(png_bytep, display->local_row); + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t row_bytes = display->row_bytes; + int passes; + + /* Handle interlacing. */ + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + png_error(png_ptr, "unknown interlace type"); + } + + /* Read each pass using local_row as intermediate buffer. */ + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep output_row = first_row; + + for (; y > 0; --y) + { + /* Read into local_row (gets transformed 8-bit data). */ + png_read_row(png_ptr, local_row, NULL); + + /* Copy from local_row to user buffer. */ + memcpy(output_row, local_row, (size_t)row_bytes); + output_row += row_bytes; + } + } + + return 1; +} + /* Just the row reading part of png_image_read. */ static int png_image_read_composite(png_voidp argument) @@ -3305,6 +3207,7 @@ png_image_read_composite(png_voidp argument) ptrdiff_t step_row = display->row_bytes; unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1; + int optimize_alpha = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0; int pass; for (pass = 0; pass < passes; ++pass) @@ -3361,20 +3264,44 @@ png_image_read_composite(png_voidp argument) if (alpha < 255) /* else just use component */ { - /* This is PNG_OPTIMIZED_ALPHA, the component value - * is a linear 8-bit value. Combine this with the - * current outrow[c] value which is sRGB encoded. - * Arithmetic here is 16-bits to preserve the output - * values correctly. - */ - component *= 257*255; /* =65535 */ - component += (255-alpha)*png_sRGB_table[outrow[c]]; + if (optimize_alpha != 0) + { + /* This is PNG_OPTIMIZED_ALPHA, the component value + * is a linear 8-bit value. Combine this with the + * current outrow[c] value which is sRGB encoded. + * Arithmetic here is 16-bits to preserve the output + * values correctly. + */ + component *= 257*255; /* =65535 */ + component += (255-alpha)*png_sRGB_table[outrow[c]]; - /* So 'component' is scaled by 255*65535 and is - * therefore appropriate for the sRGB to linear - * conversion table. - */ - component = PNG_sRGB_FROM_LINEAR(component); + /* Clamp to the valid range to defend against + * unforeseen cases where the data might be sRGB + * instead of linear premultiplied. + * (Belt-and-suspenders for CVE-2025-66293.) + */ + if (component > 255*65535) + component = 255*65535; + + /* So 'component' is scaled by 255*65535 and is + * therefore appropriate for the sRGB-to-linear + * conversion table. + */ + component = PNG_sRGB_FROM_LINEAR(component); + } + else + { + /* Compositing was already done on the palette + * entries. The data is sRGB premultiplied on black. + * Composite with the background in sRGB space. + * This is not gamma-correct, but matches what was + * done to the palette. + */ + png_uint_32 background = outrow[c]; + component += ((255-alpha) * background + 127) / 255; + if (component > 255) + component = 255; + } } outrow[c] = (png_byte)component; @@ -3693,6 +3620,7 @@ png_image_read_direct(png_voidp argument) int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; int do_local_compose = 0; int do_local_background = 0; /* to avoid double gamma correction bug */ + int do_local_scale = 0; /* for interlaced 16-to-8 bit conversion */ int passes = 0; /* Add transforms to ensure the correct output format is produced then check @@ -3744,6 +3672,12 @@ png_image_read_direct(png_voidp argument) /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise. */ { + /* This is safe but should no longer be necessary as + * png_ptr->default_gamma should have been set after the + * info-before-IDAT was read in png_image_read_header. + * + * TODO: 1.8: remove this and see what happens. + */ png_fixed_point input_gamma_default; if ((base_format & PNG_FORMAT_FLAG_LINEAR) != 0 && @@ -3799,8 +3733,9 @@ png_image_read_direct(png_voidp argument) * yet; it's set below. png_struct::gamma, however, is set to the * final value. */ - if (png_muldiv(>est, output_gamma, png_ptr->colorspace.gamma, - PNG_FP_1) != 0 && png_gamma_significant(gtest) == 0) + if (png_muldiv(>est, output_gamma, + png_resolve_file_gamma(png_ptr), PNG_FP_1) != 0 && + png_gamma_significant(gtest) == 0) do_local_background = 0; else if (mode == PNG_ALPHA_STANDARD) @@ -3819,8 +3754,16 @@ png_image_read_direct(png_voidp argument) png_set_expand_16(png_ptr); else /* 8-bit output */ + { png_set_scale_16(png_ptr); + /* For interlaced images, use local_row buffer to avoid overflow + * in png_combine_row() which writes using IHDR bit-depth. + */ + if (png_ptr->interlaced != 0) + do_local_scale = 1; + } + change &= ~PNG_FORMAT_FLAG_LINEAR; } @@ -4096,6 +4039,24 @@ png_image_read_direct(png_voidp argument) return result; } + else if (do_local_scale != 0) + { + /* For interlaced 16-to-8 conversion, use an intermediate row buffer + * to avoid buffer overflows in png_combine_row. The local_row is sized + * for the transformed (8-bit) output, preventing the overflow that would + * occur if png_combine_row wrote 16-bit data directly to the user buffer. + */ + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_direct_scaled, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + else { png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes; diff --git a/3rdparty/libpng/pngrio.c b/3rdparty/libpng/pngrio.c index 3b137f275f..b4a216161b 100644 --- a/3rdparty/libpng/pngrio.c +++ b/3rdparty/libpng/pngrio.c @@ -1,6 +1,6 @@ /* pngrio.c - functions for data input * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -56,7 +56,7 @@ png_default_read_data(png_structp png_ptr, png_bytep data, size_t length) /* fread() returns 0 on error, so it is OK to store this in a size_t * instead of an int, which is what fread() actually returns. */ - check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); + check = fread(data, 1, length, png_voidcast(FILE *, png_ptr->io_ptr)); if (check != length) png_error(png_ptr, "Read Error"); diff --git a/3rdparty/libpng/pngrtran.c b/3rdparty/libpng/pngrtran.c index 124906635b..507d11381e 100644 --- a/3rdparty/libpng/pngrtran.c +++ b/3rdparty/libpng/pngrtran.c @@ -1,6 +1,6 @@ /* pngrtran.c - transforms the data in a row for PNG readers * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -28,6 +28,12 @@ # endif #endif +#ifdef PNG_RISCV_RVV_IMPLEMENTATION +# if PNG_RISCV_RVV_IMPLEMENTATION == 1 +# define PNG_RISCV_RVV_INTRINSICS_AVAILABLE +# endif +#endif + #ifdef PNG_READ_SUPPORTED /* Set the action on getting a CRC error for an ancillary or critical chunk. */ @@ -218,9 +224,59 @@ png_set_strip_alpha(png_structrp png_ptr) #endif #if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED) +/* PNGv3 conformance: this private API exists to resolve the now mandatory error + * resolution when multiple conflicting sources of gamma or colour space + * information are available. + * + * Terminology (assuming power law, "gamma", encodings): + * "screen" gamma: a power law imposed by the output device when digital + * samples are converted to visible light output. The EOTF - volage to + * luminance on output. + * + * "file" gamma: a power law used to encode luminance levels from the input + * data (the scene or the mastering display system) into digital voltages. + * The OETF - luminance to voltage on input. + * + * gamma "correction": a power law matching the **inverse** of the overall + * transfer function from input luminance levels to output levels. The + * **inverse** of the OOTF; the correction "corrects" for the OOTF by aiming + * to make the overall OOTF (including the correction) linear. + * + * It is important to understand this terminology because the defined terms are + * scattered throughout the libpng code and it is very easy to end up with the + * inverse of the power law required. + * + * Variable and struct::member names: + * file_gamma OETF how the PNG data was encoded + * + * screen_gamma EOTF how the screen will decode digital levels + * + * -- not used -- OOTF the net effect OETF x EOTF + * gamma_correction the inverse of OOTF to make the result linear + * + * All versions of libpng require a call to "png_set_gamma" to establish the + * "screen" gamma, the power law representing the EOTF. png_set_gamma may also + * set or default the "file" gamma; the OETF. gamma_correction is calculated + * internally. + * + * The earliest libpng versions required file_gamma to be supplied to set_gamma. + * Later versions started allowing png_set_gamma and, later, png_set_alpha_mode, + * to cause defaulting from the file data. + * + * PNGv3 mandated a particular form for this defaulting, one that is compatible + * with what libpng did except that if libpng detected inconsistencies it marked + * all the chunks as "invalid". PNGv3 effectively invalidates this prior code. + * + * Behaviour implemented below: + * translate_gamma_flags(gamma, is_screen) + * The libpng-1.6 API for the gamma parameters to libpng APIs + * (png_set_gamma and png_set_alpha_mode at present). This allows the + * 'gamma' value to be passed as a png_fixed_point number or as one of a + * set of integral values for specific "well known" examples of transfer + * functions. This is compatible with PNGv3. + */ static png_fixed_point -translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma, - int is_screen) +translate_gamma_flags(png_fixed_point output_gamma, int is_screen) { /* Check for flag values. The main reason for having the old Mac value as a * flag is that it is pretty near impossible to work out what the correct @@ -230,14 +286,6 @@ translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma, if (output_gamma == PNG_DEFAULT_sRGB || output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB) { - /* If there is no sRGB support this just sets the gamma to the standard - * sRGB value. (This is a side effect of using this function!) - */ -# ifdef PNG_READ_sRGB_SUPPORTED - png_ptr->flags |= PNG_FLAG_ASSUME_sRGB; -# else - PNG_UNUSED(png_ptr) -# endif if (is_screen != 0) output_gamma = PNG_GAMMA_sRGB; else @@ -279,6 +327,33 @@ convert_gamma_value(png_structrp png_ptr, double output_gamma) return (png_fixed_point)output_gamma; } # endif + +static int +unsupported_gamma(png_structrp png_ptr, png_fixed_point gamma, int warn) +{ + /* Validate a gamma value to ensure it is in a reasonable range. The value + * is expected to be 1 or greater, but this range test allows for some + * viewing correction values. The intent is to weed out the API users + * who might use the inverse of the gamma value accidentally! + * + * 1.6.47: apply the test in png_set_gamma as well but only warn and return + * false if it fires. + * + * TODO: 1.8: make this an app_error in png_set_gamma as well. + */ + if (gamma < PNG_LIB_GAMMA_MIN || gamma > PNG_LIB_GAMMA_MAX) + { +# define msg "gamma out of supported range" + if (warn) + png_app_warning(png_ptr, msg); + else + png_app_error(png_ptr, msg); + return 1; +# undef msg + } + + return 0; +} #endif /* READ_ALPHA_MODE || READ_GAMMA */ #ifdef PNG_READ_ALPHA_MODE_SUPPORTED @@ -286,31 +361,29 @@ void PNGFAPI png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, png_fixed_point output_gamma) { - int compose = 0; png_fixed_point file_gamma; + int compose = 0; png_debug(1, "in png_set_alpha_mode_fixed"); if (png_rtran_ok(png_ptr, 0) == 0) return; - output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/); - - /* Validate the value to ensure it is in a reasonable range. The value - * is expected to be 1 or greater, but this range test allows for some - * viewing correction values. The intent is to weed out the API users - * who might use the inverse of the gamma value accidentally! - * - * In libpng 1.6.0, we changed from 0.07..3 to 0.01..100, to accommodate - * the optimal 16-bit gamma of 36 and its reciprocal. - */ - if (output_gamma < 1000 || output_gamma > 10000000) - png_error(png_ptr, "output gamma out of expected range"); + output_gamma = translate_gamma_flags(output_gamma, 1/*screen*/); + if (unsupported_gamma(png_ptr, output_gamma, 0/*error*/)) + return; /* The default file gamma is the inverse of the output gamma; the output - * gamma may be changed below so get the file value first: + * gamma may be changed below so get the file value first. The default_gamma + * is set here and from the simplified API (which uses a different algorithm) + * so don't overwrite a set value: */ - file_gamma = png_reciprocal(output_gamma); + file_gamma = png_ptr->default_gamma; + if (file_gamma == 0) + { + file_gamma = png_reciprocal(output_gamma); + png_ptr->default_gamma = file_gamma; + } /* There are really 8 possibilities here, composed of any combination * of: @@ -361,17 +434,7 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, png_error(png_ptr, "invalid alpha mode"); } - /* Only set the default gamma if the file gamma has not been set (this has - * the side effect that the gamma in a second call to png_set_alpha_mode will - * be ignored.) - */ - if (png_ptr->colorspace.gamma == 0) - { - png_ptr->colorspace.gamma = file_gamma; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - } - - /* But always set the output gamma: */ + /* Set the screen gamma values: */ png_ptr->screen_gamma = output_gamma; /* Finally, if pre-multiplying, set the background fields to achieve the @@ -381,7 +444,7 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, { /* And obtain alpha pre-multiplication by composing on black: */ memset(&png_ptr->background, 0, (sizeof png_ptr->background)); - png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */ + png_ptr->background_gamma = file_gamma; /* just in case */ png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE; png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; @@ -438,9 +501,19 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, { int i; + /* Initialize the array to index colors. + * + * Ensure quantize_index can fit 256 elements (PNG_MAX_PALETTE_LENGTH) + * rather than num_palette elements. This is to prevent buffer overflows + * caused by malformed PNG files with out-of-range palette indices. + * + * Be careful to avoid leaking memory. Applications are allowed to call + * this function more than once per png_struct. + */ + png_free(png_ptr, png_ptr->quantize_index); png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, - (png_alloc_size_t)num_palette); - for (i = 0; i < num_palette; i++) + PNG_MAX_PALETTE_LENGTH); + for (i = 0; i < PNG_MAX_PALETTE_LENGTH; i++) png_ptr->quantize_index[i] = (png_byte)i; } @@ -452,15 +525,14 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, * Perhaps not the best solution, but good enough. */ - int i; + png_bytep quantize_sort; + int i, j; - /* Initialize an array to sort colors */ - png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, + /* Initialize the local array to sort colors. */ + quantize_sort = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)num_palette); - - /* Initialize the quantize_sort array */ for (i = 0; i < num_palette; i++) - png_ptr->quantize_sort[i] = (png_byte)i; + quantize_sort[i] = (png_byte)i; /* Find the least used palette entries by starting a * bubble sort, and running it until we have sorted @@ -472,19 +544,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = num_palette - 1; i >= maximum_colors; i--) { int done; /* To stop early if the list is pre-sorted */ - int j; done = 1; for (j = 0; j < i; j++) { - if (histogram[png_ptr->quantize_sort[j]] - < histogram[png_ptr->quantize_sort[j + 1]]) + if (histogram[quantize_sort[j]] + < histogram[quantize_sort[j + 1]]) { png_byte t; - t = png_ptr->quantize_sort[j]; - png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; - png_ptr->quantize_sort[j + 1] = t; + t = quantize_sort[j]; + quantize_sort[j] = quantize_sort[j + 1]; + quantize_sort[j + 1] = t; done = 0; } } @@ -496,18 +567,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, /* Swap the palette around, and set up a table, if necessary */ if (full_quantize != 0) { - int j = num_palette; + j = num_palette; /* Put all the useful colors within the max, but don't * move the others. */ for (i = 0; i < maximum_colors; i++) { - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); palette[i] = palette[j]; } @@ -515,7 +586,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } else { - int j = num_palette; + j = num_palette; /* Move all the used colors inside the max limit, and * develop a translation table. @@ -523,13 +594,13 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = 0; i < maximum_colors; i++) { /* Only move the colors we need to */ - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { png_color tmp_color; do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); tmp_color = palette[j]; palette[j] = palette[i]; @@ -567,8 +638,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } } } - png_free(png_ptr, png_ptr->quantize_sort); - png_ptr->quantize_sort = NULL; + png_free(png_ptr, quantize_sort); } else { @@ -819,8 +889,8 @@ png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, return; /* New in libpng-1.5.4 - reserve particular negative values as flags. */ - scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/); - file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/); + scrn_gamma = translate_gamma_flags(scrn_gamma, 1/*screen*/); + file_gamma = translate_gamma_flags(file_gamma, 0/*file*/); /* Checking the gamma values for being >0 was added in 1.5.4 along with the * premultiplied alpha support; this actually hides an undocumented feature @@ -834,17 +904,19 @@ png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, * libpng-1.6.0. */ if (file_gamma <= 0) - png_error(png_ptr, "invalid file gamma in png_set_gamma"); - + png_app_error(png_ptr, "invalid file gamma in png_set_gamma"); if (scrn_gamma <= 0) - png_error(png_ptr, "invalid screen gamma in png_set_gamma"); + png_app_error(png_ptr, "invalid screen gamma in png_set_gamma"); - /* Set the gamma values unconditionally - this overrides the value in the PNG - * file if a gAMA chunk was present. png_set_alpha_mode provides a - * different, easier, way to default the file gamma. + if (unsupported_gamma(png_ptr, file_gamma, 1/*warn*/) || + unsupported_gamma(png_ptr, scrn_gamma, 1/*warn*/)) + return; + + /* 1.6.47: png_struct::file_gamma and png_struct::screen_gamma are now only + * written by this API. This removes dependencies on the order of API calls + * and allows the complex gamma checks to be delayed until needed. */ - png_ptr->colorspace.gamma = file_gamma; - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + png_ptr->file_gamma = file_gamma; png_ptr->screen_gamma = scrn_gamma; } @@ -1022,26 +1094,9 @@ png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, png_ptr->rgb_to_gray_coefficients_set = 1; } - else - { - if (red >= 0 && green >= 0) - png_app_warning(png_ptr, - "ignoring out of range rgb_to_gray coefficients"); - - /* Use the defaults, from the cHRM chunk if set, else the historical - * values which are close to the sRGB/HDTV/ITU-Rec 709 values. See - * png_do_rgb_to_gray for more discussion of the values. In this case - * the coefficients are not marked as 'set' and are not overwritten if - * something has already provided a default. - */ - if (png_ptr->rgb_to_gray_red_coeff == 0 && - png_ptr->rgb_to_gray_green_coeff == 0) - { - png_ptr->rgb_to_gray_red_coeff = 6968; - png_ptr->rgb_to_gray_green_coeff = 23434; - /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ - } - } + else if (red >= 0 && green >= 0) + png_app_warning(png_ptr, + "ignoring out of range rgb_to_gray coefficients"); } } @@ -1282,6 +1337,80 @@ png_init_rgb_transformations(png_structrp png_ptr) #endif /* READ_EXPAND && READ_BACKGROUND */ } +#ifdef PNG_READ_GAMMA_SUPPORTED +png_fixed_point /* PRIVATE */ +png_resolve_file_gamma(png_const_structrp png_ptr) +{ + png_fixed_point file_gamma; + + /* The file gamma is determined by these precedence rules, in this order + * (i.e. use the first value found): + * + * png_set_gamma; png_struct::file_gammma if not zero, then: + * png_struct::chunk_gamma if not 0 (determined the PNGv3 rules), then: + * png_set_gamma; 1/png_struct::screen_gamma if not zero + * + * 0 (i.e. do no gamma handling) + */ + file_gamma = png_ptr->file_gamma; + if (file_gamma != 0) + return file_gamma; + + file_gamma = png_ptr->chunk_gamma; + if (file_gamma != 0) + return file_gamma; + + file_gamma = png_ptr->default_gamma; + if (file_gamma != 0) + return file_gamma; + + /* If png_reciprocal oveflows it returns 0 which indicates to the caller that + * there is no usable file gamma. (The checks added to png_set_gamma and + * png_set_alpha_mode should prevent a screen_gamma which would overflow.) + */ + if (png_ptr->screen_gamma != 0) + file_gamma = png_reciprocal(png_ptr->screen_gamma); + + return file_gamma; +} + +static int +png_init_gamma_values(png_structrp png_ptr) +{ + /* The following temporary indicates if overall gamma correction is + * required. + */ + int gamma_correction = 0; + png_fixed_point file_gamma, screen_gamma; + + /* Resolve the file_gamma. See above: if png_ptr::screen_gamma is set + * file_gamma will always be set here: + */ + file_gamma = png_resolve_file_gamma(png_ptr); + screen_gamma = png_ptr->screen_gamma; + + if (file_gamma > 0) /* file has been set */ + { + if (screen_gamma > 0) /* screen set too */ + gamma_correction = png_gamma_threshold(file_gamma, screen_gamma); + + else + /* Assume the output matches the input; a long time default behavior + * of libpng, although the standard has nothing to say about this. + */ + screen_gamma = png_reciprocal(file_gamma); + } + + else /* both unset, prevent corrections: */ + file_gamma = screen_gamma = PNG_FP_1; + + png_ptr->file_gamma = file_gamma; + png_ptr->screen_gamma = screen_gamma; + return gamma_correction; + +} +#endif /* READ_GAMMA */ + void /* PRIVATE */ png_init_read_transformations(png_structrp png_ptr) { @@ -1301,59 +1430,22 @@ png_init_read_transformations(png_structrp png_ptr) * the test needs to be performed later - here. In addition prior to 1.5.4 * the tests were repeated for the PALETTE color type here - this is no * longer necessary (and doesn't seem to have been necessary before.) + * + * PNGv3: the new mandatory precedence/priority rules for colour space chunks + * are handled here (by calling the above function). + * + * Turn the gamma transformation on or off as appropriate. Notice that + * PNG_GAMMA just refers to the file->screen correction. Alpha composition + * may independently cause gamma correction because it needs linear data + * (e.g. if the file has a gAMA chunk but the screen gamma hasn't been + * specified.) In any case this flag may get turned off in the code + * immediately below if the transform can be handled outside the row loop. */ - { - /* The following temporary indicates if overall gamma correction is - * required. - */ - int gamma_correction = 0; + if (png_init_gamma_values(png_ptr) != 0) + png_ptr->transformations |= PNG_GAMMA; - if (png_ptr->colorspace.gamma != 0) /* has been set */ - { - if (png_ptr->screen_gamma != 0) /* screen set too */ - gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma, - png_ptr->screen_gamma); - - else - /* Assume the output matches the input; a long time default behavior - * of libpng, although the standard has nothing to say about this. - */ - png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma); - } - - else if (png_ptr->screen_gamma != 0) - /* The converse - assume the file matches the screen, note that this - * perhaps undesirable default can (from 1.5.4) be changed by calling - * png_set_alpha_mode (even if the alpha handling mode isn't required - * or isn't changed from the default.) - */ - png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma); - - else /* neither are set */ - /* Just in case the following prevents any processing - file and screen - * are both assumed to be linear and there is no way to introduce a - * third gamma value other than png_set_background with 'UNIQUE', and, - * prior to 1.5.4 - */ - png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1; - - /* We have a gamma value now. */ - png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; - - /* Now turn the gamma transformation on or off as appropriate. Notice - * that PNG_GAMMA just refers to the file->screen correction. Alpha - * composition may independently cause gamma correction because it needs - * linear data (e.g. if the file has a gAMA chunk but the screen gamma - * hasn't been specified.) In any case this flag may get turned off in - * the code immediately below if the transform can be handled outside the - * row loop. - */ - if (gamma_correction != 0) - png_ptr->transformations |= PNG_GAMMA; - - else - png_ptr->transformations &= ~PNG_GAMMA; - } + else + png_ptr->transformations &= ~PNG_GAMMA; #endif /* Certain transformations have the effect of preventing other @@ -1425,7 +1517,7 @@ png_init_read_transformations(png_structrp png_ptr) * appropriately. */ if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0) - png_colorspace_set_rgb_coefficients(png_ptr); + png_set_rgb_coefficients(png_ptr); #endif #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED @@ -1568,10 +1660,10 @@ png_init_read_transformations(png_structrp png_ptr) */ if ((png_ptr->transformations & PNG_GAMMA) != 0 || ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0 && - (png_gamma_significant(png_ptr->colorspace.gamma) != 0 || + (png_gamma_significant(png_ptr->file_gamma) != 0 || png_gamma_significant(png_ptr->screen_gamma) != 0)) || ((png_ptr->transformations & PNG_COMPOSE) != 0 && - (png_gamma_significant(png_ptr->colorspace.gamma) != 0 || + (png_gamma_significant(png_ptr->file_gamma) != 0 || png_gamma_significant(png_ptr->screen_gamma) != 0 # ifdef PNG_READ_BACKGROUND_SUPPORTED || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE && @@ -1627,8 +1719,8 @@ png_init_read_transformations(png_structrp png_ptr) break; case PNG_BACKGROUND_GAMMA_FILE: - g = png_reciprocal(png_ptr->colorspace.gamma); - gs = png_reciprocal2(png_ptr->colorspace.gamma, + g = png_reciprocal(png_ptr->file_gamma); + gs = png_reciprocal2(png_ptr->file_gamma, png_ptr->screen_gamma); break; @@ -1689,19 +1781,51 @@ png_init_read_transformations(png_structrp png_ptr) } else /* if (png_ptr->trans_alpha[i] != 0xff) */ { - png_byte v, w; + if ((png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0) + { + /* Premultiply only: + * component = round((component * alpha) / 255) + */ + png_uint_32 component; - v = png_ptr->gamma_to_1[palette[i].red]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); - palette[i].red = png_ptr->gamma_from_1[w]; + component = png_ptr->gamma_to_1[palette[i].red]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].red = png_ptr->gamma_from_1[component]; - v = png_ptr->gamma_to_1[palette[i].green]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); - palette[i].green = png_ptr->gamma_from_1[w]; + component = png_ptr->gamma_to_1[palette[i].green]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].green = png_ptr->gamma_from_1[component]; - v = png_ptr->gamma_to_1[palette[i].blue]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); - palette[i].blue = png_ptr->gamma_from_1[w]; + component = png_ptr->gamma_to_1[palette[i].blue]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].blue = png_ptr->gamma_from_1[component]; + } + else + { + /* Composite with background color: + * component = + * alpha * component + (1 - alpha) * background + */ + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } } } else @@ -1719,6 +1843,7 @@ png_init_read_transformations(png_structrp png_ptr) * transformations elsewhere. */ png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA); + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; } /* color_type == PNG_COLOR_TYPE_PALETTE */ /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ @@ -1736,8 +1861,8 @@ png_init_read_transformations(png_structrp png_ptr) break; case PNG_BACKGROUND_GAMMA_FILE: - g = png_reciprocal(png_ptr->colorspace.gamma); - gs = png_reciprocal2(png_ptr->colorspace.gamma, + g = png_reciprocal(png_ptr->file_gamma); + gs = png_reciprocal2(png_ptr->file_gamma, png_ptr->screen_gamma); break; @@ -1987,11 +2112,11 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr) * been called before this from png_read_update_info->png_read_start_row * sometimes does the gamma transform and cancels the flag. * - * TODO: this looks wrong; the info_ptr should end up with a gamma equal to - * the screen_gamma value. The following probably results in weirdness if - * the info_ptr is used by the app after the rows have been read. + * TODO: this is confusing. It only changes the result of png_get_gAMA and, + * yes, it does return the value that the transformed data effectively has + * but does any app really understand this? */ - info_ptr->colorspace.gamma = png_ptr->colorspace.gamma; + info_ptr->gamma = png_ptr->file_gamma; #endif if (info_ptr->bit_depth == 16) @@ -4924,13 +5049,8 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) #ifdef PNG_READ_QUANTIZE_SUPPORTED if ((png_ptr->transformations & PNG_QUANTIZE) != 0) - { png_do_quantize(row_info, png_ptr->row_buf + 1, png_ptr->palette_lookup, png_ptr->quantize_index); - - if (row_info->rowbytes == 0) - png_error(png_ptr, "png_do_quantize returned rowbytes=0"); - } #endif /* READ_QUANTIZE */ #ifdef PNG_READ_EXPAND_16_SUPPORTED diff --git a/3rdparty/libpng/pngrutil.c b/3rdparty/libpng/pngrutil.c index 7c609b4b48..e7c7bbe48e 100644 --- a/3rdparty/libpng/pngrutil.c +++ b/3rdparty/libpng/pngrutil.c @@ -1,6 +1,6 @@ /* pngrutil.c - utilities to read a PNG file * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -17,6 +17,11 @@ #ifdef PNG_READ_SUPPORTED +/* The minimum 'zlib' stream is assumed to be just the 2 byte header, 5 bytes + * minimum 'deflate' stream, and the 4 byte checksum. + */ +#define LZ77Min (2U+5U+4U) + #ifdef PNG_READ_INTERLACING_SUPPORTED /* Arrays to facilitate interlacing - use pass (0 - 6) as index. */ @@ -43,30 +48,6 @@ png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf) return uval; } -#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED) -/* The following is a variation on the above for use with the fixed - * point values used for gAMA and cHRM. Instead of png_error it - * issues a warning and returns (-1) - an invalid value because both - * gAMA and cHRM use *unsigned* integers for fixed point values. - */ -#define PNG_FIXED_ERROR (-1) - -static png_fixed_point /* PRIVATE */ -png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf) -{ - png_uint_32 uval = png_get_uint_32(buf); - - if (uval <= PNG_UINT_31_MAX) - return (png_fixed_point)uval; /* known to be in range */ - - /* The caller can turn off the warning by passing NULL. */ - if (png_ptr != NULL) - png_warning(png_ptr, "PNG fixed point integer out of range"); - - return PNG_FIXED_ERROR; -} -#endif - #ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED /* NOTE: the read macros will obscure these definitions, so that if * PNG_USE_READ_MACROS is set the library will not use them internally, @@ -163,6 +144,38 @@ png_read_sig(png_structrp png_ptr, png_inforp info_ptr) png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; } +/* This function is called to verify that a chunk name is valid. + * Do this using the bit-whacking approach from contrib/tools/pngfix.c + * + * Copied from libpng 1.7. + */ +static int +check_chunk_name(png_uint_32 name) +{ + png_uint_32 t; + + /* Remove bit 5 from all but the reserved byte; this means + * every 8-bit unit must be in the range 65-90 to be valid. + * So bit 5 must be zero, bit 6 must be set and bit 7 zero. + */ + name &= ~PNG_U32(32,32,0,32); + t = (name & ~0x1f1f1f1fU) ^ 0x40404040U; + + /* Subtract 65 for each 8-bit quantity, this must not + * overflow and each byte must then be in the range 0-25. + */ + name -= PNG_U32(65,65,65,65); + t |= name; + + /* Subtract 26, handling the overflow which should set the + * top three bits of each byte. + */ + name -= PNG_U32(25,25,25,26); + t |= ~name; + + return (t & 0xe0e0e0e0U) == 0U; +} + /* Read the chunk header (length + type name). * Put the type name into png_ptr->chunk_name, and return the length. */ @@ -170,33 +183,36 @@ png_uint_32 /* PRIVATE */ png_read_chunk_header(png_structrp png_ptr) { png_byte buf[8]; - png_uint_32 length; + png_uint_32 chunk_name, length; #ifdef PNG_IO_STATE_SUPPORTED png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; #endif - /* Read the length and the chunk name. - * This must be performed in a single I/O call. + /* Read the length and the chunk name. png_struct::chunk_name is immediately + * updated even if they are detectably wrong. This aids error message + * handling by allowing png_chunk_error to be used. */ png_read_data(png_ptr, buf, 8); length = png_get_uint_31(png_ptr, buf); - - /* Put the chunk name into png_ptr->chunk_name. */ - png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4); - - png_debug2(0, "Reading chunk typeid = 0x%lx, length = %lu", - (unsigned long)png_ptr->chunk_name, (unsigned long)length); + png_ptr->chunk_name = chunk_name = PNG_CHUNK_FROM_STRING(buf+4); /* Reset the crc and run it over the chunk name. */ png_reset_crc(png_ptr); png_calculate_crc(png_ptr, buf + 4, 4); - /* Check to see if chunk name is valid. */ - png_check_chunk_name(png_ptr, png_ptr->chunk_name); + png_debug2(0, "Reading chunk typeid = 0x%lx, length = %lu", + (unsigned long)png_ptr->chunk_name, (unsigned long)length); - /* Check for too-large chunk length */ - png_check_chunk_length(png_ptr, length); + /* Sanity check the length (first by <= 0x80) and the chunk name. An error + * here indicates a broken stream and libpng has no recovery from this. + */ + if (buf[0] >= 0x80U) + png_chunk_error(png_ptr, "bad header (invalid length)"); + + /* Check to see if chunk name is valid. */ + if (!check_chunk_name(chunk_name)) + png_chunk_error(png_ptr, "bad header (invalid type)"); #ifdef PNG_IO_STATE_SUPPORTED png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; @@ -216,59 +232,43 @@ png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) png_calculate_crc(png_ptr, buf, length); } -/* Optionally skip data and then check the CRC. Depending on whether we - * are reading an ancillary or critical chunk, and how the program has set - * things up, we may calculate the CRC on the data and print a message. - * Returns '1' if there was a CRC error, '0' otherwise. - */ -int /* PRIVATE */ -png_crc_finish(png_structrp png_ptr, png_uint_32 skip) -{ - /* The size of the local buffer for inflate is a good guess as to a - * reasonable size to use for buffering reads from the application. - */ - while (skip > 0) - { - png_uint_32 len; - png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; - - len = (sizeof tmpbuf); - if (len > skip) - len = skip; - skip -= len; - - png_crc_read(png_ptr, tmpbuf, len); - } - - if (png_crc_error(png_ptr) != 0) - { - if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ? - (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0 : - (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE) != 0) - { - png_chunk_warning(png_ptr, "CRC error"); - } - - else - png_chunk_error(png_ptr, "CRC error"); - - return 1; - } - - return 0; -} - /* Compare the CRC stored in the PNG file with that calculated by libpng from * the data it has read thus far. */ -int /* PRIVATE */ -png_crc_error(png_structrp png_ptr) +static int +png_crc_error(png_structrp png_ptr, int handle_as_ancillary) { png_byte crc_bytes[4]; png_uint_32 crc; int need_crc = 1; - if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0) + /* There are four flags two for ancillary and two for critical chunks. The + * default setting of these flags is all zero. + * + * PNG_FLAG_CRC_ANCILLARY_USE + * PNG_FLAG_CRC_ANCILLARY_NOWARN + * USE+NOWARN: no CRC calculation (implemented here), else; + * NOWARN: png_chunk_error on error (implemented in png_crc_finish) + * else: png_chunk_warning on error (implemented in png_crc_finish) + * This is the default. + * + * I.e. NOWARN without USE produces png_chunk_error. The default setting + * where neither are set does the same thing. + * + * PNG_FLAG_CRC_CRITICAL_USE + * PNG_FLAG_CRC_CRITICAL_IGNORE + * IGNORE: no CRC calculation (implemented here), else; + * USE: png_chunk_warning on error (implemented in png_crc_finish) + * else: png_chunk_error on error (implemented in png_crc_finish) + * This is the default. + * + * This arose because of original mis-implementation and has persisted for + * compatibility reasons. + * + * TODO: the flag names are internal so maybe this can be changed to + * something comprehensible. + */ + if (handle_as_ancillary || PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0) { if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) @@ -298,21 +298,87 @@ png_crc_error(png_structrp png_ptr) return 0; } +/* Optionally skip data and then check the CRC. Depending on whether we + * are reading an ancillary or critical chunk, and how the program has set + * things up, we may calculate the CRC on the data and print a message. + * Returns '1' if there was a CRC error, '0' otherwise. + * + * There is one public version which is used in most places and another which + * takes the value for the 'critical' flag to check. This allows PLTE and IEND + * handling code to ignore the CRC error and removes some confusing code + * duplication. + */ +static int +png_crc_finish_critical(png_structrp png_ptr, png_uint_32 skip, + int handle_as_ancillary) +{ + /* The size of the local buffer for inflate is a good guess as to a + * reasonable size to use for buffering reads from the application. + */ + while (skip > 0) + { + png_uint_32 len; + png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; + + len = (sizeof tmpbuf); + if (len > skip) + len = skip; + skip -= len; + + png_crc_read(png_ptr, tmpbuf, len); + } + + /* If 'handle_as_ancillary' has been requested and this is a critical chunk + * but PNG_FLAG_CRC_CRITICAL_IGNORE was set then png_read_crc did not, in + * fact, calculate the CRC so the ANCILLARY settings should not be used + * instead. + */ + if (handle_as_ancillary && + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0) + handle_as_ancillary = 0; + + /* TODO: this might be more comprehensible if png_crc_error was inlined here. + */ + if (png_crc_error(png_ptr, handle_as_ancillary) != 0) + { + /* See above for the explanation of how the flags work. */ + if (handle_as_ancillary || PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ? + (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0 : + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE) != 0) + png_chunk_warning(png_ptr, "CRC error"); + + else + png_chunk_error(png_ptr, "CRC error"); + + return 1; + } + + return 0; +} + +int /* PRIVATE */ +png_crc_finish(png_structrp png_ptr, png_uint_32 skip) +{ + return png_crc_finish_critical(png_ptr, skip, 0/*critical handling*/); +} + #if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\ defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_sCAL_SUPPORTED) ||\ defined(PNG_READ_sPLT_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) ||\ - defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_SEQUENTIAL_READ_SUPPORTED) + defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_eXIf_SUPPORTED) ||\ + defined(PNG_SEQUENTIAL_READ_SUPPORTED) /* Manage the read buffer; this simply reallocates the buffer if it is not small * enough (or if it is not allocated). The routine returns a pointer to the * buffer; if an error occurs and 'warn' is set the routine returns NULL, else - * it will call png_error (via png_malloc) on failure. (warn == 2 means - * 'silent'). + * it will call png_error on failure. */ static png_bytep -png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) +png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size) { png_bytep buffer = png_ptr->read_buffer; + if (new_size > png_chunk_max(png_ptr)) return NULL; + if (buffer != NULL && new_size > png_ptr->read_buffer_size) { png_ptr->read_buffer = NULL; @@ -327,24 +393,17 @@ png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) if (buffer != NULL) { - memset(buffer, 0, new_size); /* just in case */ +# ifndef PNG_NO_MEMZERO /* for detecting UIM bugs **only** */ + memset(buffer, 0, new_size); /* just in case */ +# endif png_ptr->read_buffer = buffer; png_ptr->read_buffer_size = new_size; } - - else if (warn < 2) /* else silent */ - { - if (warn != 0) - png_chunk_warning(png_ptr, "insufficient memory to read chunk"); - - else - png_chunk_error(png_ptr, "insufficient memory to read chunk"); - } } return buffer; } -#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */ +#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|eXIf|SEQUENTIAL_READ */ /* png_inflate_claim: claim the zstream for some nefarious purpose that involves * decompression. Returns Z_OK on success, else a zlib error code. It checks @@ -631,16 +690,7 @@ png_decompress_chunk(png_structrp png_ptr, * maybe a '\0' terminator too. We have to assume that 'prefix_size' is * limited only by the maximum chunk size. */ - png_alloc_size_t limit = PNG_SIZE_MAX; - -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < limit) - limit = png_ptr->user_chunk_malloc_max; -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - if (PNG_USER_CHUNK_MALLOC_MAX < limit) - limit = PNG_USER_CHUNK_MALLOC_MAX; -# endif + png_alloc_size_t limit = png_chunk_max(png_ptr); if (limit >= prefix_size + (terminate != 0)) { @@ -845,9 +895,9 @@ png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, } #endif /* READ_iCCP */ +/* CHUNK HANDLING */ /* Read and check the IDHR chunk */ - -void /* PRIVATE */ +static png_handle_result_code png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[13]; @@ -857,12 +907,7 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_IHDR"); - if ((png_ptr->mode & PNG_HAVE_IHDR) != 0) - png_chunk_error(png_ptr, "out of place"); - - /* Check the length */ - if (length != 13) - png_chunk_error(png_ptr, "invalid"); + /* Length and position are checked by the caller. */ png_ptr->mode |= PNG_HAVE_IHDR; @@ -916,257 +961,196 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); png_debug1(3, "channels = %d", png_ptr->channels); png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes); + + /* Rely on png_set_IHDR to completely validate the data and call png_error if + * it's wrong. + */ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, compression_type, filter_type); + + return handled_ok; + PNG_UNUSED(length) } /* Read and check the palette */ -void /* PRIVATE */ +/* TODO: there are several obvious errors in this code when handling + * out-of-place chunks and there is much over-complexity caused by trying to + * patch up the problems. + */ +static png_handle_result_code png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_color palette[PNG_MAX_PALETTE_LENGTH]; - int max_palette_length, num, i; -#ifdef PNG_POINTER_INDEXING_SUPPORTED - png_colorp pal_ptr; -#endif + png_const_charp errmsg = NULL; png_debug(1, "in png_handle_PLTE"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - /* Moved to before the 'after IDAT' check below because otherwise duplicate - * PLTE chunks are potentially ignored (the spec says there shall not be more - * than one PLTE, the error is not treated as benign, so this check trumps - * the requirement that PLTE appears before IDAT.) + /* 1.6.47: consistency. This used to be especially treated as a critical + * error even in an image which is not colour mapped, there isn't a good + * justification for treating some errors here one way and others another so + * everything uses the same logic. */ - else if ((png_ptr->mode & PNG_HAVE_PLTE) != 0) - png_chunk_error(png_ptr, "duplicate"); + if ((png_ptr->mode & PNG_HAVE_PLTE) != 0) + errmsg = "duplicate"; else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - /* This is benign because the non-benign error happened before, when an - * IDAT was encountered in a color-mapped image with no PLTE. - */ - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } + errmsg = "out of place"; - png_ptr->mode |= PNG_HAVE_PLTE; + else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) + errmsg = "ignored in grayscale PNG"; - if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "ignored in grayscale PNG"); - return; - } + else if (length > 3*PNG_MAX_PALETTE_LENGTH || (length % 3) != 0) + errmsg = "invalid"; -#ifndef PNG_READ_OPT_PLTE_SUPPORTED - if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - { - png_crc_finish(png_ptr, length); - return; - } -#endif - - if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) - { - png_crc_finish(png_ptr, length); - - if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - png_chunk_benign_error(png_ptr, "invalid"); - - else - png_chunk_error(png_ptr, "invalid"); - - return; - } - - /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */ - num = (int)length / 3; - - /* If the palette has 256 or fewer entries but is too large for the bit - * depth, we don't issue an error, to preserve the behavior of previous - * libpng versions. We silently truncate the unused extra palette entries - * here. + /* This drops PLTE in favour of tRNS or bKGD because both of those chunks + * can have an effect on the rendering of the image whereas PLTE only matters + * in the case of an 8-bit display with a decoder which controls the palette. + * + * The alternative here is to ignore the error and store the palette anyway; + * destroying the tRNS will definately cause problems. + * + * NOTE: the case of PNG_COLOR_TYPE_PALETTE need not be considered because + * the png_handle_ routines for the three 'after PLTE' chunks tRNS, bKGD and + * hIST all check for a preceding PLTE in these cases. */ - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - max_palette_length = (1 << png_ptr->bit_depth); + else if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE && + (png_has_chunk(png_ptr, tRNS) || png_has_chunk(png_ptr, bKGD))) + errmsg = "out of place"; + else - max_palette_length = PNG_MAX_PALETTE_LENGTH; - - if (num > max_palette_length) - num = max_palette_length; - -#ifdef PNG_POINTER_INDEXING_SUPPORTED - for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) { - png_byte buf[3]; - - png_crc_read(png_ptr, buf, 3); - pal_ptr->red = buf[0]; - pal_ptr->green = buf[1]; - pal_ptr->blue = buf[2]; - } -#else - for (i = 0; i < num; i++) - { - png_byte buf[3]; - - png_crc_read(png_ptr, buf, 3); - /* Don't depend upon png_color being any order */ - palette[i].red = buf[0]; - palette[i].green = buf[1]; - palette[i].blue = buf[2]; - } -#endif - - /* If we actually need the PLTE chunk (ie for a paletted image), we do - * whatever the normal CRC configuration tells us. However, if we - * have an RGB image, the PLTE can be considered ancillary, so - * we will act as though it is. - */ -#ifndef PNG_READ_OPT_PLTE_SUPPORTED - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) -#endif - { - png_crc_finish(png_ptr, (png_uint_32) (length - (unsigned int)num * 3)); - } - -#ifndef PNG_READ_OPT_PLTE_SUPPORTED - else if (png_crc_error(png_ptr) != 0) /* Only if we have a CRC error */ - { - /* If we don't want to use the data from an ancillary chunk, - * we have two options: an error abort, or a warning and we - * ignore the data in this chunk (which should be OK, since - * it's considered ancillary for a RGB or RGBA image). - * - * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the - * chunk type to determine whether to check the ancillary or the critical - * flags. + /* If the palette has 256 or fewer entries but is too large for the bit + * depth we don't issue an error to preserve the behavior of previous + * libpng versions. We silently truncate the unused extra palette entries + * here. */ - if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE) == 0) - { - if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) != 0) - return; + const unsigned max_palette_length = + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ? + 1U << png_ptr->bit_depth : PNG_MAX_PALETTE_LENGTH; - else - png_chunk_error(png_ptr, "CRC error"); + /* The cast is safe because 'length' is less than + * 3*PNG_MAX_PALETTE_LENGTH + */ + const unsigned num = (length > 3U*max_palette_length) ? + max_palette_length : (unsigned)length / 3U; + + unsigned i, j; + png_byte buf[3*PNG_MAX_PALETTE_LENGTH]; + png_color palette[PNG_MAX_PALETTE_LENGTH]; + + /* Read the chunk into the buffer then read to the end of the chunk. */ + png_crc_read(png_ptr, buf, num*3U); + png_crc_finish_critical(png_ptr, length - 3U*num, + /* Handle as ancillary if PLTE is optional: */ + png_ptr->color_type != PNG_COLOR_TYPE_PALETTE); + + for (i = 0U, j = 0U; i < num; i++) + { + palette[i].red = buf[j++]; + palette[i].green = buf[j++]; + palette[i].blue = buf[j++]; } - /* Otherwise, we (optionally) emit a warning and use the chunk. */ - else if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0) - png_chunk_warning(png_ptr, "CRC error"); - } -#endif + /* A valid PLTE chunk has been read */ + png_ptr->mode |= PNG_HAVE_PLTE; - /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its - * own copy of the palette. This has the side effect that when png_start_row - * is called (this happens after any call to png_read_update_info) the - * info_ptr palette gets changed. This is extremely unexpected and - * confusing. - * - * Fix this by not sharing the palette in this way. - */ - png_set_PLTE(png_ptr, info_ptr, palette, num); - - /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before - * IDAT. Prior to 1.6.0 this was not checked; instead the code merely - * checked the apparent validity of a tRNS chunk inserted before PLTE on a - * palette PNG. 1.6.0 attempts to rigorously follow the standard and - * therefore does a benign error if the erroneous condition is detected *and* - * cancels the tRNS if the benign error returns. The alternative is to - * amend the standard since it would be rather hypocritical of the standards - * maintainers to ignore it. - */ -#ifdef PNG_READ_tRNS_SUPPORTED - if (png_ptr->num_trans > 0 || - (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)) - { - /* Cancel this because otherwise it would be used if the transforms - * require it. Don't cancel the 'valid' flag because this would prevent - * detection of duplicate chunks. + /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to + * its own copy of the palette. This has the side effect that when + * png_start_row is called (this happens after any call to + * png_read_update_info) the info_ptr palette gets changed. This is + * extremely unexpected and confusing. + * + * REVIEW: there have been consistent bugs in the past about gamma and + * similar transforms to colour mapped images being useless because the + * modified palette cannot be accessed because of the above. + * + * CONSIDER: Fix this by not sharing the palette in this way. But does + * this completely fix the problem? */ - png_ptr->num_trans = 0; - - if (info_ptr != NULL) - info_ptr->num_trans = 0; - - png_chunk_benign_error(png_ptr, "tRNS must be after"); + png_set_PLTE(png_ptr, info_ptr, palette, num); + return handled_ok; } -#endif -#ifdef PNG_READ_hIST_SUPPORTED - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) - png_chunk_benign_error(png_ptr, "hIST must be after"); -#endif + /* Here on error: errmsg is non NULL. */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + png_chunk_error(png_ptr, errmsg); + } -#ifdef PNG_READ_bKGD_SUPPORTED - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) - png_chunk_benign_error(png_ptr, "bKGD must be after"); -#endif + else /* not critical to this image */ + { + png_crc_finish_critical(png_ptr, length, 1/*handle as ancillary*/); + png_chunk_benign_error(png_ptr, errmsg); + } + + /* Because PNG_UNUSED(errmsg) does not work if all the uses are compiled out + * (this does happen). + */ + return errmsg != NULL ? handled_error : handled_error; } -void /* PRIVATE */ +/* On read the IDAT chunk is always handled specially, even if marked for + * unknown handling (this is allowed), so: + */ +#define png_handle_IDAT NULL + +static png_handle_result_code png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_debug(1, "in png_handle_IEND"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0 || - (png_ptr->mode & PNG_HAVE_IDAT) == 0) - png_chunk_error(png_ptr, "out of place"); - png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); - png_crc_finish(png_ptr, length); - if (length != 0) png_chunk_benign_error(png_ptr, "invalid"); + png_crc_finish_critical(png_ptr, length, 1/*handle as ancillary*/); + + return handled_ok; PNG_UNUSED(info_ptr) } #ifdef PNG_READ_gAMA_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_fixed_point igamma; + png_uint_32 ugamma; png_byte buf[4]; png_debug(1, "in png_handle_gAMA"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 4) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, buf, 4); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; - igamma = png_get_fixed_point(NULL, buf); + ugamma = png_get_uint_32(buf); - png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma); - png_colorspace_sync(png_ptr, info_ptr); + if (ugamma > PNG_UINT_31_MAX) + { + png_chunk_benign_error(png_ptr, "invalid"); + return handled_error; + } + + png_set_gAMA_fixed(png_ptr, info_ptr, (png_fixed_point)/*SAFE*/ugamma); + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* PNGv3: chunk precedence for gamma is cICP, [iCCP], sRGB, gAMA. gAMA is + * at the end of the chain so simply check for an unset value. + */ + if (png_ptr->chunk_gamma == 0) + png_ptr->chunk_gamma = (png_fixed_point)/*SAFE*/ugamma; +#endif /*READ_GAMMA*/ + + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_gAMA NULL #endif #ifdef PNG_READ_sBIT_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { unsigned int truelen, i; @@ -1175,23 +1159,6 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_sBIT"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { truelen = 3; @@ -1204,25 +1171,25 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) sample_depth = png_ptr->bit_depth; } - if (length != truelen || length > 4) + if (length != truelen) { - png_chunk_benign_error(png_ptr, "invalid"); png_crc_finish(png_ptr, length); - return; + png_chunk_benign_error(png_ptr, "bad length"); + return handled_error; } buf[0] = buf[1] = buf[2] = buf[3] = sample_depth; png_crc_read(png_ptr, buf, truelen); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; for (i=0; i sample_depth) { png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } } @@ -1234,7 +1201,7 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_ptr->sig_bit.alpha = buf[3]; } - else + else /* grayscale */ { png_ptr->sig_bit.gray = buf[0]; png_ptr->sig_bit.red = buf[0]; @@ -1244,133 +1211,132 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); + return handled_ok; } +#else +# define png_handle_sBIT NULL #endif #ifdef PNG_READ_cHRM_SUPPORTED -void /* PRIVATE */ +static png_int_32 +png_get_int_32_checked(png_const_bytep buf, int *error) +{ + png_uint_32 uval = png_get_uint_32(buf); + if ((uval & 0x80000000) == 0) /* non-negative */ + return (png_int_32)uval; + + uval = (uval ^ 0xffffffff) + 1; /* 2's complement: -x = ~x+1 */ + if ((uval & 0x80000000) == 0) /* no overflow */ + return -(png_int_32)uval; + + /* This version of png_get_int_32 has a way of returning the error to the + * caller, so: + */ + *error = 1; + return 0; /* Safe */ +} + +static png_handle_result_code /* PRIVATE */ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_byte buf[32]; + int error = 0; png_xy xy; + png_byte buf[32]; png_debug(1, "in png_handle_cHRM"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 32) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, buf, 32); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; - xy.whitex = png_get_fixed_point(NULL, buf); - xy.whitey = png_get_fixed_point(NULL, buf + 4); - xy.redx = png_get_fixed_point(NULL, buf + 8); - xy.redy = png_get_fixed_point(NULL, buf + 12); - xy.greenx = png_get_fixed_point(NULL, buf + 16); - xy.greeny = png_get_fixed_point(NULL, buf + 20); - xy.bluex = png_get_fixed_point(NULL, buf + 24); - xy.bluey = png_get_fixed_point(NULL, buf + 28); + xy.whitex = png_get_int_32_checked(buf + 0, &error); + xy.whitey = png_get_int_32_checked(buf + 4, &error); + xy.redx = png_get_int_32_checked(buf + 8, &error); + xy.redy = png_get_int_32_checked(buf + 12, &error); + xy.greenx = png_get_int_32_checked(buf + 16, &error); + xy.greeny = png_get_int_32_checked(buf + 20, &error); + xy.bluex = png_get_int_32_checked(buf + 24, &error); + xy.bluey = png_get_int_32_checked(buf + 28, &error); - if (xy.whitex == PNG_FIXED_ERROR || - xy.whitey == PNG_FIXED_ERROR || - xy.redx == PNG_FIXED_ERROR || - xy.redy == PNG_FIXED_ERROR || - xy.greenx == PNG_FIXED_ERROR || - xy.greeny == PNG_FIXED_ERROR || - xy.bluex == PNG_FIXED_ERROR || - xy.bluey == PNG_FIXED_ERROR) + if (error) { - png_chunk_benign_error(png_ptr, "invalid values"); - return; + png_chunk_benign_error(png_ptr, "invalid"); + return handled_error; } - /* If a colorspace error has already been output skip this chunk */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) - return; + /* png_set_cHRM may complain about some of the values but this doesn't matter + * because it was a cHRM and it did have vaguely (if, perhaps, ridiculous) + * values. Ridiculousity will be checked if the values are used later. + */ + png_set_cHRM_fixed(png_ptr, info_ptr, xy.whitex, xy.whitey, xy.redx, xy.redy, + xy.greenx, xy.greeny, xy.bluex, xy.bluey); - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0) - { - png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; - png_colorspace_sync(png_ptr, info_ptr); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } + /* We only use 'chromaticities' for RGB to gray */ +# ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* There is no need to check sRGB here, cICP is NYI and iCCP is not + * supported so just check mDCV. + */ + if (!png_has_chunk(png_ptr, mDCV)) + { + png_ptr->chromaticities = xy; + } +# endif /* READ_RGB_TO_GRAY */ - png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; - (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy, - 1/*prefer cHRM values*/); - png_colorspace_sync(png_ptr, info_ptr); + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_cHRM NULL #endif #ifdef PNG_READ_sRGB_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte intent; png_debug(1, "in png_handle_sRGB"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - if (length != 1) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, &intent, 1); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; - /* If a colorspace error has already been output skip this chunk */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) - return; - - /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect - * this. + /* This checks the range of the "rendering intent" because it is specified in + * the PNG spec itself; the "reserved" values will result in the chunk not + * being accepted, just as they do with the various "reserved" values in + * IHDR. */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) != 0) + if (intent > 3/*PNGv3 spec*/) { - png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; - png_colorspace_sync(png_ptr, info_ptr); - png_chunk_benign_error(png_ptr, "too many profiles"); - return; + png_chunk_benign_error(png_ptr, "invalid"); + return handled_error; } - (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent); - png_colorspace_sync(png_ptr, info_ptr); + png_set_sRGB(png_ptr, info_ptr, intent); + /* NOTE: png_struct::chromaticities is not set here because the RGB to gray + * coefficients are known without a need for the chromaticities. + */ + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* PNGv3: chunk precedence for gamma is cICP, [iCCP], sRGB, gAMA. iCCP is + * not supported by libpng so the only requirement is to check for cICP + * setting the gamma (this is NYI, but this check is safe.) + */ + if (!png_has_chunk(png_ptr, cICP) || png_ptr->chunk_gamma == 0) + png_ptr->chunk_gamma = PNG_GAMMA_sRGB_INVERSE; +#endif /*READ_GAMMA*/ + + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_sRGB NULL #endif /* READ_sRGB */ #ifdef PNG_READ_iCCP_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Note: this does not properly handle profiles that are > 64K under DOS */ { @@ -1379,44 +1345,10 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_iCCP"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - /* Consistent with all the above colorspace handling an obviously *invalid* - * chunk is just ignored, so does not invalidate the color space. An - * alternative is to set the 'invalid' flags at the start of this routine - * and only clear them in they were not set before and all the tests pass. + /* PNGv3: allow PNG files with both sRGB and iCCP because the PNG spec only + * ever said that there "should" be only one, not "shall" and the PNGv3 + * colour chunk precedence rules give a handling for this case anyway. */ - - /* The keyword must be at least one character and there is a - * terminator (0) byte and the compression method byte, and the - * 'zlib' datastream is at least 11 bytes. - */ - if (length < 14) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too short"); - return; - } - - /* If a colorspace error has already been output skip this chunk */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0) - { - png_crc_finish(png_ptr, length); - return; - } - - /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect - * this. - */ - if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0) { uInt read_length, keyword_length; char keyword[81]; @@ -1426,19 +1358,16 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ read_length = 81; /* maximum */ if (read_length > length) - read_length = (uInt)length; + read_length = (uInt)/*SAFE*/length; png_crc_read(png_ptr, (png_bytep)keyword, read_length); length -= read_length; - /* The minimum 'zlib' stream is assumed to be just the 2 byte header, - * 5 bytes minimum 'deflate' stream, and the 4 byte checksum. - */ - if (length < 11) + if (length < LZ77Min) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "too short"); - return; + return handled_error; } keyword_length = 0; @@ -1475,15 +1404,14 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ png_uint_32 profile_length = png_get_uint_32(profile_header); - if (png_icc_check_length(png_ptr, &png_ptr->colorspace, - keyword, profile_length) != 0) + if (png_icc_check_length(png_ptr, keyword, profile_length) != + 0) { /* The length is apparently ok, so we can check the 132 * byte header. */ - if (png_icc_check_header(png_ptr, &png_ptr->colorspace, - keyword, profile_length, profile_header, - png_ptr->color_type) != 0) + if (png_icc_check_header(png_ptr, keyword, profile_length, + profile_header, png_ptr->color_type) != 0) { /* Now read the tag table; a variable size buffer is * needed at this point, allocate one for the whole @@ -1493,7 +1421,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_uint_32 tag_count = png_get_uint_32(profile_header + 128); png_bytep profile = png_read_buffer(png_ptr, - profile_length, 2/*silent*/); + profile_length); if (profile != NULL) { @@ -1512,8 +1440,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (size == 0) { if (png_icc_check_tag_table(png_ptr, - &png_ptr->colorspace, keyword, profile_length, - profile) != 0) + keyword, profile_length, profile) != 0) { /* The profile has been validated for basic * security issues, so read the whole thing in. @@ -1545,13 +1472,6 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_crc_finish(png_ptr, length); finished = 1; -# if defined(PNG_sRGB_SUPPORTED) && PNG_sRGB_PROFILE_CHECKS >= 0 - /* Check for a match against sRGB */ - png_icc_set_sRGB(png_ptr, - &png_ptr->colorspace, profile, - png_ptr->zstream.adler); -# endif - /* Steal the profile for info_ptr. */ if (info_ptr != NULL) { @@ -1574,11 +1494,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } else - { - png_ptr->colorspace.flags |= - PNG_COLORSPACE_INVALID; errmsg = "out of memory"; - } } /* else the profile remains in the read @@ -1586,13 +1502,10 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * chunks. */ - if (info_ptr != NULL) - png_colorspace_sync(png_ptr, info_ptr); - if (errmsg == NULL) { png_ptr->zowner = 0; - return; + return handled_ok; } } if (errmsg == NULL) @@ -1633,22 +1546,21 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) errmsg = "bad keyword"; } - else - errmsg = "too many profiles"; - /* Failure: the reason is in 'errmsg' */ if (finished == 0) png_crc_finish(png_ptr, length); - png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; - png_colorspace_sync(png_ptr, info_ptr); if (errmsg != NULL) /* else already output */ png_chunk_benign_error(png_ptr, errmsg); + + return handled_error; } +#else +# define png_handle_iCCP NULL #endif /* READ_iCCP */ #ifdef PNG_READ_sPLT_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Note: this does not properly handle chunks that are > 64K under DOS */ { @@ -1669,43 +1581,24 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); - return; + return handled_error; } if (--png_ptr->user_chunk_cache_max == 1) { png_warning(png_ptr, "No space in chunk cache for sPLT"); png_crc_finish(png_ptr, length); - return; + return handled_error; } } #endif - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - -#ifdef PNG_MAX_MALLOC_64K - if (length > 65535U) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too large to fit in memory"); - return; - } -#endif - - buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } @@ -1716,7 +1609,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, skip) != 0) - return; + return handled_error; buffer[length] = 0; @@ -1729,7 +1622,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length < 2U || entry_start > buffer + (length - 2U)) { png_warning(png_ptr, "malformed sPLT chunk"); - return; + return handled_error; } new_palette.depth = *entry_start++; @@ -1743,7 +1636,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if ((data_length % (unsigned int)entry_size) != 0) { png_warning(png_ptr, "sPLT chunk has bad length"); - return; + return handled_error; } dl = (png_uint_32)(data_length / (unsigned int)entry_size); @@ -1752,7 +1645,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (dl > max_dl) { png_warning(png_ptr, "sPLT chunk too long"); - return; + return handled_error; } new_palette.nentries = (png_int_32)(data_length / (unsigned int)entry_size); @@ -1763,10 +1656,9 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (new_palette.entries == NULL) { png_warning(png_ptr, "sPLT chunk requires too much memory"); - return; + return handled_error; } -#ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0; i < new_palette.nentries; i++) { pp = new_palette.entries + i; @@ -1789,31 +1681,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) pp->frequency = png_get_uint_16(entry_start); entry_start += 2; } -#else - pp = new_palette.entries; - - for (i = 0; i < new_palette.nentries; i++) - { - - if (new_palette.depth == 8) - { - pp[i].red = *entry_start++; - pp[i].green = *entry_start++; - pp[i].blue = *entry_start++; - pp[i].alpha = *entry_start++; - } - - else - { - pp[i].red = png_get_uint_16(entry_start); entry_start += 2; - pp[i].green = png_get_uint_16(entry_start); entry_start += 2; - pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; - pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; - } - - pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2; - } -#endif /* Discard all chunk data except the name and stash that */ new_palette.name = (png_charp)buffer; @@ -1821,34 +1688,20 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); png_free(png_ptr, new_palette.entries); + return handled_ok; } +#else +# define png_handle_sPLT NULL #endif /* READ_sPLT */ #ifdef PNG_READ_tRNS_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; png_debug(1, "in png_handle_tRNS"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) { png_byte buf[2]; @@ -1857,7 +1710,7 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_crc_read(png_ptr, buf, 2); @@ -1873,7 +1726,7 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_crc_read(png_ptr, buf, length); @@ -1887,10 +1740,9 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { if ((png_ptr->mode & PNG_HAVE_PLTE) == 0) { - /* TODO: is this actually an error in the ISO spec? */ png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of place"); - return; + return handled_error; } if (length > (unsigned int) png_ptr->num_palette || @@ -1899,7 +1751,7 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_crc_read(png_ptr, readbuf, length); @@ -1910,13 +1762,13 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid with alpha channel"); - return; + return handled_error; } if (png_crc_finish(png_ptr, 0) != 0) { png_ptr->num_trans = 0; - return; + return handled_error; } /* TODO: this is a horrible side effect in the palette case because the @@ -1925,11 +1777,14 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, &(png_ptr->trans_color)); + return handled_ok; } +#else +# define png_handle_tRNS NULL #endif #ifdef PNG_READ_bKGD_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { unsigned int truelen; @@ -1938,27 +1793,17 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_bKGD"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 || - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - (png_ptr->mode & PNG_HAVE_PLTE) == 0)) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if ((png_ptr->mode & PNG_HAVE_PLTE) == 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return handled_error; + } + truelen = 1; + } else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0) truelen = 6; @@ -1970,13 +1815,13 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_crc_read(png_ptr, buf, truelen); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; /* We convert the index value into RGB components so that we can allow * arbitrary RGB values for background when we have transparency, and @@ -1992,7 +1837,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (buf[0] >= info_ptr->num_palette) { png_chunk_benign_error(png_ptr, "invalid index"); - return; + return handled_error; } background.red = (png_uint_16)png_ptr->palette[buf[0]].red; @@ -2013,7 +1858,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (buf[0] != 0 || buf[1] >= (unsigned int)(1 << png_ptr->bit_depth)) { png_chunk_benign_error(png_ptr, "invalid gray level"); - return; + return handled_error; } } @@ -2031,7 +1876,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (buf[0] != 0 || buf[2] != 0 || buf[4] != 0) { png_chunk_benign_error(png_ptr, "invalid color"); - return; + return handled_error; } } @@ -2043,116 +1888,174 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } png_set_bKGD(png_ptr, info_ptr, &background); + return handled_ok; } +#else +# define png_handle_bKGD NULL #endif #ifdef PNG_READ_cICP_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_cICP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[4]; png_debug(1, "in png_handle_cICP"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cICP) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - else if (length != 4) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, buf, 4); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; png_set_cICP(png_ptr, info_ptr, buf[0], buf[1], buf[2], buf[3]); + + /* We only use 'chromaticities' for RGB to gray */ +# ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if (!png_has_chunk(png_ptr, mDCV)) + { + /* TODO: png_ptr->chromaticities = chromaticities; */ + } +# endif /* READ_RGB_TO_GRAY */ + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* PNGv3: chunk precedence for gamma is cICP, [iCCP], sRGB, gAMA. cICP is + * at the head so simply set the gamma if it can be determined. If not + * chunk_gamma remains unchanged; sRGB and gAMA handling check it for + * being zero. + */ + /* TODO: set png_struct::chunk_gamma when possible */ +#endif /*READ_GAMMA*/ + + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_cICP NULL +#endif + +#ifdef PNG_READ_cLLI_SUPPORTED +static png_handle_result_code /* PRIVATE */ +png_handle_cLLI(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[8]; + + png_debug(1, "in png_handle_cLLI"); + + png_crc_read(png_ptr, buf, 8); + + if (png_crc_finish(png_ptr, 0) != 0) + return handled_error; + + /* The error checking happens here, this puts it in just one place: */ + png_set_cLLI_fixed(png_ptr, info_ptr, png_get_uint_32(buf), + png_get_uint_32(buf+4)); + return handled_ok; + PNG_UNUSED(length) +} +#else +# define png_handle_cLLI NULL +#endif + +#ifdef PNG_READ_mDCV_SUPPORTED +static png_handle_result_code /* PRIVATE */ +png_handle_mDCV(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_xy chromaticities; + png_byte buf[24]; + + png_debug(1, "in png_handle_mDCV"); + + png_crc_read(png_ptr, buf, 24); + + if (png_crc_finish(png_ptr, 0) != 0) + return handled_error; + + /* The error checking happens here, this puts it in just one place. The + * odd /50000 scaling factor makes it more difficult but the (x.y) values are + * only two bytes so a <<1 is safe. + * + * WARNING: the PNG specification defines the cHRM chunk to **start** with + * the white point (x,y). The W3C PNG v3 specification puts the white point + * **after* R,G,B. The x,y values in mDCV are also scaled by 50,000 and + * stored in just two bytes, whereas those in cHRM are scaled by 100,000 and + * stored in four bytes. This is very, very confusing. These APIs remove + * the confusion by copying the existing, well established, API. + */ + chromaticities.redx = png_get_uint_16(buf+ 0U) << 1; /* red x */ + chromaticities.redy = png_get_uint_16(buf+ 2U) << 1; /* red y */ + chromaticities.greenx = png_get_uint_16(buf+ 4U) << 1; /* green x */ + chromaticities.greeny = png_get_uint_16(buf+ 6U) << 1; /* green y */ + chromaticities.bluex = png_get_uint_16(buf+ 8U) << 1; /* blue x */ + chromaticities.bluey = png_get_uint_16(buf+10U) << 1; /* blue y */ + chromaticities.whitex = png_get_uint_16(buf+12U) << 1; /* white x */ + chromaticities.whitey = png_get_uint_16(buf+14U) << 1; /* white y */ + + png_set_mDCV_fixed(png_ptr, info_ptr, + chromaticities.whitex, chromaticities.whitey, + chromaticities.redx, chromaticities.redy, + chromaticities.greenx, chromaticities.greeny, + chromaticities.bluex, chromaticities.bluey, + png_get_uint_32(buf+16U), /* peak luminance */ + png_get_uint_32(buf+20U));/* minimum perceivable luminance */ + + /* We only use 'chromaticities' for RGB to gray */ +# ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_ptr->chromaticities = chromaticities; +# endif /* READ_RGB_TO_GRAY */ + + return handled_ok; + PNG_UNUSED(length) +} +#else +# define png_handle_mDCV NULL #endif #ifdef PNG_READ_eXIf_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - unsigned int i; + png_bytep buffer = NULL; png_debug(1, "in png_handle_eXIf"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); + buffer = png_read_buffer(png_ptr, length); - if (length < 2) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too short"); - return; - } - - else if (info_ptr == NULL || (info_ptr->valid & PNG_INFO_eXIf) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - info_ptr->free_me |= PNG_FREE_EXIF; - - info_ptr->eXIf_buf = png_voidcast(png_bytep, - png_malloc_warn(png_ptr, length)); - - if (info_ptr->eXIf_buf == NULL) + if (buffer == NULL) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } - for (i = 0; i < length; i++) + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, 0) != 0) + return handled_error; + + /* PNGv3: the code used to check the byte order mark at the start for MM or + * II, however PNGv3 states that the the first 4 bytes should be checked. + * The caller ensures that there are four bytes available. + */ { - png_byte buf[1]; - png_crc_read(png_ptr, buf, 1); - info_ptr->eXIf_buf[i] = buf[0]; - if (i == 1) + png_uint_32 header = png_get_uint_32(buffer); + + /* These numbers are copied from the PNGv3 spec: */ + if (header != 0x49492A00 && header != 0x4D4D002A) { - if ((buf[0] != 'M' && buf[0] != 'I') || - (info_ptr->eXIf_buf[0] != buf[0])) - { - png_crc_finish(png_ptr, length - 2); - png_chunk_benign_error(png_ptr, "incorrect byte-order specifier"); - png_free(png_ptr, info_ptr->eXIf_buf); - info_ptr->eXIf_buf = NULL; - return; - } + png_chunk_benign_error(png_ptr, "invalid"); + return handled_error; } } - if (png_crc_finish(png_ptr, 0) == 0) - png_set_eXIf_1(png_ptr, info_ptr, length, info_ptr->eXIf_buf); - - png_free(png_ptr, info_ptr->eXIf_buf); - info_ptr->eXIf_buf = NULL; + png_set_eXIf_1(png_ptr, info_ptr, length, buffer); + return handled_ok; } +#else +# define png_handle_eXIf NULL #endif #ifdef PNG_READ_hIST_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { unsigned int num, i; @@ -2160,25 +2063,13 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_hIST"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 || - (png_ptr->mode & PNG_HAVE_PLTE) == 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - num = length / 2 ; + /* This cast is safe because the chunk definition limits the length to a + * maximum of 1024 bytes. + * + * TODO: maybe use png_uint_32 anyway, not unsigned int, to reduce the + * casts. + */ + num = (unsigned int)length / 2 ; if (length != num * 2 || num != (unsigned int)png_ptr->num_palette || @@ -2186,7 +2077,7 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } for (i = 0; i < num; i++) @@ -2198,14 +2089,17 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; png_set_hIST(png_ptr, info_ptr, readbuf); + return handled_ok; } +#else +# define png_handle_hIST NULL #endif #ifdef PNG_READ_pHYs_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[9]; @@ -2214,44 +2108,24 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_pHYs"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - if (length != 9) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, buf, 9); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; res_x = png_get_uint_32(buf); res_y = png_get_uint_32(buf + 4); unit_type = buf[8]; png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_pHYs NULL #endif #ifdef PNG_READ_oFFs_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[9]; @@ -2260,45 +2134,25 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_oFFs"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - if (length != 9) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, buf, 9); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; offset_x = png_get_int_32(buf); offset_y = png_get_int_32(buf + 4); unit_type = buf[8]; png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_oFFs NULL #endif #ifdef PNG_READ_pCAL_SUPPORTED /* Read the pCAL chunk (described in the PNG Extensions document) */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_int_32 X0, X1; @@ -2308,40 +2162,22 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) int i; png_debug(1, "in png_handle_pCAL"); - - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)", length + 1); - buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; buffer[length] = 0; /* Null terminate the last string */ @@ -2357,7 +2193,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (endptr - buf <= 12) { png_chunk_benign_error(png_ptr, "invalid"); - return; + return handled_error; } png_debug(3, "Reading pCAL X0, X1, type, nparams, and units"); @@ -2377,7 +2213,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) { png_chunk_benign_error(png_ptr, "invalid parameter count"); - return; + return handled_error; } else if (type >= PNG_EQUATION_LAST) @@ -2396,7 +2232,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (params == NULL) { png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } /* Get pointers to the start of each parameter string. */ @@ -2414,20 +2250,29 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_free(png_ptr, params); png_chunk_benign_error(png_ptr, "invalid data"); - return; + return handled_error; } } png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams, (png_charp)units, params); + /* TODO: BUG: png_set_pCAL calls png_chunk_report which, in this case, calls + * png_benign_error and that can error out. + * + * png_read_buffer needs to be allocated with space for both nparams and the + * parameter strings. Not hard to do. + */ png_free(png_ptr, params); + return handled_ok; } +#else +# define png_handle_pCAL NULL #endif #ifdef PNG_READ_sCAL_SUPPORTED /* Read the sCAL chunk */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_bytep buffer; @@ -2435,55 +2280,29 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) int state; png_debug(1, "in png_handle_sCAL"); - - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "out of place"); - return; - } - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - - /* Need unit type, width, \0, height: minimum 4 bytes */ - else if (length < 4) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)", length + 1); - buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { - png_chunk_benign_error(png_ptr, "out of memory"); png_crc_finish(png_ptr, length); - return; + png_chunk_benign_error(png_ptr, "out of memory"); + return handled_error; } png_crc_read(png_ptr, buffer, length); buffer[length] = 0; /* Null terminate the last string */ if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; /* Validate the unit. */ if (buffer[0] != 1 && buffer[0] != 2) { png_chunk_benign_error(png_ptr, "invalid unit"); - return; + return handled_error; } /* Validate the ASCII numbers, need two ASCII numbers separated by @@ -2512,15 +2331,22 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_chunk_benign_error(png_ptr, "non-positive height"); else + { /* This is the (only) success case. */ png_set_sCAL_s(png_ptr, info_ptr, buffer[0], (png_charp)buffer+1, (png_charp)buffer+heighti); + return handled_ok; + } } + + return handled_error; } +#else +# define png_handle_sCAL NULL #endif #ifdef PNG_READ_tIME_SUPPORTED -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[7]; @@ -2528,30 +2354,17 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_tIME"); - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) != 0) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate"); - return; - } - + /* TODO: what is this doing here? It should be happened in pngread.c and + * pngpread.c, although it could be moved to png_handle_chunk below and + * thereby avoid some code duplication. + */ if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) png_ptr->mode |= PNG_AFTER_IDAT; - if (length != 7) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid"); - return; - } - png_crc_read(png_ptr, buf, 7); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; mod_time.second = buf[6]; mod_time.minute = buf[5]; @@ -2561,12 +2374,16 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) mod_time.year = png_get_uint_16(buf); png_set_tIME(png_ptr, info_ptr, &mod_time); + return handled_ok; + PNG_UNUSED(length) } +#else +# define png_handle_tIME NULL #endif #ifdef PNG_READ_tEXt_SUPPORTED /* Note: this does not properly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_text text_info; @@ -2583,45 +2400,31 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); - return; + return handled_error; } if (--png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "no space in chunk cache"); - return; + return handled_error; } } #endif - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - -#ifdef PNG_MAX_MALLOC_64K - if (length > 65535U) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "too large to fit in memory"); - return; - } -#endif - - buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { + png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, skip) != 0) - return; + return handled_error; key = (png_charp)buffer; key[length] = 0; @@ -2640,14 +2443,19 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) text_info.text = text; text_info.text_length = strlen(text); - if (png_set_text_2(png_ptr, info_ptr, &text_info, 1) != 0) - png_warning(png_ptr, "Insufficient memory to process text chunk"); + if (png_set_text_2(png_ptr, info_ptr, &text_info, 1) == 0) + return handled_ok; + + png_chunk_benign_error(png_ptr, "out of memory"); + return handled_error; } +#else +# define png_handle_tEXt NULL #endif #ifdef PNG_READ_zTXt_SUPPORTED /* Note: this does not correctly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_const_charp errmsg = NULL; @@ -2662,40 +2470,35 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); - return; + return handled_error; } if (--png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "no space in chunk cache"); - return; + return handled_error; } } #endif - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - /* Note, "length" is sufficient here; we won't be adding - * a null terminator later. + * a null terminator later. The limit check in png_handle_chunk should be + * sufficient. */ - buffer = png_read_buffer(png_ptr, length, 2/*silent*/); + buffer = png_read_buffer(png_ptr, length); if (buffer == NULL) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; /* TODO: also check that the keyword contents match the spec! */ for (keyword_length = 0; @@ -2748,8 +2551,10 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) text.lang = NULL; text.lang_key = NULL; - if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0) - errmsg = "insufficient memory"; + if (png_set_text_2(png_ptr, info_ptr, &text, 1) == 0) + return handled_ok; + + errmsg = "out of memory"; } } @@ -2757,14 +2562,16 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) errmsg = png_ptr->zstream.msg; } - if (errmsg != NULL) - png_chunk_benign_error(png_ptr, errmsg); + png_chunk_benign_error(png_ptr, errmsg); + return handled_error; } +#else +# define png_handle_zTXt NULL #endif #ifdef PNG_READ_iTXt_SUPPORTED /* Note: this does not correctly handle chunks that are > 64K under DOS */ -void /* PRIVATE */ +static png_handle_result_code /* PRIVATE */ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_const_charp errmsg = NULL; @@ -2779,37 +2586,31 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); - return; + return handled_error; } if (--png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "no space in chunk cache"); - return; + return handled_error; } } #endif - if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) - png_chunk_error(png_ptr, "missing IHDR"); - - if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - - buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of memory"); - return; + return handled_error; } png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, 0) != 0) - return; + return handled_error; /* First the keyword. */ for (prefix_length=0; @@ -2899,8 +2700,10 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) text.text_length = 0; text.itxt_length = uncompressed_length; - if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0) - errmsg = "insufficient memory"; + if (png_set_text_2(png_ptr, info_ptr, &text, 1) == 0) + return handled_ok; + + errmsg = "out of memory"; } } @@ -2909,7 +2712,10 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (errmsg != NULL) png_chunk_benign_error(png_ptr, errmsg); + return handled_error; } +#else +# define png_handle_iTXt NULL #endif #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED @@ -2917,7 +2723,7 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) static int png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) { - png_alloc_size_t limit = PNG_SIZE_MAX; + const png_alloc_size_t limit = png_chunk_max(png_ptr); if (png_ptr->unknown_chunk.data != NULL) { @@ -2925,16 +2731,6 @@ png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) png_ptr->unknown_chunk.data = NULL; } -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < limit) - limit = png_ptr->user_chunk_malloc_max; - -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - if (PNG_USER_CHUNK_MALLOC_MAX < limit) - limit = PNG_USER_CHUNK_MALLOC_MAX; -# endif - if (length <= limit) { PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name); @@ -2973,11 +2769,11 @@ png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) #endif /* READ_UNKNOWN_CHUNKS */ /* Handle an unknown, or known but disabled, chunk */ -void /* PRIVATE */ +png_handle_result_code /*PRIVATE*/ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length, int keep) { - int handled = 0; /* the chunk was handled */ + png_handle_result_code handled = handled_discarded; /* the default */ png_debug(1, "in png_handle_unknown"); @@ -3024,7 +2820,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, * error at this point unless it is to be saved. * positive: The chunk was handled, libpng will ignore/discard it. */ - if (ret < 0) + if (ret < 0) /* handled_error */ png_chunk_error(png_ptr, "error in user chunk"); else if (ret == 0) @@ -3058,7 +2854,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, else /* chunk was handled */ { - handled = 1; + handled = handled_ok; /* Critical chunks can be safely discarded at this point. */ keep = PNG_HANDLE_CHUNK_NEVER; } @@ -3143,7 +2939,7 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, */ png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); - handled = 1; + handled = handled_saved; # ifdef PNG_USER_LIMITS_SUPPORTED break; } @@ -3169,79 +2965,267 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, #endif /* !READ_UNKNOWN_CHUNKS */ /* Check for unhandled critical chunks */ - if (handled == 0 && PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) + if (handled < handled_saved && PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) png_chunk_error(png_ptr, "unhandled critical chunk"); + + return handled; } -/* This function is called to verify that a chunk name is valid. - * This function can't have the "critical chunk check" incorporated - * into it, since in the future we will need to be able to call user - * functions to handle unknown critical chunks after we check that - * the chunk name itself is valid. - */ - -/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is: +/* APNG handling: the minimal implementation of APNG handling in libpng 1.6 + * requires that those significant applications which already handle APNG not + * get hosed. To do this ensure the code here will have to ensure than APNG + * data by default (at least in 1.6) gets stored in the unknown chunk list. + * Maybe this can be relaxed in a few years but at present it's just the only + * safe way. * - * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + * ATM just cause unknown handling for all three chunks: */ +#define png_handle_acTL NULL +#define png_handle_fcTL NULL +#define png_handle_fdAT NULL -void /* PRIVATE */ -png_check_chunk_name(png_const_structrp png_ptr, png_uint_32 chunk_name) +/* + * 1.6.47: This is the new table driven interface to all the chunk handling. + * + * The table describes the PNG standard rules for **reading** known chunks - + * every chunk which has an entry in PNG_KNOWN_CHUNKS. The table contains an + * entry for each PNG_INDEX_cHNK describing the rules. + * + * In this initial version the only information in the entry is the + * png_handle_cHNK function for the chunk in question. When chunk support is + * compiled out the entry will be NULL. + */ +static const struct { - int i; - png_uint_32 cn=chunk_name; + png_handle_result_code (*handler)( + png_structrp, png_inforp, png_uint_32 length); + /* A chunk-specific 'handler', NULL if the chunk is not supported in this + * build. + */ - png_debug(1, "in png_check_chunk_name"); + /* Crushing these values helps on modern 32-bit architectures because the + * pointer and the following bit fields both end up requiring 32 bits. + * Typically this will halve the table size. On 64-bit architectures the + * table entries will typically be 8 bytes. + */ + png_uint_32 max_length :12; /* Length min, max in bytes */ + png_uint_32 min_length :8; + /* Length errors on critical chunks have special handling to preserve the + * existing behaviour in libpng 1.6. Anciallary chunks are checked below + * and produce a 'benign' error. + */ + png_uint_32 pos_before :4; /* PNG_HAVE_ values chunk must precede */ + png_uint_32 pos_after :4; /* PNG_HAVE_ values chunk must follow */ + /* NOTE: PLTE, tRNS and bKGD require special handling which depends on + * the colour type of the base image. + */ + png_uint_32 multiple :1; /* Multiple occurences permitted */ + /* This is enabled for PLTE because PLTE may, in practice, be optional */ +} +read_chunks[PNG_INDEX_unknown] = +{ + /* Definitions as above but done indirectly by #define so that + * PNG_KNOWN_CHUNKS can be used safely to build the table in order. + * + * Each CDcHNK definition lists the values for the parameters **after** + * the first, 'handler', function. 'handler' is NULL when the chunk has no + * compiled in support. + */ +# define NoCheck 0x801U /* Do not check the maximum length */ +# define Limit 0x802U /* Limit to png_chunk_max bytes */ +# define LKMin 3U+LZ77Min /* Minimum length of keyword+LZ77 */ - for (i=1; i<=4; ++i) +#define hIHDR PNG_HAVE_IHDR +#define hPLTE PNG_HAVE_PLTE +#define hIDAT PNG_HAVE_IDAT + /* For the two chunks, tRNS and bKGD which can occur in PNGs without a PLTE + * but must occur after the PLTE use this and put the check in the handler + * routine for colour mapped images were PLTE is required. Also put a check + * in PLTE for other image types to drop the PLTE if tRNS or bKGD have been + * seen. + */ +#define hCOL (PNG_HAVE_PLTE|PNG_HAVE_IDAT) + /* Used for the decoding chunks which must be before PLTE. */ +#define aIDAT PNG_AFTER_IDAT + + /* Chunks from W3C PNG v3: */ + /* cHNK max_len, min, before, after, multiple */ +# define CDIHDR 13U, 13U, hIHDR, 0, 0 +# define CDPLTE NoCheck, 0U, 0, hIHDR, 1 + /* PLTE errors are only critical for colour-map images, consequently the + * hander does all the checks. + */ +# define CDIDAT NoCheck, 0U, aIDAT, hIHDR, 1 +# define CDIEND NoCheck, 0U, 0, aIDAT, 0 + /* Historically data was allowed in IEND */ +# define CDtRNS 256U, 0U, hIDAT, hIHDR, 0 +# define CDcHRM 32U, 32U, hCOL, hIHDR, 0 +# define CDgAMA 4U, 4U, hCOL, hIHDR, 0 +# define CDiCCP NoCheck, LKMin, hCOL, hIHDR, 0 +# define CDsBIT 4U, 1U, hCOL, hIHDR, 0 +# define CDsRGB 1U, 1U, hCOL, hIHDR, 0 +# define CDcICP 4U, 4U, hCOL, hIHDR, 0 +# define CDmDCV 24U, 24U, hCOL, hIHDR, 0 +# define CDeXIf Limit, 4U, 0, hIHDR, 0 +# define CDcLLI 8U, 8U, hCOL, hIHDR, 0 +# define CDtEXt NoCheck, 2U, 0, hIHDR, 1 + /* Allocates 'length+1'; checked in the handler */ +# define CDzTXt Limit, LKMin, 0, hIHDR, 1 +# define CDiTXt NoCheck, 6U, 0, hIHDR, 1 + /* Allocates 'length+1'; checked in the handler */ +# define CDbKGD 6U, 1U, hIDAT, hIHDR, 0 +# define CDhIST 1024U, 0U, hPLTE, hIHDR, 0 +# define CDpHYs 9U, 9U, hIDAT, hIHDR, 0 +# define CDsPLT NoCheck, 3U, hIDAT, hIHDR, 1 + /* Allocates 'length+1'; checked in the handler */ +# define CDtIME 7U, 7U, 0, hIHDR, 0 +# define CDacTL 8U, 8U, hIDAT, hIHDR, 0 +# define CDfcTL 25U, 26U, 0, hIHDR, 1 +# define CDfdAT Limit, 4U, hIDAT, hIHDR, 1 + /* Supported chunks from PNG extensions 1.5.0, NYI so limit */ +# define CDoFFs 9U, 9U, hIDAT, hIHDR, 0 +# define CDpCAL NoCheck, 14U, hIDAT, hIHDR, 0 + /* Allocates 'length+1'; checked in the handler */ +# define CDsCAL Limit, 4U, hIDAT, hIHDR, 0 + /* Allocates 'length+1'; checked in the handler */ + +# define PNG_CHUNK(cHNK, index) { png_handle_ ## cHNK, CD ## cHNK }, + PNG_KNOWN_CHUNKS +# undef PNG_CHUNK +}; + + +static png_index +png_chunk_index_from_name(png_uint_32 chunk_name) +{ + /* For chunk png_cHNK return PNG_INDEX_cHNK. Return PNG_INDEX_unknown if + * chunk_name is not known. Notice that in a particular build "known" does + * not necessarily mean "supported", although the inverse applies. + */ + switch (chunk_name) { - int c = cn & 0xff; +# define PNG_CHUNK(cHNK, index)\ + case png_ ## cHNK: return PNG_INDEX_ ## cHNK; /* == index */ - if (c < 65 || c > 122 || (c > 90 && c < 97)) - png_chunk_error(png_ptr, "invalid chunk type"); + PNG_KNOWN_CHUNKS - cn >>= 8; +# undef PNG_CHUNK + + default: return PNG_INDEX_unknown; } } -void /* PRIVATE */ -png_check_chunk_length(png_const_structrp png_ptr, png_uint_32 length) +png_handle_result_code /*PRIVATE*/ +png_handle_chunk(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_alloc_size_t limit = PNG_UINT_31_MAX; + /* CSE: these things don't change, these autos are just to save typing and + * make the code more clear. + */ + const png_uint_32 chunk_name = png_ptr->chunk_name; + const png_index chunk_index = png_chunk_index_from_name(chunk_name); -# ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_malloc_max > 0 && - png_ptr->user_chunk_malloc_max < limit) - limit = png_ptr->user_chunk_malloc_max; -# elif PNG_USER_CHUNK_MALLOC_MAX > 0 - if (PNG_USER_CHUNK_MALLOC_MAX < limit) - limit = PNG_USER_CHUNK_MALLOC_MAX; -# endif - if (png_ptr->chunk_name == png_IDAT) + png_handle_result_code handled = handled_error; + png_const_charp errmsg = NULL; + + /* Is this a known chunk? If not there are no checks performed here; + * png_handle_unknown does the correct checks. This means that the values + * for known but unsupported chunks in the above table are not used here + * however the chunks_seen fields in png_struct are still set. + */ + if (chunk_index == PNG_INDEX_unknown || + read_chunks[chunk_index].handler == NULL) { - png_alloc_size_t idat_limit = PNG_UINT_31_MAX; - size_t row_factor = - (size_t)png_ptr->width - * (size_t)png_ptr->channels - * (png_ptr->bit_depth > 8? 2: 1) - + 1 - + (png_ptr->interlaced? 6: 0); - if (png_ptr->height > PNG_UINT_32_MAX/row_factor) - idat_limit = PNG_UINT_31_MAX; - else - idat_limit = png_ptr->height * row_factor; - row_factor = row_factor > 32566? 32566 : row_factor; - idat_limit += 6 + 5*(idat_limit/row_factor+1); /* zlib+deflate overhead */ - idat_limit=idat_limit < PNG_UINT_31_MAX? idat_limit : PNG_UINT_31_MAX; - limit = limit < idat_limit? idat_limit : limit; + handled = png_handle_unknown( + png_ptr, info_ptr, length, PNG_HANDLE_CHUNK_AS_DEFAULT); } - if (length > limit) + /* First check the position. The first check is historical; the stream must + * start with IHDR and anything else causes libpng to give up immediately. + */ + else if (chunk_index != PNG_INDEX_IHDR && + (png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); /* NORETURN */ + + /* Before all the pos_before chunks, after all the pos_after chunks. */ + else if (((png_ptr->mode & read_chunks[chunk_index].pos_before) != 0) || + ((png_ptr->mode & read_chunks[chunk_index].pos_after) != + read_chunks[chunk_index].pos_after)) { - png_debug2(0," length = %lu, limit = %lu", - (unsigned long)length,(unsigned long)limit); - png_benign_error(png_ptr, "chunk data is too large"); + errmsg = "out of place"; } + + /* Now check for duplicates: duplicated critical chunks also produce a + * full error. + */ + else if (read_chunks[chunk_index].multiple == 0 && + png_file_has_chunk(png_ptr, chunk_index)) + { + errmsg = "duplicate"; + } + + else if (length < read_chunks[chunk_index].min_length) + errmsg = "too short"; + else + { + /* NOTE: apart from IHDR the critical chunks (PLTE, IDAT and IEND) are set + * up above not to do any length checks. + * + * The png_chunk_max check ensures that the variable length chunks are + * always checked at this point for being within the system allocation + * limits. + */ + unsigned max_length = read_chunks[chunk_index].max_length; + + switch (max_length) + { + case Limit: + /* png_read_chunk_header has already png_error'ed chunks with a + * length exceeding the 31-bit PNG limit, so just check the memory + * limit: + */ + if (length <= png_chunk_max(png_ptr)) + goto MeetsLimit; + + errmsg = "length exceeds libpng limit"; + break; + + default: + if (length <= max_length) + goto MeetsLimit; + + errmsg = "too long"; + break; + + case NoCheck: + MeetsLimit: + handled = read_chunks[chunk_index].handler( + png_ptr, info_ptr, length); + break; + } + } + + /* If there was an error or the chunk was simply skipped it is not counted as + * 'seen'. + */ + if (errmsg != NULL) + { + if (PNG_CHUNK_CRITICAL(chunk_name)) /* stop immediately */ + png_chunk_error(png_ptr, errmsg); + else /* ancillary chunk */ + { + /* The chunk data is skipped: */ + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, errmsg); + } + } + + else if (handled >= handled_saved) + { + if (chunk_index != PNG_INDEX_unknown) + png_file_add_chunk(png_ptr, chunk_index); + } + + return handled; } /* Combines the row recently read in with the existing pixels in the row. This @@ -4231,6 +4215,9 @@ png_read_IDAT_data(png_structrp png_ptr, png_bytep output, avail_in = png_ptr->IDAT_read_size; + if (avail_in > png_chunk_max(png_ptr)) + avail_in = (uInt)/*SAFE*/png_chunk_max(png_ptr); + if (avail_in > png_ptr->idat_size) avail_in = (uInt)png_ptr->idat_size; @@ -4238,8 +4225,13 @@ png_read_IDAT_data(png_structrp png_ptr, png_bytep output, * to minimize memory usage by causing lots of re-allocs, but * realistically doing IDAT_read_size re-allocs is not likely to be a * big problem. + * + * An error here corresponds to the system being out of memory. */ - buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/); + buffer = png_read_buffer(png_ptr, avail_in); + + if (buffer == NULL) + png_chunk_error(png_ptr, "out of memory"); png_crc_read(png_ptr, buffer, avail_in); png_ptr->idat_size -= avail_in; diff --git a/3rdparty/libpng/pngset.c b/3rdparty/libpng/pngset.c index 462b50cf2a..3e63c2724e 100644 --- a/3rdparty/libpng/pngset.c +++ b/3rdparty/libpng/pngset.c @@ -41,27 +41,21 @@ png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr, png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, png_fixed_point blue_y) { - png_xy xy; - png_debug1(1, "in %s storage function", "cHRM fixed"); if (png_ptr == NULL || info_ptr == NULL) return; - xy.redx = red_x; - xy.redy = red_y; - xy.greenx = green_x; - xy.greeny = green_y; - xy.bluex = blue_x; - xy.bluey = blue_y; - xy.whitex = white_x; - xy.whitey = white_y; + info_ptr->cHRM.redx = red_x; + info_ptr->cHRM.redy = red_y; + info_ptr->cHRM.greenx = green_x; + info_ptr->cHRM.greeny = green_y; + info_ptr->cHRM.bluex = blue_x; + info_ptr->cHRM.bluey = blue_y; + info_ptr->cHRM.whitex = white_x; + info_ptr->cHRM.whitey = white_y; - if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy, - 2/* override with app values*/) != 0) - info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; - - png_colorspace_sync_info(png_ptr, info_ptr); + info_ptr->valid |= PNG_INFO_cHRM; } void PNGFAPI @@ -73,6 +67,7 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, png_fixed_point int_blue_Z) { png_XYZ XYZ; + png_xy xy; png_debug1(1, "in %s storage function", "cHRM XYZ fixed"); @@ -89,11 +84,14 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, XYZ.blue_Y = int_blue_Y; XYZ.blue_Z = int_blue_Z; - if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, - &XYZ, 2) != 0) - info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + if (png_xy_from_XYZ(&xy, &XYZ) == 0) + { + info_ptr->cHRM = xy; + info_ptr->valid |= PNG_INFO_cHRM; + } - png_colorspace_sync_info(png_ptr, info_ptr); + else + png_app_error(png_ptr, "invalid cHRM XYZ"); } # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -159,6 +157,163 @@ png_set_cICP(png_const_structrp png_ptr, png_inforp info_ptr, } #endif /* cICP */ +#ifdef PNG_cLLI_SUPPORTED +void PNGFAPI +png_set_cLLI_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + /* The values below are in cd/m2 (nits) and are scaled by 10,000; not + * 100,000 as in the case of png_fixed_point. + */ + png_uint_32 maxCLL, png_uint_32 maxFALL) +{ + png_debug1(1, "in %s storage function", "cLLI"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check the light level range: */ + if (maxCLL > 0x7FFFFFFFU || maxFALL > 0x7FFFFFFFU) + { + /* The limit is 200kcd/m2; somewhat bright but not inconceivable because + * human vision is said to run up to 100Mcd/m2. The sun is about 2Gcd/m2. + * + * The reference sRGB monitor is 80cd/m2 and the limit of PQ encoding is + * 2kcd/m2. + */ + png_chunk_report(png_ptr, "cLLI light level exceeds PNG limit", + PNG_CHUNK_WRITE_ERROR); + return; + } + + info_ptr->maxCLL = maxCLL; + info_ptr->maxFALL = maxFALL; + info_ptr->valid |= PNG_INFO_cLLI; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cLLI(png_const_structrp png_ptr, png_inforp info_ptr, + double maxCLL, double maxFALL) +{ + png_set_cLLI_fixed(png_ptr, info_ptr, + png_fixed_ITU(png_ptr, maxCLL, "png_set_cLLI(maxCLL)"), + png_fixed_ITU(png_ptr, maxFALL, "png_set_cLLI(maxFALL)")); +} +# endif /* FLOATING_POINT */ +#endif /* cLLI */ + +#ifdef PNG_mDCV_SUPPORTED +static png_uint_16 +png_ITU_fixed_16(int *error, png_fixed_point v) +{ + /* Return a safe uint16_t value scaled according to the ITU H273 rules for + * 16-bit display chromaticities. Functions like the corresponding + * png_fixed() internal function with regard to errors: it's an error on + * write, a chunk_benign_error on read: See the definition of + * png_chunk_report in pngpriv.h. + */ + v /= 2; /* rounds to 0 in C: avoids insignificant arithmetic errors */ + if (v > 65535 || v < 0) + { + *error = 1; + return 0; + } + + return (png_uint_16)/*SAFE*/v; +} + +void PNGAPI +png_set_mDCV_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point white_x, png_fixed_point white_y, + png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y, + png_uint_32 maxDL, + png_uint_32 minDL) +{ + png_uint_16 rx, ry, gx, gy, bx, by, wx, wy; + int error; + + png_debug1(1, "in %s storage function", "mDCV"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check the input values to ensure they are in the expected range: */ + error = 0; + rx = png_ITU_fixed_16(&error, red_x); + ry = png_ITU_fixed_16(&error, red_y); + gx = png_ITU_fixed_16(&error, green_x); + gy = png_ITU_fixed_16(&error, green_y); + bx = png_ITU_fixed_16(&error, blue_x); + by = png_ITU_fixed_16(&error, blue_y); + wx = png_ITU_fixed_16(&error, white_x); + wy = png_ITU_fixed_16(&error, white_y); + + if (error) + { + png_chunk_report(png_ptr, + "mDCV chromaticities outside representable range", + PNG_CHUNK_WRITE_ERROR); + return; + } + + /* Check the light level range: */ + if (maxDL > 0x7FFFFFFFU || minDL > 0x7FFFFFFFU) + { + /* The limit is 200kcd/m2; somewhat bright but not inconceivable because + * human vision is said to run up to 100Mcd/m2. The sun is about 2Gcd/m2. + * + * The reference sRGB monitor is 80cd/m2 and the limit of PQ encoding is + * 2kcd/m2. + */ + png_chunk_report(png_ptr, "mDCV display light level exceeds PNG limit", + PNG_CHUNK_WRITE_ERROR); + return; + } + + /* All values are safe, the settings are accepted. + * + * IMPLEMENTATION NOTE: in practice the values can be checked and assigned + * but the result is confusing if a writing app calls png_set_mDCV more than + * once, the second time with an invalid value. This approach is more + * obviously correct at the cost of typing and a very slight machine + * overhead. + */ + info_ptr->mastering_red_x = rx; + info_ptr->mastering_red_y = ry; + info_ptr->mastering_green_x = gx; + info_ptr->mastering_green_y = gy; + info_ptr->mastering_blue_x = bx; + info_ptr->mastering_blue_y = by; + info_ptr->mastering_white_x = wx; + info_ptr->mastering_white_y = wy; + info_ptr->mastering_maxDL = maxDL; + info_ptr->mastering_minDL = minDL; + info_ptr->valid |= PNG_INFO_mDCV; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_mDCV(png_const_structrp png_ptr, png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y, + double maxDL, double minDL) +{ + png_set_mDCV_fixed(png_ptr, info_ptr, + png_fixed(png_ptr, white_x, "png_set_mDCV(white(x))"), + png_fixed(png_ptr, white_y, "png_set_mDCV(white(y))"), + png_fixed(png_ptr, red_x, "png_set_mDCV(red(x))"), + png_fixed(png_ptr, red_y, "png_set_mDCV(red(y))"), + png_fixed(png_ptr, green_x, "png_set_mDCV(green(x))"), + png_fixed(png_ptr, green_y, "png_set_mDCV(green(y))"), + png_fixed(png_ptr, blue_x, "png_set_mDCV(blue(x))"), + png_fixed(png_ptr, blue_y, "png_set_mDCV(blue(y))"), + png_fixed_ITU(png_ptr, maxDL, "png_set_mDCV(maxDL)"), + png_fixed_ITU(png_ptr, minDL, "png_set_mDCV(minDL)")); +} +# endif /* FLOATING_POINT */ +#endif /* mDCV */ + #ifdef PNG_eXIf_SUPPORTED void PNGAPI png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, @@ -210,8 +365,8 @@ png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma); - png_colorspace_sync_info(png_ptr, info_ptr); + info_ptr->gamma = file_gamma; + info_ptr->valid |= PNG_INFO_gAMA; } # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -670,8 +825,8 @@ png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent) if (png_ptr == NULL || info_ptr == NULL) return; - (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent); - png_colorspace_sync_info(png_ptr, info_ptr); + info_ptr->rendering_intent = srgb_intent; + info_ptr->valid |= PNG_INFO_sRGB; } void PNGAPI @@ -683,15 +838,20 @@ png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, - srgb_intent) != 0) - { - /* This causes the gAMA and cHRM to be written too */ - info_ptr->colorspace.flags |= - PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; - } + png_set_sRGB(png_ptr, info_ptr, srgb_intent); - png_colorspace_sync_info(png_ptr, info_ptr); +# ifdef PNG_gAMA_SUPPORTED + png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE); +# endif /* gAMA */ + +# ifdef PNG_cHRM_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + /* color x y */ + /* white */ 31270, 32900, + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000); +# endif /* cHRM */ } #endif /* sRGB */ @@ -714,27 +874,6 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, if (compression_type != PNG_COMPRESSION_TYPE_BASE) png_app_error(png_ptr, "Invalid iCCP compression method"); - /* Set the colorspace first because this validates the profile; do not - * override previously set app cHRM or gAMA here (because likely as not the - * application knows better than libpng what the correct values are.) Pass - * the info_ptr color_type field to png_colorspace_set_ICC because in the - * write case it has not yet been stored in png_ptr. - */ - { - int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, - proflen, profile, info_ptr->color_type); - - png_colorspace_sync_info(png_ptr, info_ptr); - - /* Don't do any of the copying if the profile was bad, or inconsistent. */ - if (result == 0) - return; - - /* But do write the gAMA and cHRM chunks from the profile. */ - info_ptr->colorspace.flags |= - PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; - } - length = strlen(name)+1; new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length)); @@ -1421,11 +1560,13 @@ png_set_keep_unknown_chunks(png_structrp png_ptr, int keep, 98, 75, 71, 68, '\0', /* bKGD */ 99, 72, 82, 77, '\0', /* cHRM */ 99, 73, 67, 80, '\0', /* cICP */ + 99, 76, 76, 73, '\0', /* cLLI */ 101, 88, 73, 102, '\0', /* eXIf */ 103, 65, 77, 65, '\0', /* gAMA */ 104, 73, 83, 84, '\0', /* hIST */ 105, 67, 67, 80, '\0', /* iCCP */ 105, 84, 88, 116, '\0', /* iTXt */ + 109, 68, 67, 86, '\0', /* mDCV */ 111, 70, 70, 115, '\0', /* oFFs */ 112, 67, 65, 76, '\0', /* pCAL */ 112, 72, 89, 115, '\0', /* pHYs */ @@ -1687,8 +1828,24 @@ png_set_chunk_malloc_max(png_structrp png_ptr, { png_debug(1, "in png_set_chunk_malloc_max"); + /* pngstruct::user_chunk_malloc_max is initialized to a non-zero value in + * png.c. This API supports '0' for unlimited, make sure the correct + * (unlimited) value is set here to avoid a need to check for 0 everywhere + * the parameter is used. + */ if (png_ptr != NULL) - png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; + { + if (user_chunk_malloc_max == 0U) /* unlimited */ + { +# ifdef PNG_MAX_MALLOC_64K + png_ptr->user_chunk_malloc_max = 65536U; +# else + png_ptr->user_chunk_malloc_max = PNG_SIZE_MAX; +# endif + } + else + png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; + } } #endif /* ?SET_USER_LIMITS */ diff --git a/3rdparty/libpng/pngstruct.h b/3rdparty/libpng/pngstruct.h index 7e919d0a37..fe5fa04155 100644 --- a/3rdparty/libpng/pngstruct.h +++ b/3rdparty/libpng/pngstruct.h @@ -1,6 +1,6 @@ -/* pngstruct.h - header file for PNG reference library +/* pngstruct.h - internal structures for libpng * - * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -10,11 +10,9 @@ * and license in png.h */ -/* The structure that holds the information to read and write PNG files. - * The only people who need to care about what is inside of this are the - * people who will be modifying the library for their own special needs. - * It should NOT be accessed directly by an application. - */ +#ifndef PNGPRIV_H +# error This file must not be included by applications; please include +#endif #ifndef PNGSTRUCT_H #define PNGSTRUCT_H @@ -69,13 +67,7 @@ typedef struct png_compression_buffer /* Colorspace support; structures used in png_struct, png_info and in internal * functions to hold and communicate information about the color space. - * - * PNG_COLORSPACE_SUPPORTED is only required if the application will perform - * colorspace corrections, otherwise all the colorspace information can be - * skipped and the size of libpng can be reduced (significantly) by compiling - * out the colorspace support. */ -#ifdef PNG_COLORSPACE_SUPPORTED /* The chromaticities of the red, green and blue colorants and the chromaticity * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)). */ @@ -96,48 +88,36 @@ typedef struct png_XYZ png_fixed_point green_X, green_Y, green_Z; png_fixed_point blue_X, blue_Y, blue_Z; } png_XYZ; -#endif /* COLORSPACE */ -#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) -/* A colorspace is all the above plus, potentially, profile information; - * however at present libpng does not use the profile internally so it is only - * stored in the png_info struct (if iCCP is supported.) The rendering intent - * is retained here and is checked. - * - * The file gamma encoding information is also stored here and gamma correction - * is done by libpng, whereas color correction must currently be done by the - * application. +/* Chunk index values as an enum, PNG_INDEX_unknown is also a count of the + * number of chunks. */ -typedef struct png_colorspace +#define PNG_CHUNK(cHNK, i) PNG_INDEX_ ## cHNK = (i), +typedef enum { -#ifdef PNG_GAMMA_SUPPORTED - png_fixed_point gamma; /* File gamma */ -#endif + PNG_KNOWN_CHUNKS + PNG_INDEX_unknown +} png_index; +#undef PNG_CHUNK -#ifdef PNG_COLORSPACE_SUPPORTED - png_xy end_points_xy; /* End points as chromaticities */ - png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */ - png_uint_16 rendering_intent; /* Rendering intent of a profile */ -#endif +/* Chunk flag values. These are (png_uint_32 values) with exactly one bit set + * and can be combined into a flag set with bitwise 'or'. + * + * TODO: C23: convert these macros to C23 inlines (which are static). + */ +#define png_chunk_flag_from_index(i) (0x80000000U >> (31 - (i))) + /* The flag coresponding to the given png_index enum value. This is defined + * for png_unknown as well (until it reaches the value 32) but this should + * not be relied on. + */ - /* Flags are always defined to simplify the code. */ - png_uint_16 flags; /* As defined below */ -} png_colorspace, * PNG_RESTRICT png_colorspacerp; +#define png_file_has_chunk(png_ptr, i)\ + (((png_ptr)->chunks & png_chunk_flag_from_index(i)) != 0) + /* The chunk has been recorded in png_struct */ -typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp; - -/* General flags for the 'flags' field */ -#define PNG_COLORSPACE_HAVE_GAMMA 0x0001 -#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002 -#define PNG_COLORSPACE_HAVE_INTENT 0x0004 -#define PNG_COLORSPACE_FROM_gAMA 0x0008 -#define PNG_COLORSPACE_FROM_cHRM 0x0010 -#define PNG_COLORSPACE_FROM_sRGB 0x0020 -#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040 -#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */ -#define PNG_COLORSPACE_INVALID 0x8000 -#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags)) -#endif /* COLORSPACE || GAMMA */ +#define png_file_add_chunk(pnt_ptr, i)\ + ((void)((png_ptr)->chunks |= png_chunk_flag_from_index(i))) + /* Record the chunk in the png_struct */ struct png_struct_def { @@ -209,6 +189,11 @@ struct png_struct_def int zlib_set_strategy; #endif + png_uint_32 chunks; /* PNG_CF_ for every chunk read or (NYI) written */ +# define png_has_chunk(png_ptr, cHNK)\ + png_file_has_chunk(png_ptr, PNG_INDEX_ ## cHNK) + /* Convenience accessor - use this to check for a known chunk by name */ + png_uint_32 width; /* width of image in pixels */ png_uint_32 height; /* height of image in pixels */ png_uint_32 num_rows; /* number of rows in current pass */ @@ -285,9 +270,16 @@ struct png_struct_def png_uint_32 flush_rows; /* number of rows written since last flush */ #endif +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_xy chromaticities; /* From mDVC, cICP, [iCCP], sRGB or cHRM */ +#endif + #ifdef PNG_READ_GAMMA_SUPPORTED int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ - png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ + png_fixed_point screen_gamma; /* screen gamma value (display exponent) */ + png_fixed_point file_gamma; /* file gamma value (encoding exponent) */ + png_fixed_point chunk_gamma; /* from cICP, iCCP, sRGB or gAMA */ + png_fixed_point default_gamma;/* from png_set_alpha_mode */ png_bytep gamma_table; /* gamma table for 8-bit depth files */ png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ @@ -299,7 +291,7 @@ struct png_struct_def png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ -#endif +#endif /* READ_GAMMA */ #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) png_color_8 sig_bit; /* significant bits in each available channel */ @@ -349,8 +341,8 @@ struct png_struct_def /* To do: remove this from libpng-1.7 */ #ifdef PNG_TIME_RFC1123_SUPPORTED char time_buffer[29]; /* String to hold RFC 1123 time text */ -#endif -#endif +#endif /* TIME_RFC1123 */ +#endif /* LIBPNG_VER < 10700 */ /* New members added in libpng-1.0.6 */ @@ -360,8 +352,8 @@ struct png_struct_def png_voidp user_chunk_ptr; #ifdef PNG_READ_USER_CHUNKS_SUPPORTED png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ -#endif -#endif +#endif /* READ_USER_CHUNKS */ +#endif /* USER_CHUNKS */ #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED int unknown_default; /* As PNG_HANDLE_* */ @@ -383,7 +375,8 @@ struct png_struct_def /* New member added in libpng-1.6.36 */ #if defined(PNG_READ_EXPAND_SUPPORTED) && \ - defined(PNG_ARM_NEON_IMPLEMENTATION) + (defined(PNG_ARM_NEON_IMPLEMENTATION) || \ + defined(PNG_RISCV_RVV_IMPLEMENTATION)) png_bytep riffled_palette; /* buffer for accelerated palette expansion */ #endif @@ -412,7 +405,6 @@ struct png_struct_def #ifdef PNG_READ_QUANTIZE_SUPPORTED /* The following three members were added at version 1.0.14 and 1.2.4 */ - png_bytep quantize_sort; /* working sort array */ png_bytep index_to_palette; /* where the original index currently is in the palette */ png_bytep palette_to_index; /* which original index points to this @@ -468,11 +460,5 @@ struct png_struct_def /* New member added in libpng-1.5.7 */ void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, png_bytep row, png_const_bytep prev_row); - -#ifdef PNG_READ_SUPPORTED -#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) - png_colorspace colorspace; -#endif -#endif }; #endif /* PNGSTRUCT_H */ diff --git a/3rdparty/libpng/pngwio.c b/3rdparty/libpng/pngwio.c index 38c9c006cb..96a3187ff0 100644 --- a/3rdparty/libpng/pngwio.c +++ b/3rdparty/libpng/pngwio.c @@ -1,6 +1,6 @@ /* pngwio.c - functions for data output * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2014,2016,2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -54,7 +54,7 @@ png_default_write_data(png_structp png_ptr, png_bytep data, size_t length) if (png_ptr == NULL) return; - check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); + check = fwrite(data, 1, length, (FILE *)png_ptr->io_ptr); if (check != length) png_error(png_ptr, "Write Error"); @@ -77,12 +77,12 @@ png_flush(png_structrp png_ptr) void PNGCBAPI png_default_flush(png_structp png_ptr) { - png_FILE_p io_ptr; + FILE *io_ptr; if (png_ptr == NULL) return; - io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr)); + io_ptr = png_voidcast(FILE *, png_ptr->io_ptr); fflush(io_ptr); } # endif diff --git a/3rdparty/libpng/pngwrite.c b/3rdparty/libpng/pngwrite.c index 8b1b06c20c..83148960ef 100644 --- a/3rdparty/libpng/pngwrite.c +++ b/3rdparty/libpng/pngwrite.c @@ -157,8 +157,30 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) * space priority. As above it therefore behooves libpng to write the colour * space chunks in the priority order so that a streaming app need not buffer * them. + * + * PNG v3: Chunks mDCV and cLLI provide ancillary information for the + * interpretation of the colourspace chunkgs but do not require support for + * those chunks so are outside the "COLORSPACE" check but before the write of + * the colourspace chunks themselves. */ -#ifdef PNG_COLORSPACE_SUPPORTED +#ifdef PNG_WRITE_cLLI_SUPPORTED + if ((info_ptr->valid & PNG_INFO_cLLI) != 0) + { + png_write_cLLI_fixed(png_ptr, info_ptr->maxCLL, info_ptr->maxFALL); + } +#endif +#ifdef PNG_WRITE_mDCV_SUPPORTED + if ((info_ptr->valid & PNG_INFO_mDCV) != 0) + { + png_write_mDCV_fixed(png_ptr, + info_ptr->mastering_red_x, info_ptr->mastering_red_y, + info_ptr->mastering_green_x, info_ptr->mastering_green_y, + info_ptr->mastering_blue_x, info_ptr->mastering_blue_y, + info_ptr->mastering_white_x, info_ptr->mastering_white_y, + info_ptr->mastering_maxDL, info_ptr->mastering_minDL); + } +#endif + # ifdef PNG_WRITE_cICP_SUPPORTED /* Priority 4 */ if ((info_ptr->valid & PNG_INFO_cICP) != 0) { @@ -170,50 +192,28 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) } # endif - /* PNG v3 change: it is now permitted to write both sRGB and ICC profiles, - * however because the libpng code auto-generates an sRGB for the - * corresponding ICC profiles and because PNG v2 disallowed this we need - * to only write one. - * - * Remove the PNG v2 warning about writing an sRGB ICC profile as well - * because it's invalid with PNG v3. - */ # ifdef PNG_WRITE_iCCP_SUPPORTED /* Priority 3 */ - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && - (info_ptr->valid & PNG_INFO_iCCP) != 0) + if ((info_ptr->valid & PNG_INFO_iCCP) != 0) { png_write_iCCP(png_ptr, info_ptr->iccp_name, - info_ptr->iccp_profile); + info_ptr->iccp_profile, info_ptr->iccp_proflen); } -# ifdef PNG_WRITE_sRGB_SUPPORTED - else -# endif # endif # ifdef PNG_WRITE_sRGB_SUPPORTED /* Priority 2 */ - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && - (info_ptr->valid & PNG_INFO_sRGB) != 0) - png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); + if ((info_ptr->valid & PNG_INFO_sRGB) != 0) + png_write_sRGB(png_ptr, info_ptr->rendering_intent); # endif /* WRITE_sRGB */ -#endif /* COLORSPACE */ -#ifdef PNG_GAMMA_SUPPORTED # ifdef PNG_WRITE_gAMA_SUPPORTED /* Priority 1 */ - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && - (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 && - (info_ptr->valid & PNG_INFO_gAMA) != 0) - png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); + if ((info_ptr->valid & PNG_INFO_gAMA) != 0) + png_write_gAMA_fixed(png_ptr, info_ptr->gamma); # endif -#endif -#ifdef PNG_COLORSPACE_SUPPORTED # ifdef PNG_WRITE_cHRM_SUPPORTED /* Also priority 1 */ - if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && - (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 && - (info_ptr->valid & PNG_INFO_cHRM) != 0) - png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); + if ((info_ptr->valid & PNG_INFO_cHRM) != 0) + png_write_cHRM_fixed(png_ptr, &info_ptr->cHRM); # endif -#endif png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; } @@ -2173,8 +2173,7 @@ png_image_write_main(png_voidp argument) * before it is written. This only applies when the input is 16-bit and * either there is an alpha channel or it is converted to 8-bit. */ - if ((linear != 0 && alpha != 0 ) || - (colormap == 0 && display->convert_to_8bit != 0)) + if (linear != 0 && (alpha != 0 || display->convert_to_8bit != 0)) { png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr))); @@ -2333,7 +2332,7 @@ int PNGAPI png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, const void *buffer, png_int_32 row_stride, const void *colormap) { - /* Write the image to the given (FILE*). */ + /* Write the image to the given FILE object. */ if (image != NULL && image->version == PNG_IMAGE_VERSION) { if (file != NULL && buffer != NULL) diff --git a/3rdparty/libpng/pngwutil.c b/3rdparty/libpng/pngwutil.c index 8b2bf4e6d6..863ffe8c2f 100644 --- a/3rdparty/libpng/pngwutil.c +++ b/3rdparty/libpng/pngwutil.c @@ -1,6 +1,6 @@ /* pngwutil.c - utilities to write a PNG file * - * Copyright (c) 2018-2024 Cosmin Truta + * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -1132,10 +1132,9 @@ png_write_sRGB(png_structrp png_ptr, int srgb_intent) /* Write an iCCP chunk */ void /* PRIVATE */ png_write_iCCP(png_structrp png_ptr, png_const_charp name, - png_const_bytep profile) + png_const_bytep profile, png_uint_32 profile_len) { png_uint_32 name_len; - png_uint_32 profile_len; png_byte new_name[81]; /* 1 byte for the compression byte */ compression_state comp; png_uint_32 temp; @@ -1148,11 +1147,12 @@ png_write_iCCP(png_structrp png_ptr, png_const_charp name, if (profile == NULL) png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */ - profile_len = png_get_uint_32(profile); - if (profile_len < 132) png_error(png_ptr, "ICC profile too short"); + if (png_get_uint_32(profile) != profile_len) + png_error(png_ptr, "Incorrect data in iCCP"); + temp = (png_uint_32) (*(profile+8)); if (temp > 3 && (profile_len & 0x03)) png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)"); @@ -1511,6 +1511,50 @@ png_write_cICP(png_structrp png_ptr, } #endif +#ifdef PNG_WRITE_cLLI_SUPPORTED +void /* PRIVATE */ +png_write_cLLI_fixed(png_structrp png_ptr, png_uint_32 maxCLL, + png_uint_32 maxFALL) +{ + png_byte buf[8]; + + png_debug(1, "in png_write_cLLI_fixed"); + + png_save_uint_32(buf, maxCLL); + png_save_uint_32(buf + 4, maxFALL); + + png_write_complete_chunk(png_ptr, png_cLLI, buf, 8); +} +#endif + +#ifdef PNG_WRITE_mDCV_SUPPORTED +void /* PRIVATE */ +png_write_mDCV_fixed(png_structrp png_ptr, + png_uint_16 red_x, png_uint_16 red_y, + png_uint_16 green_x, png_uint_16 green_y, + png_uint_16 blue_x, png_uint_16 blue_y, + png_uint_16 white_x, png_uint_16 white_y, + png_uint_32 maxDL, png_uint_32 minDL) +{ + png_byte buf[24]; + + png_debug(1, "in png_write_mDCV_fixed"); + + png_save_uint_16(buf + 0, red_x); + png_save_uint_16(buf + 2, red_y); + png_save_uint_16(buf + 4, green_x); + png_save_uint_16(buf + 6, green_y); + png_save_uint_16(buf + 8, blue_x); + png_save_uint_16(buf + 10, blue_y); + png_save_uint_16(buf + 12, white_x); + png_save_uint_16(buf + 14, white_y); + png_save_uint_32(buf + 16, maxDL); + png_save_uint_32(buf + 20, minDL); + + png_write_complete_chunk(png_ptr, png_mDCV, buf, 24); +} +#endif + #ifdef PNG_WRITE_eXIf_SUPPORTED /* Write the Exif data */ void /* PRIVATE */ diff --git a/3rdparty/libpng/powerpc/filter_vsx_intrinsics.c b/3rdparty/libpng/powerpc/filter_vsx_intrinsics.c index 01cf8800dc..5acc17c949 100644 --- a/3rdparty/libpng/powerpc/filter_vsx_intrinsics.c +++ b/3rdparty/libpng/powerpc/filter_vsx_intrinsics.c @@ -23,7 +23,7 @@ #if PNG_POWERPC_VSX_OPT > 0 #ifndef __VSX__ -# error "This code requires VSX support (POWER7 and later). Please provide -mvsx compiler flag." +# error This code requires VSX support (POWER7 and later); please compile with -mvsx #endif #define vec_ld_unaligned(vec,data) vec = vec_vsx_ld(0,data) diff --git a/3rdparty/libpng/powerpc/powerpc_init.c b/3rdparty/libpng/powerpc/powerpc_init.c index 9027480098..70782eda1b 100644 --- a/3rdparty/libpng/powerpc/powerpc_init.c +++ b/3rdparty/libpng/powerpc/powerpc_init.c @@ -46,7 +46,7 @@ static int png_have_vsx(png_structp png_ptr); #include PNG_POWERPC_VSX_FILE #else /* PNG_POWERPC_VSX_FILE */ -# error "PNG_POWERPC_VSX_FILE undefined: no support for run-time POWERPC VSX checks" +# error PNG_POWERPC_VSX_FILE undefined: no support for run-time POWERPC VSX checks #endif /* PNG_POWERPC_VSX_FILE */ #endif /* PNG_POWERPC_VSX_CHECK_SUPPORTED */ diff --git a/3rdparty/libpng/riscv/filter_rvv_intrinsics.c b/3rdparty/libpng/riscv/filter_rvv_intrinsics.c new file mode 100644 index 0000000000..b41cf9d400 --- /dev/null +++ b/3rdparty/libpng/riscv/filter_rvv_intrinsics.c @@ -0,0 +1,290 @@ +/* filter_rvv_intrinsics.c - RISC-V Vector optimized filter functions + * + * Copyright (c) 2023 Google LLC + * Written by Manfred SCHLAEGL, 2022 + * Revised by: + * - Dragoș Tiselice , May 2023 + * - Filip Wasil , March 2025 + * - Liang Junzhao , November 2025 + * - Alexander Smorkalov , December 2025 + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_RISCV_RVV_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */ + +#include + +void +png_read_filter_row_up_rvv(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t len = row_info->rowbytes; + + for (size_t vl; len > 0; len -= vl, row += vl, prev_row += vl) + { + vl = __riscv_vsetvl_e8m8(len); + + vuint8m8_t prev_vals = __riscv_vle8_v_u8m8(prev_row, vl); + vuint8m8_t row_vals = __riscv_vle8_v_u8m8(row, vl); + + row_vals = __riscv_vadd_vv_u8m8(row_vals, prev_vals, vl); + + __riscv_vse8_v_u8m8(row, row_vals, vl); + } +} + +static inline void +png_read_filter_row_sub_rvv(size_t len, size_t bpp, unsigned char* row) +{ + png_bytep rp_end = row + len; + + /* + * row: | a | x | + * + * a = a + x + * + * a .. [v0](e8) + * x .. [v8](e8) + */ + + size_t vl = __riscv_vsetvl_e8m1(bpp); + + /* a = *row */ + vuint8m1_t a = __riscv_vle8_v_u8m1(row, vl); + row += bpp; + + while (row < rp_end) + { + /* x = *row */ + vuint8m1_t x = __riscv_vle8_v_u8m1(row, vl); + + /* a = a + x */ + a = __riscv_vadd_vv_u8m1(a, x, vl); + + /* *row = a */ + __riscv_vse8_v_u8m1(row, a, vl); + row += bpp; + } +} + +void +png_read_filter_row_sub3_rvv(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t len = row_info->rowbytes; + + png_read_filter_row_sub_rvv(len, 3, row); + + PNG_UNUSED(prev_row) +} + +void +png_read_filter_row_sub4_rvv(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t len = row_info->rowbytes; + + png_read_filter_row_sub_rvv(len, 4, row); + + PNG_UNUSED(prev_row) +} + +static inline void +png_read_filter_row_avg_rvv(size_t len, size_t bpp, unsigned char* row, + const unsigned char* prev_row) +{ + png_bytep rp_end = row + len; + + /* + * row: | a | x | + * prev_row: | | b | + * + * a .. [v2](e8) + * b .. [v4](e8) + * x .. [v8](e8) + * tmp .. [v12-v13](e16) + */ + + /* first pixel */ + + size_t vl = __riscv_vsetvl_e8m1(bpp); + + /* b = *prev_row */ + vuint8m1_t b = __riscv_vle8_v_u8m1(prev_row, vl); + prev_row += bpp; + + /* x = *row */ + vuint8m1_t x = __riscv_vle8_v_u8m1(row, vl); + + /* b = b / 2 */ + b = __riscv_vsrl_vx_u8m1(b, 1, vl); + + /* a = x + b */ + vuint8m1_t a = __riscv_vadd_vv_u8m1(b, x, vl); + + /* *row = a */ + __riscv_vse8_v_u8m1(row, a, vl); + row += bpp; + + /* remaining pixels */ + while (row < rp_end) + { + /* b = *prev_row */ + b = __riscv_vle8_v_u8m1(prev_row, vl); + prev_row += bpp; + + /* x = *row */ + x = __riscv_vle8_v_u8m1(row, vl); + + /* a = (a + b) / 2, round to zero with vxrm = 2 */ + a = __riscv_vaaddu_vv_u8m1(a, b, 2, vl); + + /* a += x */ + a = __riscv_vadd_vv_u8m1(a, x, vl); + + /* *row = a */ + __riscv_vse8_v_u8m1(row, a, vl); + row += bpp; + } +} + +void +png_read_filter_row_avg3_rvv(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t len = row_info->rowbytes; + + png_read_filter_row_avg_rvv(len, 3, row, prev_row); + + PNG_UNUSED(prev_row) +} + +void +png_read_filter_row_avg4_rvv(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t len = row_info->rowbytes; + + png_read_filter_row_avg_rvv(len, 4, row, prev_row); + + PNG_UNUSED(prev_row) +} + +static inline void +png_read_filter_row_paeth_rvv(size_t len, size_t bpp, unsigned char* row, + const unsigned char* prev) +{ + png_bytep rp_end = row + len; + + /* + * row: | a | x | + * prev: | c | b | + * + * a .. [v2](e8) + * b .. [v4](e8) + * c .. [v6](e8) + * x .. [v8](e8) + * p .. [v12-v13](e16) + * pa, pb, pc .. [v16-v17, v20-v21, v24-v25](e16) + */ + + /* first pixel */ + + size_t vl = __riscv_vsetvl_e8m1(bpp); + + /* a = *row */ + vuint8m1_t a = __riscv_vle8_v_u8m1(row, vl); + + /* c = *prev */ + vuint8m1_t c = __riscv_vle8_v_u8m1(prev, vl); + + /* a += c */ + a = __riscv_vadd_vv_u8m1(a, c, vl); + + /* *row = a */ + __riscv_vse8_v_u8m1(row, a, vl); + row += bpp; + prev += bpp; + + /* remaining pixels */ + + while (row < rp_end) + { + /* b = *prev */ + vuint8m1_t b = __riscv_vle8_v_u8m1(prev, vl); + prev += bpp; + + /* x = *row */ + vuint8m1_t x = __riscv_vle8_v_u8m1(row, vl); + + /* p = b - c and pc = a - c */ + vuint16m2_t p = __riscv_vwsubu_vv_u16m2(b, c, vl); + vuint16m2_t pc = __riscv_vwsubu_vv_u16m2(a, c, vl); + + /* pa = |p| */ + vuint16m2_t tmp = __riscv_vrsub_vx_u16m2(p, 0, vl); + vuint16m2_t pa = __riscv_vminu_vv_u16m2(p, tmp, vl); + + /* pb = |pc| */ + tmp = __riscv_vrsub_vx_u16m2(pc, 0, vl); + vuint16m2_t pb = __riscv_vminu_vv_u16m2(pc, tmp, vl); + + /* pc = |p + pc| */ + pc = __riscv_vadd_vv_u16m2(p, pc, vl); + tmp = __riscv_vrsub_vx_u16m2(pc, 0, vl); + pc = __riscv_vminu_vv_u16m2(pc, tmp, vl); + + /* + * The key insight is that we want the minimum of pa, pb, pc. + * - If pa <= pb and pa <= pc, use a + * - Else if pb <= pc, use b + * - Else use c + */ + + /* if (pb < pa) { pa = pb; a = b; } */ + vbool8_t m1 = __riscv_vmsltu_vv_u16m2_b8(pb, pa, vl); + pa = __riscv_vmerge_vvm_u16m2(pa, pb, m1, vl); + a = __riscv_vmerge_vvm_u8m1(a, b, m1, vl); + + /* if (pc < pa) a = c; */ + vbool8_t m2 = __riscv_vmsltu_vv_u16m2_b8(pc, pa, vl); + a = __riscv_vmerge_vvm_u8m1(a, c, m2, vl); + + /* a = result + x */ + a = __riscv_vadd_vv_u8m1(a, x, vl); + + /* *row = a */ + __riscv_vse8_v_u8m1(row, a, vl); + row += bpp; + + /* c = b for next iteration */ + c = b; + } +} +void +png_read_filter_row_paeth3_rvv(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t len = row_info->rowbytes; + + png_read_filter_row_paeth_rvv(len, 3, row, prev_row); +} + +void +png_read_filter_row_paeth4_rvv(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t len = row_info->rowbytes; + + png_read_filter_row_paeth_rvv(len, 4, row, prev_row); +} + +#endif /* PNG_RISCV_RVV_IMPLEMENTATION == 1 */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/3rdparty/libpng/riscv/riscv_init.c b/3rdparty/libpng/riscv/riscv_init.c new file mode 100644 index 0000000000..36a41b54f7 --- /dev/null +++ b/3rdparty/libpng/riscv/riscv_init.c @@ -0,0 +1,45 @@ +/* riscv_init.c - RISC-V Vector optimized filter functions + * + * Copyright (c) 2023 Google LLC + * Written by Dragoș Tiselice , May 2023. + * Filip Wasil , March 2025. + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_RISCV_RVV_IMPLEMENTATION == 1 + +#include + +#ifndef PNG_ALIGNED_MEMORY_SUPPORTED +# error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED" +#endif + +void +png_init_filter_functions_rvv(png_structp pp, unsigned int bpp) +{ + png_debug(1, "in png_init_filter_functions_rvv"); + + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_rvv; + + if (bpp == 3) + { + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_rvv; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth3_rvv; + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_rvv; + } + else if (bpp == 4) + { + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_rvv; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth4_rvv; + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_rvv; + } +} + +#endif /* PNG_RISCV_RVV_IMPLEMENTATION == 1 */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/3rdparty/libwebp/CMakeLists.txt b/3rdparty/libwebp/CMakeLists.txt index f3b6ebd0d6..13593526cd 100644 --- a/3rdparty/libwebp/CMakeLists.txt +++ b/3rdparty/libwebp/CMakeLists.txt @@ -21,6 +21,17 @@ if(ANDROID AND ARMEABI_V7A AND NOT NEON) endforeach() endif() +if(WIN32) + foreach(file ${lib_srcs}) + if("${file}" MATCHES "_avx2.c") + if(MSVC) + set_source_files_properties("${file}" COMPILE_FLAGS "/arch:AVX") + else() + set_source_files_properties("${file}" COMPILE_FLAGS "-mavx") + endif() + endif() + endforeach() +endif() # ---------------------------------------------------------------------------------- # Define the library target: diff --git a/3rdparty/libwebp/patches/7233156.patch b/3rdparty/libwebp/patches/7233156.patch new file mode 100644 index 0000000000..1fa125d0eb --- /dev/null +++ b/3rdparty/libwebp/patches/7233156.patch @@ -0,0 +1,21 @@ +diff --git a/3rdparty/libwebp/src/dsp/cpu.h b/3rdparty/libwebp/src/dsp/cpu.h +index 7f87d7daaa..f6e3666158 100644 +--- a/3rdparty/libwebp/src/dsp/cpu.h ++++ b/3rdparty/libwebp/src/dsp/cpu.h +@@ -94,6 +94,15 @@ + #define WEBP_HAVE_AVX2 + #endif + ++#if defined(WEBP_MSC_AVX2) && _MSC_VER <= 1900 ++#include ++static WEBP_INLINE int _mm256_extract_epi32(__m256i a, const int i) { ++ return a.m256i_i32[i & 7]; ++} ++static WEBP_INLINE int _mm256_cvtsi256_si32(__m256i a) { ++ return _mm256_extract_epi32(a, 0); ++} ++#endif ++ + #undef WEBP_MSC_AVX2 + #undef WEBP_MSC_SSE41 + #undef WEBP_MSC_SSE2 diff --git a/3rdparty/libwebp/sharpyuv/sharpyuv.c b/3rdparty/libwebp/sharpyuv/sharpyuv.c index 7cbf668fbb..25b842ddad 100644 --- a/3rdparty/libwebp/sharpyuv/sharpyuv.c +++ b/3rdparty/libwebp/sharpyuv/sharpyuv.c @@ -19,10 +19,10 @@ #include #include -#include "src/webp/types.h" #include "sharpyuv/sharpyuv_cpu.h" #include "sharpyuv/sharpyuv_dsp.h" #include "sharpyuv/sharpyuv_gamma.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ @@ -565,10 +565,11 @@ int SharpYuvConvertWithOptions(const void* r_ptr, const void* g_ptr, scaled_matrix.rgb_to_u[3] = Shift(yuv_matrix->rgb_to_u[3], sfix); scaled_matrix.rgb_to_v[3] = Shift(yuv_matrix->rgb_to_v[3], sfix); - return DoSharpArgbToYuv(r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride, - rgb_bit_depth, y_ptr, y_stride, u_ptr, u_stride, - v_ptr, v_stride, yuv_bit_depth, width, height, - &scaled_matrix, transfer_type); + return DoSharpArgbToYuv( + (const uint8_t*)r_ptr, (const uint8_t*)g_ptr, (const uint8_t*)b_ptr, + rgb_step, rgb_stride, rgb_bit_depth, (uint8_t*)y_ptr, y_stride, + (uint8_t*)u_ptr, u_stride, (uint8_t*)v_ptr, v_stride, yuv_bit_depth, + width, height, &scaled_matrix, transfer_type); } //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/sharpyuv/sharpyuv.h b/3rdparty/libwebp/sharpyuv/sharpyuv.h index fe95891599..f04bf1213b 100644 --- a/3rdparty/libwebp/sharpyuv/sharpyuv.h +++ b/3rdparty/libwebp/sharpyuv/sharpyuv.h @@ -52,7 +52,7 @@ extern "C" { // SharpYUV API version following the convention from semver.org #define SHARPYUV_VERSION_MAJOR 0 #define SHARPYUV_VERSION_MINOR 4 -#define SHARPYUV_VERSION_PATCH 0 +#define SHARPYUV_VERSION_PATCH 2 // Version as a uint32_t. The major number is the high 8 bits. // The minor number is the middle 8 bits. The patch number is the low 16 bits. #define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \ @@ -66,10 +66,17 @@ extern "C" { SHARPYUV_EXTERN int SharpYuvGetVersion(void); // RGB to YUV conversion matrix, in 16 bit fixed point. -// y = rgb_to_y[0] * r + rgb_to_y[1] * g + rgb_to_y[2] * b + rgb_to_y[3] -// u = rgb_to_u[0] * r + rgb_to_u[1] * g + rgb_to_u[2] * b + rgb_to_u[3] -// v = rgb_to_v[0] * r + rgb_to_v[1] * g + rgb_to_v[2] * b + rgb_to_v[3] -// Then y, u and v values are divided by 1<<16 and rounded. +// y_ = rgb_to_y[0] * r + rgb_to_y[1] * g + rgb_to_y[2] * b + rgb_to_y[3] +// u_ = rgb_to_u[0] * r + rgb_to_u[1] * g + rgb_to_u[2] * b + rgb_to_u[3] +// v_ = rgb_to_v[0] * r + rgb_to_v[1] * g + rgb_to_v[2] * b + rgb_to_v[3] +// Then the values are divided by 1<<16 and rounded. +// y = (y_ + (1 << 15)) >> 16 +// u = (u_ + (1 << 15)) >> 16 +// v = (v_ + (1 << 15)) >> 16 +// +// Typically, the offset values rgb_to_y[3], rgb_to_u[3] and rgb_to_v[3] depend +// on the input's bit depth, e.g., rgb_to_u[3] = 1 << (rgb_bit_depth - 1 + 16). +// See also sharpyuv_csp.h to get a predefined matrix or generate a matrix. typedef struct { int rgb_to_y[4]; int rgb_to_u[4]; @@ -127,6 +134,8 @@ typedef enum SharpYuvTransferFunctionType { // adjacent pixels on the y, u and v channels. If yuv_bit_depth > 8, they // should be multiples of 2. // width, height: width and height of the image in pixels +// yuv_matrix: RGB to YUV conversion matrix. The matrix values typically +// depend on the input's rgb_bit_depth. // This function calls SharpYuvConvertWithOptions with a default transfer // function of kSharpYuvTransferFunctionSrgb. SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr, diff --git a/3rdparty/libwebp/sharpyuv/sharpyuv_csp.c b/3rdparty/libwebp/sharpyuv/sharpyuv_csp.c index 0ad22be945..9714130e89 100644 --- a/3rdparty/libwebp/sharpyuv/sharpyuv_csp.c +++ b/3rdparty/libwebp/sharpyuv/sharpyuv_csp.c @@ -15,6 +15,8 @@ #include #include +#include "sharpyuv/sharpyuv.h" + static int ToFixed16(float f) { return (int)floor(f * (1 << 16) + 0.5f); } void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space, @@ -22,16 +24,16 @@ void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space, const float kr = yuv_color_space->kr; const float kb = yuv_color_space->kb; const float kg = 1.0f - kr - kb; - const float cr = 0.5f / (1.0f - kb); - const float cb = 0.5f / (1.0f - kr); + const float cb = 0.5f / (1.0f - kb); + const float cr = 0.5f / (1.0f - kr); const int shift = yuv_color_space->bit_depth - 8; const float denom = (float)((1 << yuv_color_space->bit_depth) - 1); float scale_y = 1.0f; float add_y = 0.0f; - float scale_u = cr; - float scale_v = cb; + float scale_u = cb; + float scale_v = cr; float add_uv = (float)(128 << shift); assert(yuv_color_space->bit_depth >= 8); @@ -59,31 +61,35 @@ void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space, } // Matrices are in YUV_FIX fixed point precision. -// WebP's matrix, similar but not identical to kRec601LimitedMatrix. +// WebP's matrix, similar but not identical to kRec601LimitedMatrix +// Derived using the following formulas: +// 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 static const SharpYuvConversionMatrix kWebpMatrix = { {16839, 33059, 6420, 16 << 16}, {-9719, -19081, 28800, 128 << 16}, {28800, -24116, -4684, 128 << 16}, }; -// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeLimited +// Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeLimited static const SharpYuvConversionMatrix kRec601LimitedMatrix = { {16829, 33039, 6416, 16 << 16}, {-9714, -19071, 28784, 128 << 16}, {28784, -24103, -4681, 128 << 16}, }; -// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeFull +// Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeFull static const SharpYuvConversionMatrix kRec601FullMatrix = { {19595, 38470, 7471, 0}, {-11058, -21710, 32768, 128 << 16}, {32768, -27439, -5329, 128 << 16}, }; -// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeLimited +// Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeLimited static const SharpYuvConversionMatrix kRec709LimitedMatrix = { {11966, 40254, 4064, 16 << 16}, {-6596, -22189, 28784, 128 << 16}, {28784, -26145, -2639, 128 << 16}, }; -// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeFull +// Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeFull static const SharpYuvConversionMatrix kRec709FullMatrix = { {13933, 46871, 4732, 0}, {-7509, -25259, 32768, 128 << 16}, diff --git a/3rdparty/libwebp/sharpyuv/sharpyuv_csp.h b/3rdparty/libwebp/sharpyuv/sharpyuv_csp.h index 3214e3ac60..efc01053a0 100644 --- a/3rdparty/libwebp/sharpyuv/sharpyuv_csp.h +++ b/3rdparty/libwebp/sharpyuv/sharpyuv_csp.h @@ -41,10 +41,15 @@ SHARPYUV_EXTERN void SharpYuvComputeConversionMatrix( // Enums for precomputed conversion matrices. typedef enum { + // WebP's matrix, similar but not identical to kSharpYuvMatrixRec601Limited kSharpYuvMatrixWebp = 0, + // Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeLimited kSharpYuvMatrixRec601Limited, + // Kr=0.2990f Kb=0.1140f bit_depth=8 range=kSharpYuvRangeFull kSharpYuvMatrixRec601Full, + // Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeLimited kSharpYuvMatrixRec709Limited, + // Kr=0.2126f Kb=0.0722f bit_depth=8 range=kSharpYuvRangeFull kSharpYuvMatrixRec709Full, kSharpYuvMatrixNum } SharpYuvMatrixType; diff --git a/3rdparty/libwebp/sharpyuv/sharpyuv_dsp.c b/3rdparty/libwebp/sharpyuv/sharpyuv_dsp.c index 94a40ec686..3d2cf85ffc 100644 --- a/3rdparty/libwebp/sharpyuv/sharpyuv_dsp.c +++ b/3rdparty/libwebp/sharpyuv/sharpyuv_dsp.c @@ -17,6 +17,7 @@ #include #include "sharpyuv/sharpyuv_cpu.h" +#include "src/dsp/cpu.h" #include "src/webp/types.h" //----------------------------------------------------------------------------- diff --git a/3rdparty/libwebp/sharpyuv/sharpyuv_gamma.c b/3rdparty/libwebp/sharpyuv/sharpyuv_gamma.c index 09028428ac..f72be4b89e 100644 --- a/3rdparty/libwebp/sharpyuv/sharpyuv_gamma.c +++ b/3rdparty/libwebp/sharpyuv/sharpyuv_gamma.c @@ -15,6 +15,7 @@ #include #include +#include "sharpyuv/sharpyuv.h" #include "src/webp/types.h" // Gamma correction compensates loss of resolution during chroma subsampling. diff --git a/3rdparty/libwebp/sharpyuv/sharpyuv_sse2.c b/3rdparty/libwebp/sharpyuv/sharpyuv_sse2.c index 9744d1bb6c..e085c43ebb 100644 --- a/3rdparty/libwebp/sharpyuv/sharpyuv_sse2.c +++ b/3rdparty/libwebp/sharpyuv/sharpyuv_sse2.c @@ -14,9 +14,13 @@ #include "sharpyuv/sharpyuv_dsp.h" #if defined(WEBP_USE_SSE2) -#include #include +#include + +#include "src/dsp/cpu.h" +#include "src/webp/types.h" + static uint16_t clip_SSE2(int v, int max) { return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v; } diff --git a/3rdparty/libwebp/src/dec/alpha_dec.c b/3rdparty/libwebp/src/dec/alpha_dec.c index b6c874fb84..d90bfd2be0 100644 --- a/3rdparty/libwebp/src/dec/alpha_dec.c +++ b/3rdparty/libwebp/src/dec/alpha_dec.c @@ -11,14 +11,18 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include #include + #include "src/dec/alphai_dec.h" #include "src/dec/vp8_dec.h" #include "src/dec/vp8i_dec.h" #include "src/dec/vp8li_dec.h" +#include "src/dec/webpi_dec.h" #include "src/dsp/dsp.h" #include "src/utils/quant_levels_dec_utils.h" #include "src/utils/utils.h" +#include "src/webp/decode.h" #include "src/webp/format_constants.h" #include "src/webp/types.h" @@ -34,8 +38,8 @@ WEBP_NODISCARD static ALPHDecoder* ALPHNew(void) { // Clears and deallocates an alpha decoder instance. static void ALPHDelete(ALPHDecoder* const dec) { if (dec != NULL) { - VP8LDelete(dec->vp8l_dec_); - dec->vp8l_dec_ = NULL; + VP8LDelete(dec->vp8l_dec); + dec->vp8l_dec = NULL; WebPSafeFree(dec); } } @@ -54,28 +58,28 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, 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_; + 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); + 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; + 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 || + 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; } @@ -96,11 +100,11 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, 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_; + 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); + assert(dec->method == ALPHA_LOSSLESS_COMPRESSION); ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size); } @@ -113,32 +117,32 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, // Returns false in case of bitstream error. WEBP_NODISCARD 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) { + 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_]); - assert(WebPUnfilters[alph_dec->filter_] != NULL); + 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]); + assert(WebPUnfilters[alph_dec->filter] != NULL); for (y = 0; y < num_rows; ++y) { - WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width); + WebPUnfilters[alph_dec->filter](prev_line, deltas, dst, width); 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); + 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; + dec->is_alpha_decoded = 1; } return 1; } @@ -148,25 +152,25 @@ WEBP_NODISCARD static int AllocateAlphaPlane(VP8Decoder* const dec, 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) { + 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 VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, "Alpha decoder initialization failed."); } - dec->alpha_plane_ = dec->alpha_plane_mem_; - dec->alpha_prev_line_ = NULL; + 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; + WebPSafeFree(dec->alpha_plane_mem); + dec->alpha_plane_mem = NULL; + dec->alpha_plane = NULL; + ALPHDelete(dec->alph_dec); + dec->alph_dec = NULL; } //------------------------------------------------------------------------------ @@ -184,46 +188,46 @@ WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, return NULL; } - if (!dec->is_alpha_decoded_) { - if (dec->alph_dec_ == NULL) { // Initialize decoder. - dec->alph_dec_ = ALPHNew(); - if (dec->alph_dec_ == NULL) { + if (!dec->is_alpha_decoded) { + if (dec->alph_dec == NULL) { // Initialize decoder. + dec->alph_dec = ALPHNew(); + if (dec->alph_dec == NULL) { VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, "Alpha decoder initialization failed."); return NULL; } if (!AllocateAlphaPlane(dec, io)) goto Error; - if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_, - io, dec->alpha_plane_)) { - VP8LDecoder* const vp8l_dec = dec->alph_dec_->vp8l_dec_; + if (!ALPHInit(dec->alph_dec, dec->alpha_data, dec->alpha_data_size, + io, dec->alpha_plane)) { + VP8LDecoder* const vp8l_dec = dec->alph_dec->vp8l_dec; VP8SetError(dec, (vp8l_dec == NULL) ? VP8_STATUS_OUT_OF_MEMORY - : vp8l_dec->status_, + : vp8l_dec->status, "Alpha decoder initialization failed."); 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 + 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(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 + 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_)) { + width, dec->alpha_dithering)) { goto Error; } } @@ -231,7 +235,7 @@ WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, } // Return a pointer to the current decoded row. - return dec->alpha_plane_ + row * width; + return dec->alpha_plane + row * width; Error: WebPDeallocateAlphaMemory(dec); diff --git a/3rdparty/libwebp/src/dec/alphai_dec.h b/3rdparty/libwebp/src/dec/alphai_dec.h index a64104abeb..49150318fb 100644 --- a/3rdparty/libwebp/src/dec/alphai_dec.h +++ b/3rdparty/libwebp/src/dec/alphai_dec.h @@ -14,7 +14,10 @@ #ifndef WEBP_DEC_ALPHAI_DEC_H_ #define WEBP_DEC_ALPHAI_DEC_H_ +#include "src/dec/vp8_dec.h" +#include "src/webp/types.h" #include "src/dec/webpi_dec.h" +#include "src/dsp/dsp.h" #include "src/utils/filters_utils.h" #ifdef __cplusplus @@ -25,24 +28,24 @@ 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 + 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) + 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 +// Deallocate memory associated to dec->alpha_plane decoding void WebPDeallocateAlphaMemory(VP8Decoder* const dec); //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/dec/buffer_dec.c b/3rdparty/libwebp/src/dec/buffer_dec.c index 11ce76f19e..290f7ab3b5 100644 --- a/3rdparty/libwebp/src/dec/buffer_dec.c +++ b/3rdparty/libwebp/src/dec/buffer_dec.c @@ -11,11 +11,16 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include #include +#include #include "src/dec/vp8i_dec.h" #include "src/dec/webpi_dec.h" +#include "src/utils/rescaler_utils.h" #include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // WebPDecBuffer @@ -26,10 +31,9 @@ static const uint8_t kModeBpp[MODE_LAST] = { 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) { +int IsValidColorspace(int webp_csp_mode) { return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST); } diff --git a/3rdparty/libwebp/src/dec/common_dec.h b/3rdparty/libwebp/src/dec/common_dec.h index b158550a80..4a581cb365 100644 --- a/3rdparty/libwebp/src/dec/common_dec.h +++ b/3rdparty/libwebp/src/dec/common_dec.h @@ -51,4 +51,7 @@ enum { MB_FEATURE_TREE_PROBS = 3, NUM_PROBAS = 11 }; +// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE. +int IsValidColorspace(int webp_csp_mode); + #endif // WEBP_DEC_COMMON_DEC_H_ diff --git a/3rdparty/libwebp/src/dec/frame_dec.c b/3rdparty/libwebp/src/dec/frame_dec.c index 91ca1f8609..8780772238 100644 --- a/3rdparty/libwebp/src/dec/frame_dec.c +++ b/3rdparty/libwebp/src/dec/frame_dec.c @@ -11,9 +11,20 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include #include +#include + +#include "src/dec/common_dec.h" +#include "src/dec/vp8_dec.h" #include "src/dec/vp8i_dec.h" +#include "src/dec/webpi_dec.h" +#include "src/dsp/dsp.h" +#include "src/utils/random_utils.h" +#include "src/utils/thread_utils.h" #include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Main reconstruction function. @@ -72,11 +83,11 @@ 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; + 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) { @@ -99,8 +110,8 @@ static void ReconstructRow(const VP8Decoder* const dec, } // Reconstruct one row. - for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) { - const VP8MBData* const block = ctx->mb_data_ + mb_x; + 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. @@ -115,9 +126,9 @@ static void ReconstructRow(const VP8Decoder* const dec, } { // 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_; + 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) { @@ -127,11 +138,11 @@ static void ReconstructRow(const VP8Decoder* const dec, } // predict and add residuals - if (block->is_i4x4_) { // 4x4 + 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 + 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)); @@ -143,11 +154,11 @@ static void ReconstructRow(const VP8Decoder* const dec, // 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); + 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]); + 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) { @@ -157,8 +168,8 @@ static void ReconstructRow(const VP8Decoder* const dec, } { // Chroma - const uint32_t bits_uv = block->non_zero_uv_; - const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_); + 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); @@ -166,25 +177,25 @@ static void ReconstructRow(const VP8Decoder* const dec, } // stash away top samples for next block - if (mb_y < dec->mb_h_ - 1) { + 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. + // 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; + 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); + 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); + 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); } } } @@ -201,40 +212,40 @@ static void ReconstructRow(const VP8Decoder* const dec, 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_; + 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 (dec->filter_type == 1) { // simple if (mb_x > 0) { VP8SimpleHFilter16(y_dst, y_bps, limit + 4); } - if (f_info->f_inner_) { + 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_) { + 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_; + 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_) { + 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); } @@ -242,7 +253,7 @@ static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) { 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_) { + 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); } @@ -252,9 +263,9 @@ static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) { // 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) { + 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); } } @@ -263,51 +274,51 @@ static void FilterRow(const VP8Decoder* const dec) { // Precompute the filtering strength for each segment and each i4x4/i16x16 mode. static void PrecomputeFilterStrengths(VP8Decoder* const dec) { - if (dec->filter_type_ > 0) { + if (dec->filter_type > 0) { int s; - const VP8FilterHeader* const hdr = &dec->filter_hdr_; + 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_; + 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_; + base_level = hdr->level; } for (i4x4 = 0; i4x4 <= 1; ++i4x4) { - VP8FInfo* const info = &dec->fstrengths_[s][i4x4]; + VP8FInfo* const info = &dec->fstrengths[s][i4x4]; int level = base_level; - if (hdr->use_lf_delta_) { - level += hdr->ref_lf_delta_[0]; + if (hdr->use_lf_delta) { + level += hdr->ref_lf_delta[0]; if (i4x4) { - level += hdr->mode_lf_delta_[0]; + 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) { + if (hdr->sharpness > 0) { + if (hdr->sharpness > 4) { ilevel >>= 2; } else { ilevel >>= 1; } - if (ilevel > 9 - hdr->sharpness_) { - ilevel = 9 - hdr->sharpness_; + 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; + 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_limit = 0; // no filtering } - info->f_inner_ = i4x4; + info->f_inner = i4x4; } } } @@ -321,7 +332,7 @@ static void PrecomputeFilterStrengths(VP8Decoder* const dec) { #define DITHER_AMP_TAB_SIZE 12 static const uint8_t kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = { - // roughly, it's dqm->uv_mat_[1] + // roughly, it's dqm->uv_mat[1] 8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1 }; @@ -336,24 +347,24 @@ void VP8InitDithering(const WebPDecoderOptions* const options, 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; + 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_; + all_amp |= dqm->dither; } if (all_amp != 0) { - VP8InitRandom(&dec->dithering_rg_, 1.0f); - dec->dither_ = 1; + 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; + 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; } } } @@ -370,17 +381,17 @@ static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) { 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_); + 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); } } } @@ -403,29 +414,29 @@ 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 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); + const int is_last_row = (mb_y >= dec->br_mb_y - 1); - if (dec->mt_method_ == 2) { + if (dec->mt_method == 2) { ReconstructRow(dec, ctx); } - if (ctx->filter_row_) { + if (ctx->filter_row) { FilterRow(dec); } - if (dec->dither_) { + if (dec->dither) { DitherRow(dec); } @@ -438,9 +449,9 @@ static int FinishRow(void* arg1, void* arg2) { 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; + 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) { @@ -449,9 +460,9 @@ static int FinishRow(void* arg1, void* arg2) { 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. + // 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) { + 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, @@ -462,9 +473,9 @@ static int FinishRow(void* arg1, void* arg2) { 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); + 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; } @@ -483,11 +494,11 @@ static int FinishRow(void* arg1, void* arg2) { } } // rotate top samples if needed - if (cache_id + 1 == dec->num_caches_) { + 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); + 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); } } @@ -500,43 +511,43 @@ static int FinishRow(void* arg1, void* arg2) { int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) { int ok = 1; - VP8ThreadContext* const ctx = &dec->thread_ctx_; + 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; + (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_; + WebPWorker* const worker = &dec->worker; // Finish previous job *before* updating context ok &= WebPGetWorkerInterface()->Sync(worker); - assert(worker->status_ == OK); + 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; + 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; + 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; + if (++dec->cache_id == dec->num_caches) { + dec->cache_id = 0; } } } @@ -551,12 +562,12 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const 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_; + return dec->status; } // Disable filtering per user request if (io->bypass_filtering) { - dec->filter_type_ = 0; + dec->filter_type = 0; } // Define the area where we can skip in-loop filtering, in case of cropping. @@ -569,29 +580,29 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) { // 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) { + 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; + 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; + 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_; + 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_; + if (dec->br_mb_y > dec->mb_h) { + dec->br_mb_y = dec->mb_h; } } PrecomputeFilterStrengths(dec); @@ -600,8 +611,8 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) { int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) { int ok = 1; - if (dec->mt_method_ > 0) { - ok = WebPGetWorkerInterface()->Sync(&dec->worker_); + if (dec->mt_method > 0) { + ok = WebPGetWorkerInterface()->Sync(&dec->worker); } if (io->teardown != NULL) { @@ -639,20 +650,20 @@ int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) { // 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_; + 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->data2 = (void*)&dec->thread_ctx.io; worker->hook = FinishRow; - dec->num_caches_ = - (dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1; + dec->num_caches = + (dec->filter_type > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1; } else { - dec->num_caches_ = ST_CACHE_LINES; + dec->num_caches = ST_CACHE_LINES; } return 1; } @@ -680,25 +691,25 @@ int VP8GetThreadMethod(const WebPDecoderOptions* const options, // Memory setup static int AllocateMemory(VP8Decoder* const dec) { - const int num_caches = dec->num_caches_; - const int mb_w = dec->mb_w_; + 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) + (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 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_); + (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; + + 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 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 @@ -706,77 +717,77 @@ static int AllocateMemory(VP8Decoder* const dec) { 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) { + 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; + dec->mem_size = (size_t)needed; } - mem = (uint8_t*)dec->mem_; - dec->intra_t_ = mem; + mem = (uint8_t*)dec->mem; + dec->intra_t = mem; mem += intra_pred_mode_size; - dec->yuv_t_ = (VP8TopSamples*)mem; + dec->yuv_t = (VP8TopSamples*)mem; mem += top_size; - dec->mb_info_ = ((VP8MB*)mem) + 1; + dec->mb_info = ((VP8MB*)mem) + 1; mem += mb_info_size; - dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL; + 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) { + 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; + dec->thread_ctx.f_info += mb_w; } mem = (uint8_t*)WEBP_ALIGN(mem); assert((yuv_size & WEBP_ALIGN_CST) == 0); - dec->yuv_b_ = mem; + 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; + 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; + 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; + 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; + dec->alpha_plane = alpha_size ? mem : NULL; mem += alpha_size; - assert(mem <= (uint8_t*)dec->mem_ + dec->mem_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); + 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); + memset(dec->intra_t, B_DC_PRED, intra_pred_mode_size); return 1; } @@ -784,16 +795,16 @@ static int AllocateMemory(VP8Decoder* const dec) { 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->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 (!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. diff --git a/3rdparty/libwebp/src/dec/idec_dec.c b/3rdparty/libwebp/src/dec/idec_dec.c index ad042a1ffc..cf8a33a495 100644 --- a/3rdparty/libwebp/src/dec/idec_dec.c +++ b/3rdparty/libwebp/src/dec/idec_dec.c @@ -12,15 +12,20 @@ // Author: somnath@google.com (Somnath Banerjee) #include -#include #include +#include #include "src/dec/alphai_dec.h" -#include "src/dec/webpi_dec.h" #include "src/dec/vp8_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_utils.h" +#include "src/utils/thread_utils.h" #include "src/utils/utils.h" #include "src/webp/decode.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" // In append mode, buffer allocations increase as multiples of this value. // Needs to be a power of 2. @@ -54,134 +59,140 @@ typedef enum { // 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() + 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 + 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_; + 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. + 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 + 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_; + 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_); + 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) { + 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_) { + 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_; + 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 + 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); + 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 (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); + 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); + 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); + const uint8_t* const last_start = dec->parts[last_part].buf; + // 'last_start' will be NULL when 'idec->state' is < STATE_VP8_PARTS0 + // and through a portion of that state (when there isn't enough data to + // parse the partitions). The bitreader is only used meaningfully when + // there is enough data to begin parsing partition 0. + if (last_start != NULL) { + 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 + 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)); + 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 +// 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. WEBP_NODISCARD 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_; + 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_; + (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); + 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 + 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; @@ -190,85 +201,85 @@ WEBP_NODISCARD static int AppendToMemBuffer(WebPIDecoder* const idec, (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; + 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_); + 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); + DoRemap(idec, mem->buf + mem->start - old_start); return 1; } WEBP_NODISCARD 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_; + 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); + (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! + 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; + mem->buf = (uint8_t*)data; + mem->end = mem->buf_size = data_size; - DoRemap(idec, mem->buf_ + mem->start_ - old_start); + 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; + 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_); + if (mem->mode == MEM_MODE_APPEND) { + WebPSafeFree(mem->buf); + WebPSafeFree((void*)mem->part0_buf); } } WEBP_NODISCARD 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) { + 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 + assert(mem->mode == expected); // mode is ok return 1; } // To be called last. WEBP_NODISCARD static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { - const WebPDecoderOptions* const options = idec->params_.options; - WebPDecBuffer* const output = idec->params_.output; + const WebPDecoderOptions* const options = idec->params.options; + WebPDecBuffer* const output = idec->params.output; - idec->state_ = STATE_DONE; + 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) { + if (idec->final_output != NULL) { const VP8StatusCode status = WebPCopyDecBufferPixels( - output, idec->final_output_); // do the slow-copy - WebPFreeDecBuffer(&idec->output_); + output, idec->final_output); // do the slow-copy + WebPFreeDecBuffer(&idec->output); if (status != VP8_STATUS_OK) return status; - *output = *idec->final_output_; - idec->final_output_ = NULL; + *output = *idec->final_output; + idec->final_output = NULL; } return VP8_STATUS_OK; } @@ -278,43 +289,43 @@ WEBP_NODISCARD static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { 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; + 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_; + 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) { + if (idec->state == STATE_VP8_DATA) { // Synchronize the thread, clean-up and check for errors. - (void)VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); + (void)VP8ExitCritical((VP8Decoder*)idec->dec, &idec->io); } - idec->state_ = STATE_ERROR; + 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); + 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_; + MemBuffer* const mem = &idec->mem; + const uint8_t* data = mem->buf + mem->start; size_t curr_size = MemDataSize(mem); VP8StatusCode status; WebPHeaderStructure headers; @@ -329,32 +340,32 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) { return IDecError(idec, status); } - idec->chunk_size_ = headers.compressed_size; - idec->is_lossless_ = headers.is_lossless; - if (!idec->is_lossless_) { + 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; } - dec->incremental_ = 1; - idec->dec_ = dec; - dec->alpha_data_ = headers.alpha_data; - dec->alpha_data_size_ = headers.alpha_data_size; + dec->incremental = 1; + 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; + 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_); + const uint8_t* data = idec->mem.buf + idec->mem.start; + const size_t curr_size = MemDataSize(&idec->mem); int width, height; uint32_t bits; @@ -362,61 +373,61 @@ static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) { // Not enough data bytes to extract VP8 Frame Header. return VP8_STATUS_SUSPENDED; } - if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) { + 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->mem.part0_size = (bits >> 5) + VP8_FRAME_HEADER_SIZE; - idec->io_.data = data; - idec->io_.data_size = curr_size; - idec->state_ = STATE_VP8_PARTS0; + 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); + 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_); + 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) { + 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; + 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_. + // Else: just keep pointers to the partition #0's data in dec->br. } - mem->start_ += part_size; + 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_; + 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_) { + if (MemDataSize(&idec->mem) < idec->mem.part0_size) { return VP8_STATUS_SUSPENDED; } if (!VP8GetHeaders(dec, io)) { - const VP8StatusCode status = dec->status_; + const VP8StatusCode status = dec->status; if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) { // treating NOT_ENOUGH_DATA as SUSPENDED state @@ -426,69 +437,69 @@ static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) { } // 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_); + 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); + 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_); + 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_); + return IDecError(idec, dec->status); } // Note: past this point, teardown() must always be called // in case of error. - idec->state_ = STATE_VP8_DATA; + idec->state = STATE_VP8_DATA; // Allocate memory and prepare everything. if (!VP8InitFrame(dec, io)) { - return IDecError(idec, dec->status_); + 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_; + 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_) { + // 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)) { + 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_; + idec->last_mb_y = dec->mb_y; } - for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) { + for (; dec->mb_x < dec->mb_w; ++dec->mb_x) { VP8BitReader* const token_br = - &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_]; + &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) { + 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_)) { + if (dec->mt_method > 0) { + if (!WebPGetWorkerInterface()->Sync(&dec->worker)) { return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); } } @@ -496,9 +507,9 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { 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_); + 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 @@ -510,10 +521,10 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { } // Synchronize the thread and check for errors. if (!VP8ExitCritical(dec, io)) { - idec->state_ = STATE_ERROR; // prevent re-entry in IDecError + idec->state = STATE_ERROR; // prevent re-entry in IDecError return IDecError(idec, VP8_STATUS_USER_ABORT); } - dec->ready_ = 0; + dec->ready = 0; return FinishDecoding(idec); } @@ -526,81 +537,81 @@ static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec, } static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) { - VP8Io* const io = &idec->io_; - VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; - const WebPDecParams* const params = &idec->params_; + 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_); + 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 (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; + if (dec->status == VP8_STATUS_BITSTREAM_ERROR && + curr_size < idec->chunk_size) { + dec->status = VP8_STATUS_SUSPENDED; } - return ErrorStatusLossless(idec, dec->status_); + 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_); + 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; + 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_); + 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_); + dec->incremental = (curr_size < idec->chunk_size); if (!VP8LDecodeImage(dec)) { - return ErrorStatusLossless(idec, dec->status_); + 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); + 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) { + if (idec->state == STATE_WEBP_HEADER) { status = DecodeWebPHeaders(idec); } else { - if (idec->dec_ == NULL) { + if (idec->dec == NULL) { return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder. } } - if (idec->state_ == STATE_VP8_HEADER) { + if (idec->state == STATE_VP8_HEADER) { status = DecodeVP8FrameHeader(idec); } - if (idec->state_ == STATE_VP8_PARTS0) { + if (idec->state == STATE_VP8_PARTS0) { status = DecodePartition0(idec); } - if (idec->state_ == STATE_VP8_DATA) { - const VP8Decoder* const dec = (VP8Decoder*)idec->dec_; + 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) { + if (idec->state == STATE_VP8L_HEADER) { status = DecodeVP8LHeader(idec); } - if (idec->state_ == STATE_VP8L_DATA) { + if (idec->state == STATE_VP8L_DATA) { status = DecodeVP8LData(idec); } return status; @@ -617,29 +628,29 @@ WEBP_NODISCARD static WebPIDecoder* NewDecoder( return NULL; } - idec->state_ = STATE_WEBP_HEADER; - idec->chunk_size_ = 0; + idec->state = STATE_WEBP_HEADER; + idec->chunk_size = 0; - idec->last_mb_y_ = -1; + idec->last_mb_y = -1; - InitMemBuffer(&idec->mem_); - if (!WebPInitDecBuffer(&idec->output_) || !VP8InitIo(&idec->io_)) { + InitMemBuffer(&idec->mem); + if (!WebPInitDecBuffer(&idec->output) || !VP8InitIo(&idec->io)) { WebPSafeFree(idec); return NULL; } - WebPResetDecParams(&idec->params_); + WebPResetDecParams(&idec->params); if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { - idec->params_.output = &idec->output_; - idec->final_output_ = output_buffer; + idec->params.output = &idec->output; + idec->final_output = output_buffer; if (output_buffer != NULL) { - idec->params_.output->colorspace = output_buffer->colorspace; + idec->params.output->colorspace = output_buffer->colorspace; } } else { - idec->params_.output = output_buffer; - idec->final_output_ = NULL; + idec->params.output = output_buffer; + idec->final_output = NULL; } - WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions. + WebPInitCustomIo(&idec->params, &idec->io); // Plug the I/O functions. return idec; } @@ -674,27 +685,27 @@ WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, } // Finish initialization if (config != NULL) { - idec->params_.options = &config->options; + 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) { + if (idec->dec != NULL) { + if (!idec->is_lossless) { + if (idec->state == STATE_VP8_DATA) { // Synchronize the thread, clean-up and check for errors. // TODO(vrabaud) do we care about the return result? - (void)VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); + (void)VP8ExitCritical((VP8Decoder*)idec->dec, &idec->io); } - VP8Delete((VP8Decoder*)idec->dec_); + VP8Delete((VP8Decoder*)idec->dec); } else { - VP8LDelete((VP8LDecoder*)idec->dec_); + VP8LDelete((VP8LDecoder*)idec->dec); } } - ClearMemBuffer(&idec->mem_); - WebPFreeDecBuffer(&idec->output_); + ClearMemBuffer(&idec->mem); + WebPFreeDecBuffer(&idec->output); WebPSafeFree(idec); } @@ -717,11 +728,11 @@ WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, uint8_t* output_buffer, } 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; + 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; } @@ -751,20 +762,20 @@ WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride, 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; + 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; } @@ -781,10 +792,10 @@ WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride, static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) { assert(idec); - if (idec->state_ == STATE_ERROR) { + if (idec->state == STATE_ERROR) { return VP8_STATUS_BITSTREAM_ERROR; } - if (idec->state_ == STATE_DONE) { + if (idec->state == STATE_DONE) { return VP8_STATUS_OK; } return VP8_STATUS_SUSPENDED; @@ -801,7 +812,7 @@ VP8StatusCode WebPIAppend(WebPIDecoder* idec, return status; } // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. - if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) { + if (!CheckMemBufferMode(&idec->mem, MEM_MODE_APPEND)) { return VP8_STATUS_INVALID_PARAM; } // Append data to memory buffer @@ -822,7 +833,7 @@ VP8StatusCode WebPIUpdate(WebPIDecoder* idec, return status; } // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. - if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) { + if (!CheckMemBufferMode(&idec->mem, MEM_MODE_MAP)) { return VP8_STATUS_INVALID_PARAM; } // Make the memory buffer point to the new buffer @@ -835,16 +846,16 @@ VP8StatusCode WebPIUpdate(WebPIDecoder* idec, //------------------------------------------------------------------------------ static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { - if (idec == NULL || idec->dec_ == NULL) { + if (idec == NULL || idec->dec == NULL) { return NULL; } - if (idec->state_ <= STATE_VP8_PARTS0) { + if (idec->state <= STATE_VP8_PARTS0) { return NULL; } - if (idec->final_output_ != NULL) { + if (idec->final_output != NULL) { return NULL; // not yet slow-copied } - return idec->params_.output; + return idec->params.output; } const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, @@ -855,7 +866,7 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, if (top != NULL) *top = 0; if (src != NULL) { if (width != NULL) *width = src->width; - if (height != NULL) *height = idec->params_.last_y; + if (height != NULL) *height = idec->params.last_y; } else { if (width != NULL) *width = 0; if (height != NULL) *height = 0; @@ -871,7 +882,7 @@ WEBP_NODISCARD uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y, return NULL; } - if (last_y != NULL) *last_y = idec->params_.last_y; + 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; @@ -889,7 +900,7 @@ WEBP_NODISCARD uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y, return NULL; } - if (last_y != NULL) *last_y = idec->params_.last_y; + 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; @@ -907,14 +918,14 @@ int WebPISetIOHooks(WebPIDecoder* const idec, VP8IoSetupHook setup, VP8IoTeardownHook teardown, void* user_data) { - if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) { + 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; + idec->io.put = put; + idec->io.setup = setup; + idec->io.teardown = teardown; + idec->io.opaque = user_data; return 1; } diff --git a/3rdparty/libwebp/src/dec/io_dec.c b/3rdparty/libwebp/src/dec/io_dec.c index 5ef6298886..b6e720ede6 100644 --- a/3rdparty/libwebp/src/dec/io_dec.c +++ b/3rdparty/libwebp/src/dec/io_dec.c @@ -12,12 +12,20 @@ // Author: Skal (pascal.massimino@gmail.com) #include +#include #include +#include + +#include "src/dec/vp8_dec.h" +#include "src/webp/types.h" #include "src/dec/vp8i_dec.h" #include "src/dec/webpi_dec.h" +#include "src/dsp/cpu.h" #include "src/dsp/dsp.h" #include "src/dsp/yuv.h" +#include "src/utils/rescaler_utils.h" #include "src/utils/utils.h" +#include "src/webp/decode.h" //------------------------------------------------------------------------------ // Main YUV<->RGB conversion functions @@ -25,9 +33,9 @@ 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; + uint8_t* const y_dst = buf->y + (ptrdiff_t)io->mb_y * buf->y_stride; + uint8_t* const u_dst = buf->u + (ptrdiff_t)(io->mb_y >> 1) * buf->u_stride; + uint8_t* const v_dst = buf->v + (ptrdiff_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; @@ -42,7 +50,7 @@ static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) { 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; + uint8_t* const dst = buf->rgba + (ptrdiff_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, @@ -57,7 +65,7 @@ static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) { 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; + uint8_t* dst = buf->rgba + (ptrdiff_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; @@ -128,7 +136,7 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p, 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; + uint8_t* dst = buf->a + (ptrdiff_t)io->mb_y * buf->a_stride; int j; (void)expected_num_lines_out; assert(expected_num_lines_out == mb_h); @@ -181,8 +189,8 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p, (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; + const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); + uint8_t* const base_rgba = buf->rgba + (ptrdiff_t)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); @@ -205,8 +213,8 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p, 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; + const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows); + uint8_t* const base_rgba = buf->rgba + (ptrdiff_t)start_y * buf->stride; #if (WEBP_SWAP_16BIT_CSP == 1) uint8_t* alpha_dst = base_rgba; #else @@ -257,7 +265,7 @@ static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) { 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_). + // 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); @@ -271,9 +279,9 @@ static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) { 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; + uint8_t* const dst_a = buf->a + (ptrdiff_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; + uint8_t* const dst_y = buf->y + (ptrdiff_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 @@ -362,7 +370,7 @@ 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; + uint8_t* dst = buf->rgba + (ptrdiff_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. @@ -389,14 +397,14 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) { 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); + io->y + (ptrdiff_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, + p->scaler_u, uv_mb_h - uv_j, io->u + (ptrdiff_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, + p->scaler_v, uv_mb_h - uv_j, io->v + (ptrdiff_t)uv_j * io->uv_stride, io->uv_stride); (void)v_lines_in; // remove a gcc warning assert(u_lines_in == v_lines_in); @@ -409,7 +417,7 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) { 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; + uint8_t* const base_rgba = buf->rgba + (ptrdiff_t)y_pos * buf->stride; const WEBP_CSP_MODE colorspace = p->output->colorspace; const int alpha_first = (colorspace == MODE_ARGB || colorspace == MODE_Argb); @@ -437,7 +445,7 @@ static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_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; + uint8_t* const base_rgba = buf->rgba + (ptrdiff_t)y_pos * buf->stride; #if (WEBP_SWAP_16BIT_CSP == 1) uint8_t* alpha_dst = base_rgba; #else @@ -476,7 +484,7 @@ static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p, 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; + const int64_t row_offset = (ptrdiff_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); diff --git a/3rdparty/libwebp/src/dec/quant_dec.c b/3rdparty/libwebp/src/dec/quant_dec.c index a0ac018b0f..977bec56cd 100644 --- a/3rdparty/libwebp/src/dec/quant_dec.c +++ b/3rdparty/libwebp/src/dec/quant_dec.c @@ -11,7 +11,11 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include "src/dec/common_dec.h" +#include "src/dec/vp8_dec.h" #include "src/dec/vp8i_dec.h" +#include "src/utils/bit_reader_utils.h" +#include "src/webp/types.h" static WEBP_INLINE int clip(int v, int M) { return v < 0 ? 0 : v > M ? M : v; @@ -60,7 +64,7 @@ static const uint16_t kAcTable[128] = { // Paragraph 9.6 void VP8ParseQuant(VP8Decoder* const dec) { - VP8BitReader* const br = &dec->br_; + 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; @@ -73,43 +77,42 @@ void VP8ParseQuant(VP8Decoder* const dec) { const int dquv_ac = VP8Get(br, "global-header") ? VP8GetSignedValue(br, 4, "global-header") : 0; - const VP8SegmentHeader* const hdr = &dec->segment_hdr_; + 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_) { + 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]; + 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)]; + 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; + 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->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_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 + m->uv_quant = q + dquv_ac; // for dithering strength evaluation } } } //------------------------------------------------------------------------------ - diff --git a/3rdparty/libwebp/src/dec/tree_dec.c b/3rdparty/libwebp/src/dec/tree_dec.c index 2434605953..a3b00ef7b9 100644 --- a/3rdparty/libwebp/src/dec/tree_dec.c +++ b/3rdparty/libwebp/src/dec/tree_dec.c @@ -11,12 +11,19 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include + +#include "src/dec/common_dec.h" +#include "src/webp/types.h" +#include "src/dec/vp8_dec.h" #include "src/dec/vp8i_dec.h" #include "src/dsp/cpu.h" #include "src/utils/bit_reader_inl_utils.h" +#include "src/utils/bit_reader_utils.h" #if !defined(USE_GENERIC_TREE) -#if !defined(__arm__) && !defined(_M_ARM) && !WEBP_AARCH64 +#if !defined(__arm__) && !defined(_M_ARM) && !WEBP_AARCH64 && \ + !defined(__wasm__) // using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then. #define USE_GENERIC_TREE 1 // ALTERNATE_CODE #else @@ -283,40 +290,40 @@ static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = { }; void VP8ResetProba(VP8Proba* const proba) { - memset(proba->segments_, 255u, sizeof(proba->segments_)); - // proba->bands_[][] is initialized later + 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; + 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_) { + 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; + 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 + block->segment = 0; // default for intra } - if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_, "skip"); + 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_) { + 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; + block->imodes[0] = ymode; memset(top, ymode, 4 * sizeof(*top)); memset(left, ymode, 4 * sizeof(*left)); } else { - uint8_t* modes = block->imodes_; + uint8_t* modes = block->imodes; int y; for (y = 0; y < 4; ++y) { int ymode = left[y]; @@ -353,17 +360,17 @@ static void ParseIntraMode(VP8BitReader* const br, } } // 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; + 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) { + for (mb_x = 0; mb_x < dec->mb_w; ++mb_x) { ParseIntraMode(br, dec, mb_x); } - return !dec->br_.eof_; + return !dec->br.eof; } //------------------------------------------------------------------------------ @@ -513,7 +520,7 @@ static const uint8_t kBands[16 + 1] = { }; void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) { - VP8Proba* const proba = &dec->proba_; + VP8Proba* const proba = &dec->proba; int t, b, c, p; for (t = 0; t < NUM_TYPES; ++t) { for (b = 0; b < NUM_BANDS; ++b) { @@ -523,16 +530,16 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) { 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; + 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]]; + 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"); + dec->use_skip_proba = VP8Get(br, "global-header"); + if (dec->use_skip_proba) { + dec->skip_p = VP8GetValue(br, 8, "global-header"); } } diff --git a/3rdparty/libwebp/src/dec/vp8_dec.c b/3rdparty/libwebp/src/dec/vp8_dec.c index 2ee8900605..b1df3a0af0 100644 --- a/3rdparty/libwebp/src/dec/vp8_dec.c +++ b/3rdparty/libwebp/src/dec/vp8_dec.c @@ -11,14 +11,25 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include #include +#include #include "src/dec/alphai_dec.h" +#include "src/dec/common_dec.h" +#include "src/dec/vp8_dec.h" #include "src/dec/vp8i_dec.h" #include "src/dec/vp8li_dec.h" #include "src/dec/webpi_dec.h" +#include "src/dsp/cpu.h" +#include "src/dsp/dsp.h" #include "src/utils/bit_reader_inl_utils.h" +#include "src/utils/bit_reader_utils.h" +#include "src/utils/thread_utils.h" #include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ @@ -40,8 +51,8 @@ static void InitGetCoeffs(void); // VP8Decoder static void SetOk(VP8Decoder* const dec) { - dec->status_ = VP8_STATUS_OK; - dec->error_msg_ = "OK"; + dec->status = VP8_STATUS_OK; + dec->error_msg = "OK"; } int VP8InitIoInternal(VP8Io* const io, int version) { @@ -58,9 +69,9 @@ 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; + WebPGetWorkerInterface()->Init(&dec->worker); + dec->ready = 0; + dec->num_parts_minus_one = 0; InitGetCoeffs(); } return dec; @@ -68,13 +79,13 @@ VP8Decoder* VP8New(void) { VP8StatusCode VP8Status(VP8Decoder* const dec) { if (!dec) return VP8_STATUS_INVALID_PARAM; - return dec->status_; + 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_; + if (!dec->error_msg) return "OK"; + return dec->error_msg; } void VP8Delete(VP8Decoder* const dec) { @@ -87,12 +98,12 @@ void VP8Delete(VP8Decoder* const dec) { int VP8SetError(VP8Decoder* const dec, VP8StatusCode error, const char* const msg) { // VP8_STATUS_SUSPENDED is only meaningful in incremental decoding. - assert(dec->incremental_ || error != VP8_STATUS_SUSPENDED); + assert(dec->incremental || error != VP8_STATUS_SUSPENDED); // 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; + if (dec->status == VP8_STATUS_OK) { + dec->status = error; + dec->error_msg = msg; + dec->ready = 0; } return 0; } @@ -151,11 +162,11 @@ int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size, 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_)); + 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 @@ -163,32 +174,32 @@ 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"); + 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"); + hdr->absolute_delta = VP8Get(br, "global-header"); for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - hdr->quantizer_[s] = VP8Get(br, "global-header") ? + 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") ? + hdr->filter_strength[s] = VP8Get(br, "global-header") ? VP8GetSignedValue(br, 6, "global-header") : 0; } } - if (hdr->update_map_) { + if (hdr->update_map) { int s; for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) { - proba->segments_[s] = VP8Get(br, "global-header") ? + proba->segments[s] = VP8Get(br, "global-header") ? VP8GetValue(br, 8, "global-header") : 255u; } } } else { - hdr->update_map_ = 0; + hdr->update_map = 0; } - return !br->eof_; + return !br->eof; } // Paragraph 9.5 @@ -202,7 +213,7 @@ static int ParseSegmentHeader(VP8BitReader* br, // 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_; + VP8BitReader* const br = &dec->br; const uint8_t* sz = buf; const uint8_t* buf_end = buf + size; const uint8_t* part_start; @@ -210,8 +221,8 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec, 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_; + 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; @@ -221,42 +232,42 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec, 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); + VP8InitBitReader(dec->parts + p, part_start, psize); part_start += psize; size_left -= psize; sz += 3; } - VP8InitBitReader(dec->parts_ + last_part, part_start, size_left); + VP8InitBitReader(dec->parts + last_part, part_start, size_left); if (part_start < buf_end) return VP8_STATUS_OK; - return dec->incremental_ + return dec->incremental ? VP8_STATUS_SUSPENDED // Init is ok, but there's not enough data : VP8_STATUS_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_) { + 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"); + 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"); + hdr->mode_lf_delta[i] = VP8GetSignedValue(br, 6, "global-header"); } } } } - dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2; - return !br->eof_; + dec->filter_type = (hdr->level == 0) ? 0 : hdr->simple ? 1 : 2; + return !br->eof; } // Topmost call @@ -286,16 +297,16 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { // 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) { + 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_) { + if (!frm_hdr->show) { return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, "Frame not displayable."); } @@ -303,8 +314,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { buf_size -= 3; } - pic_hdr = &dec->pic_hdr_; - if (frm_hdr->key_frame_) { + 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, @@ -314,20 +325,20 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { 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; + 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; + 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. + 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; @@ -342,27 +353,27 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { io->mb_w = io->width; // for soundness io->mb_h = io->height; // ditto - VP8ResetProba(&dec->proba_); - ResetSegmentHeader(&dec->segment_hdr_); + VP8ResetProba(&dec->proba); + ResetSegmentHeader(&dec->segment_hdr); } - // Check if we have all the partition #0 available, and initialize dec->br_ + // 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) { + 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_; + 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 (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_)) { + if (!ParseSegmentHeader(br, &dec->segment_hdr, &dec->proba)) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "cannot parse segment header"); } @@ -380,17 +391,17 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { VP8ParseQuant(dec); // Frame buffer marking - if (!frm_hdr->key_frame_) { + 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_ + VP8Get(br, "global-header"); // ignore the value of 'update_proba' VP8ParseProba(br, dec); // sanitized state - dec->ready_ = 1; + dec->ready = 1; return 1; } @@ -443,17 +454,17 @@ static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) { 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]; + 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]; + p = prob[++n]->probas[0]; if (n == 16) return 16; } { // non zero coeff - const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0]; + const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas[0]; int v; if (!VP8GetBit(br, p[2], "coeffs")) { v = 1; @@ -473,17 +484,17 @@ static int GetCoeffsFast(VP8BitReader* const br, 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]; + 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]; + p = prob[++n]->probas[0]; if (n == 16) return 16; } { // non zero coeff - const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0]; + const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas[0]; int v; if (!VP8GetBitAlt(br, p[2], "coeffs")) { v = 1; @@ -516,12 +527,12 @@ static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) { 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 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; + 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; @@ -530,11 +541,11 @@ static int ParseResiduals(VP8Decoder* const dec, int first; memset(dst, 0, 384 * sizeof(*dst)); - if (!block->is_i4x4_) { // parse DC + 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); + 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 @@ -549,14 +560,14 @@ static int ParseResiduals(VP8Decoder* const dec, ac_proba = bands[3]; } - tnz = mb->nz_ & 0x0f; - lnz = left_mb->nz_ & 0x0f; + 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); + 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); @@ -571,13 +582,13 @@ static int ParseResiduals(VP8Decoder* const dec, for (ch = 0; ch < 4; ch += 2) { uint32_t nz_coeffs = 0; - tnz = mb->nz_ >> (4 + ch); - lnz = left_mb->nz_ >> (4 + ch); + 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); + 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); @@ -591,16 +602,16 @@ static int ParseResiduals(VP8Decoder* const dec, out_t_nz |= (tnz << 4) << ch; out_l_nz |= (lnz & 0xf0) << ch; } - mb->nz_ = out_t_nz; - left_mb->nz_ = out_l_nz; + 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; + 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_; + block->dither = (non_zero_uv & 0xaaaa) ? 0 : q->dither; return !(non_zero_y | non_zero_uv); // will be used for further optimization } @@ -609,50 +620,50 @@ static int ParseResiduals(VP8Decoder* const dec, // 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; + 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; + 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; + 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; + 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_; + 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; + 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_) { + 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)) { + &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_) { + 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."); @@ -665,8 +676,8 @@ static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted."); } } - if (dec->mt_method_ > 0) { - if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0; + if (dec->mt_method > 0) { + if (!WebPGetWorkerInterface()->Sync(&dec->worker)) return 0; } return 1; @@ -683,12 +694,12 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { "NULL VP8Io parameter in VP8Decode()."); } - if (!dec->ready_) { + if (!dec->ready) { if (!VP8GetHeaders(dec, io)) { return 0; } } - assert(dec->ready_); + assert(dec->ready); // Finish setting up the decoding parameter. Will call io->setup(). ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK); @@ -708,7 +719,7 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { return 0; } - dec->ready_ = 0; + dec->ready = 0; return ok; } @@ -716,13 +727,13 @@ void VP8Clear(VP8Decoder* const dec) { if (dec == NULL) { return; } - WebPGetWorkerInterface()->End(&dec->worker_); + 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; + WebPSafeFree(dec->mem); + dec->mem = NULL; + dec->mem_size = 0; + memset(&dec->br, 0, sizeof(dec->br)); + dec->ready = 0; } //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/dec/vp8_dec.h b/3rdparty/libwebp/src/dec/vp8_dec.h index 91fe104093..eb292b14b8 100644 --- a/3rdparty/libwebp/src/dec/vp8_dec.h +++ b/3rdparty/libwebp/src/dec/vp8_dec.h @@ -14,6 +14,8 @@ #ifndef WEBP_DEC_VP8_DEC_H_ #define WEBP_DEC_VP8_DEC_H_ +#include + #include "src/webp/decode.h" #include "src/webp/types.h" diff --git a/3rdparty/libwebp/src/dec/vp8i_dec.h b/3rdparty/libwebp/src/dec/vp8i_dec.h index cb21d475ae..6d4c092bcc 100644 --- a/3rdparty/libwebp/src/dec/vp8i_dec.h +++ b/3rdparty/libwebp/src/dec/vp8i_dec.h @@ -15,12 +15,16 @@ #define WEBP_DEC_VP8I_DEC_H_ #include // for memcpy() + #include "src/dec/common_dec.h" +#include "src/dec/vp8_dec.h" #include "src/dec/vp8li_dec.h" +#include "src/dec/webpi_dec.h" +#include "src/dsp/dsp.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" +#include "src/webp/decode.h" #include "src/webp/types.h" #ifdef __cplusplus @@ -32,7 +36,7 @@ extern "C" { // version numbers #define DEC_MAJ_VERSION 1 -#define DEC_MIN_VERSION 4 +#define DEC_MIN_VERSION 6 #define DEC_REV_VERSION 0 // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). @@ -69,85 +73,85 @@ extern "C" { // Headers typedef struct { - uint8_t key_frame_; - uint8_t profile_; - uint8_t show_; - uint32_t partition_length_; + 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_; + 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 + 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]; + VP8ProbaArray probas[NUM_CTX]; } VP8BandProbas; // Struct collecting all frame-persistent probabilities. typedef struct { - uint8_t segments_[MB_FEATURE_TREE_PROBS]; + 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]; + 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]; + 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] + 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) + 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_; + quant_t y1_mat, y2_mat, uv_mat; - int uv_quant_; // U/V quantizer value - int dither_; // dithering amplitude (0 = off, max=255) + 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 + 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 @@ -155,21 +159,21 @@ typedef struct { // 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_; + 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() + 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. @@ -181,89 +185,89 @@ typedef struct { // 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. + 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_; - int incremental_; // if true, incremental decoding is expected + VP8BitReader br; + int incremental; // if true, incremental decoding is expected // headers - VP8FrameHeader frm_hdr_; - VP8PictureHeader pic_hdr_; - VP8FilterHeader filter_hdr_; - VP8SegmentHeader segment_hdr_; + 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 + 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_; + 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 + 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_; + uint32_t num_parts_minus_one; // per-partition boolean decoders. - VP8BitReader parts_[MAX_NUM_PARTITIONS]; + 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 + 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]; + VP8QuantMatrix dqm[NUM_MB_SEGMENTS]; // probabilities - VP8Proba proba_; - int use_skip_proba_; - uint8_t skip_p_; + 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 + 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 + 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) + 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_; + 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_; + 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 + 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 + 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) + 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) }; //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/dec/vp8l_dec.c b/3rdparty/libwebp/src/dec/vp8l_dec.c index 11c00ea964..cf8cd82ccb 100644 --- a/3rdparty/libwebp/src/dec/vp8l_dec.c +++ b/3rdparty/libwebp/src/dec/vp8l_dec.c @@ -13,17 +13,25 @@ // Jyrki Alakuijala (jyrki@google.com) #include +#include #include +#include #include "src/dec/alphai_dec.h" +#include "src/dec/vp8_dec.h" #include "src/dec/vp8li_dec.h" +#include "src/dec/webpi_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/bit_reader_utils.h" +#include "src/utils/color_cache_utils.h" #include "src/utils/huffman_utils.h" +#include "src/utils/rescaler_utils.h" #include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" #define NUM_ARGB_CACHE_ROWS 16 @@ -104,8 +112,8 @@ static const uint16_t kTableSize[12] = { static int VP8LSetError(VP8LDecoder* const dec, VP8StatusCode error) { // The oldest error reported takes precedence over the new one. - if (dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED) { - dec->status_ = error; + if (dec->status == VP8_STATUS_OK || dec->status == VP8_STATUS_SUSPENDED) { + dec->status = error; } return 0; } @@ -131,7 +139,7 @@ static int ReadImageInfo(VP8LBitReader* const br, *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_; + return !br->eos; } int VP8LGetInfo(const uint8_t* data, size_t data_size, @@ -196,12 +204,12 @@ static WEBP_INLINE int ReadSymbol(const HuffmanCode* table, table += val & HUFFMAN_TABLE_MASK; nbits = table->bits - HUFFMAN_TABLE_BITS; if (nbits > 0) { - VP8LSetBitPos(br, br->bit_pos_ + HUFFMAN_TABLE_BITS); + 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); + VP8LSetBitPos(br, br->bit_pos + table->bits); return table->value; } @@ -215,11 +223,11 @@ static WEBP_INLINE int ReadPackedSymbols(const HTreeGroup* group, 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); + 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); + VP8LSetBitPos(br, br->bit_pos + code.bits - BITS_SPECIAL_MARKER); assert(code.value >= NUM_LITERAL_CODES); return code.value; } @@ -258,7 +266,7 @@ 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_; + VP8LBitReader* const br = &dec->br; int symbol; int max_symbol; int prev_code_len = DEFAULT_CODE_LENGTH; @@ -287,7 +295,7 @@ static int ReadHuffmanCodeLengths( if (max_symbol-- == 0) break; VP8LFillBitWindow(br); p = &tables.curr_segment->start[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK]; - VP8LSetBitPos(br, br->bit_pos_ + p->bits); + VP8LSetBitPos(br, br->bit_pos + p->bits); code_len = p->value; if (code_len < kCodeLengthLiterals) { code_lengths[symbol++] = code_len; @@ -321,7 +329,7 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, HuffmanTables* const table) { int ok = 0; int size = 0; - VP8LBitReader* const br = &dec->br_; + VP8LBitReader* const br = &dec->br; const int simple_code = VP8LReadBits(br, 1); memset(code_lengths, 0, alphabet_size * sizeof(*code_lengths)); @@ -351,7 +359,7 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, code_lengths); } - ok = ok && !br->eos_; + ok = ok && !br->eos; if (ok) { size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS, code_lengths, alphabet_size); @@ -365,11 +373,11 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, int color_cache_bits, int allow_recursion) { int i; - VP8LBitReader* const br = &dec->br_; - VP8LMetadata* const hdr = &dec->hdr_; + VP8LBitReader* const br = &dec->br; + VP8LMetadata* const hdr = &dec->hdr; uint32_t* huffman_image = NULL; HTreeGroup* htree_groups = NULL; - HuffmanTables* huffman_tables = &hdr->huffman_tables_; + HuffmanTables* huffman_tables = &hdr->huffman_tables; int num_htree_groups = 1; int num_htree_groups_max = 1; int* mapping = NULL; @@ -381,7 +389,8 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, if (allow_recursion && VP8LReadBits(br, 1)) { // use meta Huffman codes. - const int huffman_precision = VP8LReadBits(br, 3) + 2; + const int huffman_precision = + MIN_HUFFMAN_BITS + VP8LReadBits(br, NUM_HUFFMAN_BITS); const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision); const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision); const int huffman_pixs = huffman_xsize * huffman_ysize; @@ -389,7 +398,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, &huffman_image)) { goto Error; } - hdr->huffman_subsample_bits_ = huffman_precision; + 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; @@ -426,7 +435,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, } } - if (br->eos_) goto Error; + if (br->eos) goto Error; if (!ReadHuffmanCodesHelper(color_cache_bits, num_htree_groups, num_htree_groups_max, mapping, dec, @@ -436,9 +445,9 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, 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_image = huffman_image; + hdr->num_htree_groups = num_htree_groups; + hdr->htree_groups = htree_groups; Error: WebPSafeFree(mapping); @@ -620,12 +629,12 @@ static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace, 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; + 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; + uint8_t* const row_in = in + (ptrdiff_t)num_lines_in * in_stride; + uint8_t* const row_out = out + (ptrdiff_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; @@ -695,7 +704,7 @@ static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) { while (WebPRescalerHasPendingOutput(rescaler)) { WebPRescalerExportRow(rescaler); WebPMultARGBRow(src, dst_width, 1); - ConvertToYUVA(src, dst_width, y_pos, dec->output_); + ConvertToYUVA(src, dst_width, y_pos, dec->output); ++y_pos; ++num_lines_out; } @@ -705,7 +714,7 @@ static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) { 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_; + 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); @@ -724,9 +733,9 @@ static int EmitRescaledRowsYUVA(const VP8LDecoder* const dec, 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_; + int y_pos = dec->last_out_row; while (num_rows-- > 0) { - ConvertToYUVA((const uint32_t*)in, mb_w, y_pos, dec->output_); + ConvertToYUVA((const uint32_t*)in, mb_w, y_pos, dec->output); in += in_stride; ++y_pos; } @@ -773,10 +782,10 @@ static WEBP_INLINE int GetMetaIndex( 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; + 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; } //------------------------------------------------------------------------------ @@ -787,15 +796,15 @@ 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; + 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_; + uint32_t* const rows_out = dec->argb_cache; // Inverse transforms. while (n-- > 0) { - VP8LTransform* const transform = &dec->transforms_[n]; + VP8LTransform* const transform = &dec->transforms[n]; VP8LInverseTransform(transform, start_row, end_row, rows_in, rows_out); rows_in = rows_out; } @@ -808,26 +817,26 @@ static void ApplyInverseTransforms(VP8LDecoder* const dec, // 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_; + 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); + 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. + // 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_; + 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)) { + 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_; + 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; + buf->rgba + (ptrdiff_t)dec->last_out_row * buf->stride; const int num_rows_out = #if !defined(WEBP_REDUCE_SIZE) io->use_scaling ? @@ -836,31 +845,31 @@ static void ProcessRows(VP8LDecoder* const dec, int row) { #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; + // Update 'last_out_row'. + dec->last_out_row += num_rows_out; } else { // convert to YUVA - dec->last_out_row_ = io->use_scaling ? + 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); + assert(dec->last_out_row <= output->height); } } - // Update 'last_row_'. - dec->last_row_ = row; - assert(dec->last_row_ <= dec->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; + 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; + 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; @@ -871,43 +880,43 @@ static int Is8bOptimizable(const VP8LMetadata* const hdr) { 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) { + if (alph_dec->filter != WEBP_FILTER_NONE) { int y; - const uint8_t* prev_line = alph_dec->prev_line_; - assert(WebPUnfilters[alph_dec->filter_] != NULL); + 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); + WebPUnfilters[alph_dec->filter](prev_line, out, out, stride); prev_line = out; out += stride; } - alph_dec->prev_line_ = prev_line; + 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; + 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); + (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 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); + (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; + dec->last_row = dec->last_out_row = last_row; } //------------------------------------------------------------------------------ @@ -1035,22 +1044,22 @@ static WEBP_INLINE void CopyBlock32b(uint32_t* const dst, 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 + 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 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) { + while (!br->eos && pos < last) { int code; // Only update when changing tile. if ((col & mask) == 0) { @@ -1100,37 +1109,37 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, ok = 0; goto End; } - br->eos_ = VP8LIsEndOfStream(br); + 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)) { + br->eos = VP8LIsEndOfStream(br); + if (!ok || (br->eos && pos < end)) { return VP8LSetError( - dec, br->eos_ ? VP8_STATUS_SUSPENDED : VP8_STATUS_BITSTREAM_ERROR); + dec, br->eos ? VP8_STATUS_SUSPENDED : VP8_STATUS_BITSTREAM_ERROR); } - dec->last_pixel_ = pos; + 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_); + 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_); + 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); } } @@ -1138,23 +1147,23 @@ static void RestoreState(VP8LDecoder* const dec) { 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_; + 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; + 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_; + (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(dec->last_row < last_row); assert(src_last <= src_end); while (src < src_last) { @@ -1260,29 +1269,29 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, } } - br->eos_ = VP8LIsEndOfStream(br); + br->eos = VP8LIsEndOfStream(br); // In incremental decoding: - // br->eos_ && src < src_last: if 'br' reached the end of the buffer and + // br->eos && src < src_last: if 'br' reached the end of the buffer and // 'src_last' has not been reached yet, there is not enough data. 'dec' has to // be reset until there is more data. - // !br->eos_ && src < src_last: this cannot happen as either the buffer is + // !br->eos && src < src_last: this cannot happen as either the buffer is // fully read, either enough has been read to reach 'src_last'. // src >= src_last: 'src_last' is reached, all is fine. 'src' can actually go // beyond 'src_last' in case the image is cropped and an LZ77 goes further. - // The buffer might have been enough or there is some left. 'br->eos_' does + // The buffer might have been enough or there is some left. 'br->eos' does // not matter. - assert(!dec->incremental_ || (br->eos_ && src < src_last) || src >= src_last); - if (dec->incremental_ && br->eos_ && src < src_last) { + assert(!dec->incremental || (br->eos && src < src_last) || src >= src_last); + if (dec->incremental && br->eos && src < src_last) { RestoreState(dec); - } else if ((dec->incremental_ && src >= src_last) || !br->eos_) { + } else if ((dec->incremental && src >= src_last) || !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 + 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 + // if not incremental, and we are past the end of buffer (eos=1), then this // is a real bitstream error. goto Error; } @@ -1296,24 +1305,24 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, // VP8LTransform static void ClearTransform(VP8LTransform* const transform) { - WebPSafeFree(transform->data_); - transform->data_ = NULL; + 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_); + 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 data = (uint8_t*)transform->data; uint8_t* const new_data = (uint8_t*)new_color_map; - new_color_map[0] = transform->data_[0]; + new_color_map[0] = transform->data[0]; for (i = 4; i < 4 * num_colors; ++i) { // Equivalent to VP8LAddPixels(), on a byte-basis. new_data[i] = (data[i] + new_data[i - 4]) & 0xff; @@ -1321,8 +1330,8 @@ static int ExpandColorMap(int num_colors, VP8LTransform* const transform) { for (; i < 4 * final_num_colors; ++i) { new_data[i] = 0; // black tail. } - WebPSafeFree(transform->data_); - transform->data_ = new_color_map; + WebPSafeFree(transform->data); + transform->data = new_color_map; } return 1; } @@ -1330,33 +1339,34 @@ static int ExpandColorMap(int num_colors, VP8LTransform* const transform) { 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_]; + 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)) { + if (dec->transforms_seen & (1U << type)) { return 0; // Already there, let's not accept the second same transform. } - dec->transforms_seen_ |= (1U << type); + 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); + 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_), - /*is_level0=*/0, dec, &transform->data_); + transform->bits = + MIN_TRANSFORM_BITS + VP8LReadBits(br, NUM_TRANSFORM_BITS); + ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize, + transform->bits), + VP8LSubSampleSize(transform->ysize, + transform->bits), + /*is_level0=*/0, dec, &transform->data); break; case COLOR_INDEXING_TRANSFORM: { const int num_colors = VP8LReadBits(br, 8) + 1; @@ -1364,10 +1374,10 @@ static int ReadTransform(int* const xsize, int const* ysize, : (num_colors > 4) ? 1 : (num_colors > 2) ? 2 : 3; - *xsize = VP8LSubSampleSize(transform->xsize_, bits); - transform->bits_ = bits; + *xsize = VP8LSubSampleSize(transform->xsize, bits); + transform->bits = bits; ok = DecodeImageStream(num_colors, /*ysize=*/1, /*is_level0=*/0, dec, - &transform->data_); + &transform->data); if (ok && !ExpandColorMap(num_colors, transform)) { return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); } @@ -1394,11 +1404,11 @@ static void InitMetadata(VP8LMetadata* const hdr) { static void ClearMetadata(VP8LMetadata* const hdr) { assert(hdr != NULL); - WebPSafeFree(hdr->huffman_image_); - VP8LHuffmanTablesDeallocate(&hdr->huffman_tables_); - VP8LHtreeGroupsFree(hdr->htree_groups_); - VP8LColorCacheClear(&hdr->color_cache_); - VP8LColorCacheClear(&hdr->saved_color_cache_); + WebPSafeFree(hdr->huffman_image); + VP8LHuffmanTablesDeallocate(&hdr->huffman_tables); + VP8LHtreeGroupsFree(hdr->htree_groups); + VP8LColorCacheClear(&hdr->color_cache); + VP8LColorCacheClear(&hdr->saved_color_cache); InitMetadata(hdr); } @@ -1408,31 +1418,33 @@ static void ClearMetadata(VP8LMetadata* const hdr) { 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; + dec->status = VP8_STATUS_OK; + dec->state = READ_DIM; VP8LDspInit(); // Init critical function pointers. return dec; } -void VP8LClear(VP8LDecoder* const dec) { +// Resets the decoder in its initial state, reclaiming memory. +// Preserves the dec->status value. +static void VP8LClear(VP8LDecoder* const dec) { int i; if (dec == NULL) return; - ClearMetadata(&dec->hdr_); + ClearMetadata(&dec->hdr); - WebPSafeFree(dec->pixels_); - dec->pixels_ = NULL; - for (i = 0; i < dec->next_transform_; ++i) { - ClearTransform(&dec->transforms_[i]); + 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; + dec->next_transform = 0; + dec->transforms_seen = 0; WebPSafeFree(dec->rescaler_memory); dec->rescaler_memory = NULL; - dec->output_ = NULL; // leave no trace behind + dec->output = NULL; // leave no trace behind } void VP8LDelete(VP8LDecoder* const dec) { @@ -1443,13 +1455,13 @@ void VP8LDelete(VP8LDecoder* const 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; + 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; + 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, @@ -1459,8 +1471,8 @@ static int DecodeImageStream(int xsize, int ysize, int ok = 1; int transform_xsize = xsize; int transform_ysize = ysize; - VP8LBitReader* const br = &dec->br_; - VP8LMetadata* const hdr = &dec->hdr_; + VP8LBitReader* const br = &dec->br; + VP8LMetadata* const hdr = &dec->hdr; uint32_t* data = NULL; int color_cache_bits = 0; @@ -1491,18 +1503,18 @@ static int DecodeImageStream(int xsize, int ysize, // 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)) { + hdr->color_cache_size = 1 << color_cache_bits; + if (!VP8LColorCacheInit(&hdr->color_cache, color_cache_bits)) { ok = VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); goto End; } } else { - hdr->color_cache_size_ = 0; + hdr->color_cache_size = 0; } UpdateDecoder(dec, transform_xsize, transform_ysize); if (is_level0) { // level 0 complete - dec->state_ = READ_HDR; + dec->state = READ_HDR; goto End; } @@ -1518,7 +1530,7 @@ static int DecodeImageStream(int xsize, int ysize, // 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_; + ok = ok && !br->eos; End: if (!ok) { @@ -1533,16 +1545,16 @@ static int DecodeImageStream(int xsize, int ysize, assert(data == NULL); assert(is_level0); } - dec->last_pixel_ = 0; // Reset for future DECODE_DATA_FUNC() calls. + 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_. +// 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_; + 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; @@ -1551,21 +1563,21 @@ static int AllocateInternalBuffers32b(VP8LDecoder* const dec, int final_width) { 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 + 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 return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); } - dec->argb_cache_ = dec->pixels_ + num_pixels + cache_top_pixels; + 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) { + 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) { return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); } return 1; @@ -1575,31 +1587,31 @@ static int AllocateInternalBuffers8b(VP8LDecoder* const dec) { // 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 cur_row = dec->last_row; int num_rows = last_row - cur_row; - const uint32_t* in = dec->pixels_ + dec->width_ * cur_row; + const uint32_t* in = dec->pixels + dec->width * cur_row; - assert(last_row <= dec->io_->crop_bottom); + 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_) + 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_; + 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_; + 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; + dec->last_row = dec->last_out_row = last_row; } int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, @@ -1611,17 +1623,17 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, 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->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); + dec->status = VP8_STATUS_OK; + VP8LInitBitReader(&dec->br, data, data_size); - if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, /*is_level0=*/1, + if (!DecodeImageStream(alph_dec->width, alph_dec->height, /*is_level0=*/1, dec, /*decoded_data=*/NULL)) { goto Err; } @@ -1629,21 +1641,21 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, // 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; + 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_); + // 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; + alph_dec->vp8l_dec = dec; return 1; Err: @@ -1652,21 +1664,21 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, } int VP8LDecodeAlphaImageStream(ALPHDecoder* const alph_dec, int last_row) { - VP8LDecoder* const dec = alph_dec->vp8l_dec_; + VP8LDecoder* const dec = alph_dec->vp8l_dec; assert(dec != NULL); - assert(last_row <= dec->height_); + assert(last_row <= dec->height); - if (dec->last_row_ >= last_row) { + if (dec->last_row >= last_row) { return 1; // done } - if (!alph_dec->use_8b_decode_) WebPInitAlphaProcessing(); + 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_, + 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_, + DecodeImageData(dec, dec->pixels, dec->width, dec->height, last_row, ExtractAlphaRows); } @@ -1680,14 +1692,14 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { return VP8LSetError(dec, VP8_STATUS_INVALID_PARAM); } - 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->io = io; + dec->status = VP8_STATUS_OK; + VP8LInitBitReader(&dec->br, io->data, io->data_size); + if (!ReadImageInfo(&dec->br, &width, &height, &has_alpha)) { VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); goto Error; } - dec->state_ = READ_DIM; + dec->state = READ_DIM; io->width = width; io->height = height; @@ -1699,7 +1711,7 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { Error: VP8LClear(dec); - assert(dec->status_ != VP8_STATUS_OK); + assert(dec->status != VP8_STATUS_OK); return 0; } @@ -1709,19 +1721,19 @@ int VP8LDecodeImage(VP8LDecoder* const dec) { if (dec == NULL) return 0; - assert(dec->hdr_.huffman_tables_.root.start != NULL); - assert(dec->hdr_.htree_groups_ != NULL); - assert(dec->hdr_.num_htree_groups_ > 0); + assert(dec->hdr.huffman_tables.root.start != NULL); + assert(dec->hdr.htree_groups != NULL); + assert(dec->hdr.num_htree_groups > 0); - io = dec->io_; + 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 (dec->state != READ_DATA) { + dec->output = params->output; + assert(dec->output != NULL); if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) { VP8LSetError(dec, VP8_STATUS_INVALID_PARAM); @@ -1738,40 +1750,40 @@ int VP8LDecodeImage(VP8LDecoder* const dec) { goto Err; } #endif - if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) { + if (io->use_scaling || WebPIsPremultipliedMode(dec->output->colorspace)) { // need the alpha-multiply functions for premultiplied output or rescaling WebPInitAlphaProcessing(); } - if (!WebPIsRGBMode(dec->output_->colorspace)) { + if (!WebPIsRGBMode(dec->output->colorspace)) { WebPInitConvertARGBToYUV(); - if (dec->output_->u.YUVA.a != NULL) WebPInitAlphaProcessing(); + 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_)) { + 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)) { VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); goto Err; } } } - dec->state_ = READ_DATA; + dec->state = READ_DATA; } // Decode. - if (!DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, + if (!DecodeImageData(dec, dec->pixels, dec->width, dec->height, io->crop_bottom, ProcessRows)) { goto Err; } - params->last_y = dec->last_out_row_; + params->last_y = dec->last_out_row; return 1; Err: VP8LClear(dec); - assert(dec->status_ != VP8_STATUS_OK); + assert(dec->status != VP8_STATUS_OK); return 0; } diff --git a/3rdparty/libwebp/src/dec/vp8li_dec.h b/3rdparty/libwebp/src/dec/vp8li_dec.h index 9a13bcc98d..4e2eadb3e4 100644 --- a/3rdparty/libwebp/src/dec/vp8li_dec.h +++ b/3rdparty/libwebp/src/dec/vp8li_dec.h @@ -16,10 +16,15 @@ #define WEBP_DEC_VP8LI_DEC_H_ #include // for memcpy() + +#include "src/dec/vp8_dec.h" #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" +#include "src/utils/rescaler_utils.h" +#include "src/webp/decode.h" +#include "src/webp/format_constants.h" #include "src/webp/types.h" #ifdef __cplusplus @@ -34,58 +39,58 @@ typedef enum { 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. + 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 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_; - HuffmanTables huffman_tables_; + int huffman_mask; + int huffman_subsample_bits; + int huffman_xsize; + uint32_t* huffman_image; + int num_htree_groups; + HTreeGroup* htree_groups; + HuffmanTables huffman_tables; } VP8LMetadata; typedef struct VP8LDecoder VP8LDecoder; struct VP8LDecoder { - VP8StatusCode status_; - VP8LDecodeState state_; - VP8Io* io_; + VP8StatusCode status; + VP8LDecodeState state; + VP8Io* io; - const WebPDecBuffer* output_; // shortcut to io->opaque->output + 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. + 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_; + 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. + 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_; + VP8LMetadata hdr; - int next_transform_; - VP8LTransform transforms_[NUM_TRANSFORMS]; + int next_transform; + VP8LTransform transforms[NUM_TRANSFORMS]; // or'd bitset storing the transforms types. - uint32_t transforms_seen_; + uint32_t transforms_seen; uint8_t* rescaler_memory; // Working memory for rescaling work. WebPRescaler* rescaler; // Common rescaler for all channels. @@ -118,13 +123,9 @@ WEBP_NODISCARD VP8LDecoder* VP8LNew(void); WEBP_NODISCARD 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_. +// this function. Returns false in case of error, with updated dec->status. WEBP_NODISCARD 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); diff --git a/3rdparty/libwebp/src/dec/webp_dec.c b/3rdparty/libwebp/src/dec/webp_dec.c index 49ef205c8b..5e7c23feb2 100644 --- a/3rdparty/libwebp/src/dec/webp_dec.c +++ b/3rdparty/libwebp/src/dec/webp_dec.c @@ -11,15 +11,20 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include #include +#include +#include "src/dec/common_dec.h" #include "src/dec/vp8_dec.h" #include "src/dec/vp8i_dec.h" #include "src/dec/vp8li_dec.h" #include "src/dec/webpi_dec.h" +#include "src/utils/rescaler_utils.h" #include "src/utils/utils.h" -#include "src/webp/mux_types.h" // ALPHA_FLAG #include "src/webp/decode.h" +#include "src/webp/format_constants.h" +#include "src/webp/mux_types.h" // ALPHA_FLAG #include "src/webp/types.h" //------------------------------------------------------------------------------ @@ -475,23 +480,23 @@ WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data, if (dec == NULL) { return VP8_STATUS_OUT_OF_MEMORY; } - dec->alpha_data_ = headers.alpha_data; - dec->alpha_data_size_ = headers.alpha_data_size; + 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. + 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); + dec->mt_method = VP8GetThreadMethod(params->options, &headers, + io.width, io.height); VP8InitDithering(params->options, dec); if (!VP8Decode(dec, &io)) { - status = dec->status_; + status = dec->status; } } } @@ -502,14 +507,14 @@ WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data, return VP8_STATUS_OUT_OF_MEMORY; } if (!VP8LDecodeHeader(dec, &io)) { - status = dec->status_; // An error occurred. Grab error status. + 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_; + status = dec->status; } } } @@ -747,6 +752,61 @@ int WebPInitDecoderConfigInternal(WebPDecoderConfig* config, return 1; } +static int WebPCheckCropDimensionsBasic(int x, int y, int w, int h) { + return !(x < 0 || y < 0 || w <= 0 || h <= 0); +} + +int WebPValidateDecoderConfig(const WebPDecoderConfig* config) { + const WebPDecoderOptions* options; + if (config == NULL) return 0; + if (!IsValidColorspace(config->output.colorspace)) { + return 0; + } + + options = &config->options; + // bypass_filtering, no_fancy_upsampling, use_cropping, use_scaling, + // use_threads, flip can be any integer and are interpreted as boolean. + + // Check for cropping. + if (options->use_cropping && !WebPCheckCropDimensionsBasic( + options->crop_left, options->crop_top, + options->crop_width, options->crop_height)) { + return 0; + } + // Check for scaling. + if (options->use_scaling && + (options->scaled_width < 0 || options->scaled_height < 0 || + (options->scaled_width == 0 && options->scaled_height == 0))) { + return 0; + } + + // In case the WebPBitstreamFeatures has been filled in, check further. + if (config->input.width > 0 || config->input.height > 0) { + int scaled_width = options->scaled_width; + int scaled_height = options->scaled_height; + if (options->use_cropping && + !WebPCheckCropDimensions(config->input.width, config->input.height, + options->crop_left, options->crop_top, + options->crop_width, options->crop_height)) { + return 0; + } + if (options->use_scaling && !WebPRescalerGetScaledDimensions( + config->input.width, config->input.height, + &scaled_width, &scaled_height)) { + return 0; + } + } + + // Check for dithering. + if (options->dithering_strength < 0 || options->dithering_strength > 100 || + options->alpha_dithering_strength < 0 || + options->alpha_dithering_strength > 100) { + return 0; + } + + return 1; +} + VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size, WebPBitstreamFeatures* features, int version) { @@ -806,8 +866,8 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, 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 || + return WebPCheckCropDimensionsBasic(x, y, w, h) && + !(x >= image_width || w > image_width || w > image_width - x || y >= image_height || h > image_height || h > image_height - y); } diff --git a/3rdparty/libwebp/src/dec/webpi_dec.h b/3rdparty/libwebp/src/dec/webpi_dec.h index 77bf5264b7..1929796483 100644 --- a/3rdparty/libwebp/src/dec/webpi_dec.h +++ b/3rdparty/libwebp/src/dec/webpi_dec.h @@ -18,9 +18,12 @@ extern "C" { #endif -#include "src/utils/rescaler_utils.h" +#include + #include "src/dec/vp8_dec.h" +#include "src/utils/rescaler_utils.h" #include "src/webp/decode.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // WebPDecParams: Decoding output parameters. Transient internal object. diff --git a/3rdparty/libwebp/src/demux/anim_decode.c b/3rdparty/libwebp/src/demux/anim_decode.c index 27f0e2b0bb..f046f506ec 100644 --- a/3rdparty/libwebp/src/demux/anim_decode.c +++ b/3rdparty/libwebp/src/demux/anim_decode.c @@ -20,6 +20,8 @@ #include "src/utils/utils.h" #include "src/webp/decode.h" #include "src/webp/demux.h" +#include "src/webp/mux.h" +#include "src/webp/mux_types.h" #include "src/webp/types.h" #define NUM_CHANNELS 4 @@ -39,18 +41,18 @@ 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. + 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 + 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). }; @@ -73,7 +75,7 @@ WEBP_NODISCARD static int ApplyDecoderOptions( const WebPAnimDecoderOptions* const dec_options, WebPAnimDecoder* const dec) { WEBP_CSP_MODE mode; - WebPDecoderConfig* config = &dec->config_; + WebPDecoderConfig* config = &dec->config; assert(dec_options != NULL); mode = dec_options->color_mode; @@ -81,9 +83,9 @@ WEBP_NODISCARD static int ApplyDecoderOptions( mode != MODE_rgbA && mode != MODE_bgrA) { return 0; } - dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA) - ? &BlendPixelRowNonPremult - : &BlendPixelRowPremult; + dec->blend_func = (mode == MODE_RGBA || mode == MODE_BGRA) + ? &BlendPixelRowNonPremult + : &BlendPixelRowPremult; if (!WebPInitDecoderConfig(config)) { return 0; } @@ -123,22 +125,22 @@ WebPAnimDecoder* WebPAnimDecoderNewInternal( } if (!ApplyDecoderOptions(&options, dec)) goto Error; - dec->demux_ = WebPDemux(webp_data); - if (dec->demux_ == NULL) 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); + 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; + 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; @@ -150,7 +152,7 @@ WebPAnimDecoder* WebPAnimDecoderNewInternal( int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, WebPAnimInfo* info) { if (dec == NULL || info == NULL) return 0; - *info = dec->info_; + *info = dec->info; return 1; } @@ -338,25 +340,25 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, 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_; + 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)) { + if (!WebPDemuxGetFrame(dec->demux, dec->next_frame, &iter)) { return 0; } - timestamp = dec->prev_frame_timestamp_ + iter.duration; + timestamp = dec->prev_frame_timestamp + iter.duration; // Initialize. - is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_, - dec->prev_frame_was_keyframe_, width, height); + 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)) { + if (!ZeroFillCanvas(dec->curr_frame, width, height)) { goto Error; } } else { - if (!CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_, + if (!CopyCanvas(dec->prev_frame_disposed, dec->curr_frame, width, height)) { goto Error; } @@ -370,12 +372,12 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, 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_; + 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; + buf->rgba = dec->curr_frame + out_offset; if (WebPDecode(in, in_size, config) != VP8_STATUS_OK) { goto Error; @@ -388,18 +390,18 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, // 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) { + 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); + 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); + 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. @@ -407,39 +409,39 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, 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, + 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); + 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); + 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; - if (!CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height)) { + dec->prev_frame_timestamp = timestamp; + WebPDemuxReleaseIterator(&dec->prev_iter); + dec->prev_iter = iter; + dec->prev_frame_was_keyframe = is_key_frame; + if (!CopyCanvas(dec->curr_frame, dec->prev_frame_disposed, width, height)) { goto Error; } - 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); + 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_; + ++dec->next_frame; // All OK, fill in the values. - *buf_ptr = dec->curr_frame_; + *buf_ptr = dec->curr_frame; *timestamp_ptr = timestamp; return 1; @@ -450,30 +452,30 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) { if (dec == NULL) return 0; - return (dec->next_frame_ <= (int)dec->info_.frame_count); + 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; + 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_; + 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_); + WebPDemuxReleaseIterator(&dec->prev_iter); + WebPDemuxDelete(dec->demux); + WebPSafeFree(dec->curr_frame); + WebPSafeFree(dec->prev_frame_disposed); WebPSafeFree(dec); } } diff --git a/3rdparty/libwebp/src/demux/demux.c b/3rdparty/libwebp/src/demux/demux.c index d01c6a7464..7d4e23e2d1 100644 --- a/3rdparty/libwebp/src/demux/demux.c +++ b/3rdparty/libwebp/src/demux/demux.c @@ -22,55 +22,58 @@ #include "src/webp/decode.h" // WebPGetFeatures #include "src/webp/demux.h" #include "src/webp/format_constants.h" +#include "src/webp/mux.h" +#include "src/webp/mux_types.h" +#include "src/webp/types.h" #define DMUX_MAJ_VERSION 1 -#define DMUX_MIN_VERSION 4 +#define DMUX_MIN_VERSION 6 #define DMUX_REV_VERSION 0 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_; + 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_; + 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_; + 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_; + 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_; + 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 { @@ -108,10 +111,10 @@ int WebPGetDemuxVersion(void) { 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! + if (size < mem->buf_size) return 0; // can't remap to a shorter buffer! - mem->buf_ = data; - mem->end_ = mem->buf_size_ = size; + mem->buf = data; + mem->end = mem->buf_size = size; return 1; } @@ -123,49 +126,49 @@ static int InitMemBuffer(MemBuffer* const mem, // Return the remaining data size available in 'mem'. static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) { - return (mem->end_ - mem->start_); + 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_); + return (size > mem->riff_end - mem->start); } static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) { - mem->start_ += size; + mem->start += size; } static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) { - mem->start_ -= size; + mem->start -= size; } static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) { - return mem->buf_ + mem->start_; + 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_]; + 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 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 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 uint8_t* const data = mem->buf + mem->start; const uint32_t val = GetLE32(data); Skip(mem, 4); return val; @@ -175,20 +178,20 @@ static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) { // Secondary chunk parsing static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) { - *dmux->chunks_tail_ = chunk; - chunk->next_ = NULL; - dmux->chunks_tail_ = &chunk->next_; + *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; + 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_; + *dmux->frames_tail = frame; + frame->next = NULL; + dmux->frames_tail = &frame->next; return 1; } @@ -196,13 +199,13 @@ 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; + 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 @@ -218,7 +221,7 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size, if (done) return PARSE_NEED_MORE_DATA; do { - const size_t chunk_start_offset = mem->start_; + 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; @@ -238,10 +241,10 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size, 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; + 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; @@ -256,7 +259,7 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size, // is incomplete. WebPBitstreamFeatures features; const VP8StatusCode vp8_status = - WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size, + WebPGetFeatures(mem->buf + chunk_start_offset, chunk_size, &features); if (status == PARSE_NEED_MORE_DATA && vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) { @@ -281,7 +284,7 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size, break; } - if (mem->start_ == mem->riff_end_) { + if (mem->start == mem->riff_end) { done = 1; } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { status = PARSE_NEED_MORE_DATA; @@ -310,42 +313,42 @@ static ParseStatus NewFrame(const MemBuffer* const mem, // '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 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_; + 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); + 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_ = + 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) { + 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) { + 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) { + if (status != PARSE_ERROR && is_animation && frame->frame_num > 0) { added_frame = AddFrame(dmux, frame); if (added_frame) { - ++dmux->num_frames_; + ++dmux->num_frames; } else { status = PARSE_ERROR; } @@ -364,8 +367,8 @@ static int StoreChunk(WebPDemuxer* const dmux, Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk)); if (chunk == NULL) return 0; - chunk->data_.offset_ = start_offset; - chunk->data_.size_ = size; + chunk->data.offset = start_offset; + chunk->data.size = size; AddChunk(dmux, chunk); return 1; } @@ -389,9 +392,9 @@ static ParseStatus ReadHeader(MemBuffer* const mem) { 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_; + 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); @@ -400,12 +403,12 @@ static ParseStatus ReadHeader(MemBuffer* const mem) { static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { const size_t min_size = CHUNK_HEADER_SIZE; - MemBuffer* const mem = &dmux->mem_; + MemBuffer* const mem = &dmux->mem; Frame* frame; ParseStatus status; int image_added = 0; - if (dmux->frames_ != NULL) return PARSE_ERROR; + 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; @@ -414,29 +417,29 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { // 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); + status = StoreFrame(1, 0, &dmux->mem, frame); if (status != PARSE_ERROR) { - const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG); + 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; + 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 (!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; + dmux->num_frames = 1; } } @@ -445,14 +448,14 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { } static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { - const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); - MemBuffer* const mem = &dmux->mem_; + 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 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; @@ -483,8 +486,8 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { status = PARSE_NEED_MORE_DATA; } else if (anim_chunks == 0) { ++anim_chunks; - dmux->bgcolor_ = ReadLE32(mem); - dmux->loop_count_ = ReadLE16s(mem); + dmux->bgcolor = ReadLE32(mem); + dmux->loop_count = ReadLE16s(mem); Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE); } else { store_chunk = 0; @@ -498,15 +501,15 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { break; } case MKFOURCC('I', 'C', 'C', 'P'): { - store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG); + store_chunk = !!(dmux->feature_flags & ICCP_FLAG); goto Skip; } case MKFOURCC('E', 'X', 'I', 'F'): { - store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG); + store_chunk = !!(dmux->feature_flags & EXIF_FLAG); goto Skip; } case MKFOURCC('X', 'M', 'P', ' '): { - store_chunk = !!(dmux->feature_flags_ & XMP_FLAG); + store_chunk = !!(dmux->feature_flags & XMP_FLAG); goto Skip; } Skip: @@ -527,7 +530,7 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { } } - if (mem->start_ == mem->riff_end_) { + if (mem->start == mem->riff_end) { break; } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { status = PARSE_NEED_MORE_DATA; @@ -538,12 +541,12 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) { } static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { - MemBuffer* const mem = &dmux->mem_; + 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; + dmux->is_ext_format = 1; Skip(mem, TAG_SIZE); // VP8X vp8x_size = ReadLE32(mem); if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; @@ -552,15 +555,15 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR; if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA; - dmux->feature_flags_ = ReadByte(mem); + 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) { + 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; + 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; @@ -572,13 +575,13 @@ static ParseStatus ParseVP8X(WebPDemuxer* const 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; + 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 (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; + if (frame->width <= 0 || frame->height <= 0) return 0; return 1; } @@ -587,65 +590,65 @@ static int IsValidSimpleFormat(const WebPDemuxer* const dmux) { 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) { + if (frame->x_offset != 0 || frame->y_offset != 0) { return 0; } - if (frame->width_ != canvas_width || frame->height_ != canvas_height) { + 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; + 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_; + 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->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 + 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_; + const int cur_frame_set = f->frame_num; // 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; + 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 (!is_animation && f->frame_num > 1) return 0; - if (f->complete_) { - if (alpha->size_ == 0 && image->size_ == 0) 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_) { + if (alpha->size > 0 && alpha->offset > image->offset) { return 0; } - if (f->width_ <= 0 || f->height_ <= 0) 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; + if (dmux->state == WEBP_DEMUX_DONE) return 0; // Ensure alpha precedes image bitstream. - if (alpha->size_ > 0 && image->size_ > 0 && - alpha->offset_ > image->offset_) { + 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->next != NULL) return 0; } - if (f->width_ > 0 && f->height_ > 0 && + if (f->width > 0 && f->height > 0 && !CheckFrameBounds(f, !is_animation, - dmux->canvas_width_, dmux->canvas_height_)) { + dmux->canvas_width, dmux->canvas_height)) { return 0; } } @@ -657,21 +660,21 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { // 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; + 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); + 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 @@ -683,14 +686,14 @@ static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem, 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, + 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; + 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; @@ -734,7 +737,7 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, return NULL; } - partial = (mem.buf_size_ < mem.riff_end_); + partial = (mem.buf_size < mem.riff_end); if (!allow_partial && partial) return NULL; dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux)); @@ -743,16 +746,16 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, status = PARSE_ERROR; for (parser = kMasterChunks; parser->parse != NULL; ++parser) { - if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) { + 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_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; + if (status == PARSE_ERROR) dmux->state = WEBP_DEMUX_PARSE_ERROR; break; } } - if (state != NULL) *state = dmux->state_; + if (state != NULL) *state = dmux->state; if (status == PARSE_ERROR) { WebPDemuxDelete(dmux); @@ -766,14 +769,14 @@ void WebPDemuxDelete(WebPDemuxer* dmux) { Frame* f; if (dmux == NULL) return; - for (f = dmux->frames_; f != NULL;) { + for (f = dmux->frames; f != NULL;) { Frame* const cur_frame = f; - f = f->next_; + f = f->next; WebPSafeFree(cur_frame); } - for (c = dmux->chunks_; c != NULL;) { + for (c = dmux->chunks; c != NULL;) { Chunk* const cur_chunk = c; - c = c->next_; + c = c->next; WebPSafeFree(cur_chunk); } WebPSafeFree(dmux); @@ -785,12 +788,12 @@ 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_; + 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; } @@ -800,8 +803,8 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) { 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; + for (f = dmux->frames; f != NULL; f = f->next) { + if (frame_num == f->frame_num) break; } return f; } @@ -811,19 +814,19 @@ static const uint8_t* GetFramePayload(const uint8_t* const mem_buf, 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_; + 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_) + 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; + start_offset = alpha->offset; + *data_size += alpha->size + inter_size; } return mem_buf + start_offset; } @@ -834,23 +837,23 @@ static const uint8_t* GetFramePayload(const uint8_t* const mem_buf, static int SynthesizeFrame(const WebPDemuxer* const dmux, const Frame* const frame, WebPIterator* const iter) { - const uint8_t* const mem_buf = dmux->mem_.buf_; + 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->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; @@ -860,8 +863,8 @@ 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_; + 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; @@ -896,11 +899,11 @@ void WebPDemuxReleaseIterator(WebPIterator* iter) { // Chunk iteration static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) { - const uint8_t* const mem_buf = dmux->mem_.buf_; + 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_; + 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; @@ -908,11 +911,11 @@ static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) { 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 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_; + 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; } @@ -930,10 +933,10 @@ static int SetChunk(const char fourcc[4], int chunk_num, if (chunk_num == 0) chunk_num = count; if (chunk_num <= count) { - const uint8_t* const mem_buf = dmux->mem_.buf_; + 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->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; @@ -972,4 +975,3 @@ int WebPDemuxPrevChunk(WebPChunkIterator* iter) { void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) { (void)iter; } - diff --git a/3rdparty/libwebp/src/dsp/alpha_processing.c b/3rdparty/libwebp/src/dsp/alpha_processing.c index 1d152f24da..4927e73e81 100644 --- a/3rdparty/libwebp/src/dsp/alpha_processing.c +++ b/3rdparty/libwebp/src/dsp/alpha_processing.c @@ -12,7 +12,11 @@ // Author: Skal (pascal.massimino@gmail.com) #include +#include + +#include "src/dsp/cpu.h" #include "src/dsp/dsp.h" +#include "src/webp/types.h" // Tables can be faster on some platform but incur some extra binary size (~2k). #if !defined(USE_TABLES_FOR_ALPHA_MULT) diff --git a/3rdparty/libwebp/src/dsp/alpha_processing_sse2.c b/3rdparty/libwebp/src/dsp/alpha_processing_sse2.c index aa0cc2848a..1a6bfcb917 100644 --- a/3rdparty/libwebp/src/dsp/alpha_processing_sse2.c +++ b/3rdparty/libwebp/src/dsp/alpha_processing_sse2.c @@ -16,6 +16,9 @@ #if defined(WEBP_USE_SSE2) #include +#include "src/webp/types.h" +#include "src/dsp/cpu.h" + //------------------------------------------------------------------------------ static int DispatchAlpha_SSE2(const uint8_t* WEBP_RESTRICT alpha, @@ -26,38 +29,44 @@ static int DispatchAlpha_SSE2(const uint8_t* WEBP_RESTRICT alpha, uint32_t alpha_and = 0xff; int i, j; const __m128i zero = _mm_setzero_si128(); - const __m128i rgb_mask = _mm_set1_epi32((int)0xffffff00); // to preserve RGB - const __m128i all_0xff = _mm_set_epi32(0, 0, ~0, ~0); - __m128i all_alphas = all_0xff; + const __m128i alpha_mask = _mm_set1_epi32((int)0xff); // to preserve A + const __m128i all_0xff = _mm_set1_epi8((char)0xff); + __m128i all_alphas16 = all_0xff; + __m128i all_alphas8 = 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) { + char* ptr = (char*)dst; + for (i = 0; i + 16 <= width - 1; i += 16) { + // load 16 alpha bytes + const __m128i a0 = _mm_loadu_si128((const __m128i*)&alpha[i]); + const __m128i a1_lo = _mm_unpacklo_epi8(a0, zero); + const __m128i a1_hi = _mm_unpackhi_epi8(a0, zero); + const __m128i a2_lo_lo = _mm_unpacklo_epi16(a1_lo, zero); + const __m128i a2_lo_hi = _mm_unpackhi_epi16(a1_lo, zero); + const __m128i a2_hi_lo = _mm_unpacklo_epi16(a1_hi, zero); + const __m128i a2_hi_hi = _mm_unpackhi_epi16(a1_hi, zero); + _mm_maskmoveu_si128(a2_lo_lo, alpha_mask, ptr + 0); + _mm_maskmoveu_si128(a2_lo_hi, alpha_mask, ptr + 16); + _mm_maskmoveu_si128(a2_hi_lo, alpha_mask, ptr + 32); + _mm_maskmoveu_si128(a2_hi_hi, alpha_mask, ptr + 48); + // accumulate 16 alpha 'and' in parallel + all_alphas16 = _mm_and_si128(all_alphas16, a0); + ptr += 64; + } + if (i + 8 <= width - 1) { // 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; + _mm_maskmoveu_si128(a2_lo, alpha_mask, ptr); + _mm_maskmoveu_si128(a2_hi, alpha_mask, ptr + 16); + // accumulate 8 alpha 'and' in parallel + all_alphas8 = _mm_and_si128(all_alphas8, a0); + i += 8; } for (; i < width; ++i) { const uint32_t alpha_value = alpha[i]; @@ -68,8 +77,9 @@ static int DispatchAlpha_SSE2(const uint8_t* WEBP_RESTRICT alpha, 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); + alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas8, all_0xff)) & 0xff; + return (alpha_and != 0xff || + _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas16, all_0xff)) != 0xffff); } static void DispatchAlphaToGreen_SSE2(const uint8_t* WEBP_RESTRICT alpha, diff --git a/3rdparty/libwebp/src/dsp/alpha_processing_sse41.c b/3rdparty/libwebp/src/dsp/alpha_processing_sse41.c index 1156ac3417..ed95ea4ef6 100644 --- a/3rdparty/libwebp/src/dsp/alpha_processing_sse41.c +++ b/3rdparty/libwebp/src/dsp/alpha_processing_sse41.c @@ -11,10 +11,12 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include "src/dsp/cpu.h" +#include "src/webp/types.h" #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE41) - +#include #include //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/dsp/cost.c b/3rdparty/libwebp/src/dsp/cost.c index 73d2140177..acf6c21963 100644 --- a/3rdparty/libwebp/src/dsp/cost.c +++ b/3rdparty/libwebp/src/dsp/cost.c @@ -9,8 +9,15 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include +#include +#include + +#include "src/dsp/cpu.h" +#include "src/webp/types.h" #include "src/dsp/dsp.h" #include "src/enc/cost_enc.h" +#include "src/enc/vp8i_enc.h" //------------------------------------------------------------------------------ // Boolean-cost cost table @@ -354,8 +361,8 @@ static int GetResidualCost_C(int ctx0, const VP8Residual* const res) { return cost; } -static void SetResidualCoeffs_C(const int16_t* const coeffs, - VP8Residual* const res) { +static void SetResidualCoeffs_C(const int16_t* WEBP_RESTRICT const coeffs, + VP8Residual* WEBP_RESTRICT const res) { int n; res->last = -1; assert(res->first == 0 || coeffs[0] == 0); diff --git a/3rdparty/libwebp/src/dsp/cost_mips32.c b/3rdparty/libwebp/src/dsp/cost_mips32.c index 0500f88c13..5458657641 100644 --- a/3rdparty/libwebp/src/dsp/cost_mips32.c +++ b/3rdparty/libwebp/src/dsp/cost_mips32.c @@ -96,8 +96,8 @@ static int GetResidualCost_MIPS32(int ctx0, const VP8Residual* const res) { return cost; } -static void SetResidualCoeffs_MIPS32(const int16_t* const coeffs, - VP8Residual* const res) { +static void SetResidualCoeffs_MIPS32(const int16_t* WEBP_RESTRICT const coeffs, + VP8Residual* WEBP_RESTRICT const res) { const int16_t* p_coeffs = (int16_t*)coeffs; int temp0, temp1, temp2, n, n1; assert(res->first == 0 || coeffs[0] == 0); diff --git a/3rdparty/libwebp/src/dsp/cost_neon.c b/3rdparty/libwebp/src/dsp/cost_neon.c index 6582669cb3..e1bf3657e9 100644 --- a/3rdparty/libwebp/src/dsp/cost_neon.c +++ b/3rdparty/libwebp/src/dsp/cost_neon.c @@ -19,8 +19,8 @@ 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) { +static void SetResidualCoeffs_NEON(const int16_t* WEBP_RESTRICT const coeffs, + VP8Residual* WEBP_RESTRICT 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); diff --git a/3rdparty/libwebp/src/dsp/cost_sse2.c b/3rdparty/libwebp/src/dsp/cost_sse2.c index 487a079921..2fc3c1c08c 100644 --- a/3rdparty/libwebp/src/dsp/cost_sse2.c +++ b/3rdparty/libwebp/src/dsp/cost_sse2.c @@ -16,14 +16,18 @@ #if defined(WEBP_USE_SSE2) #include +#include + +#include "src/webp/types.h" +#include "src/dsp/cpu.h" #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) { +static void SetResidualCoeffs_SSE2(const int16_t* WEBP_RESTRICT const coeffs, + VP8Residual* WEBP_RESTRICT 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. diff --git a/3rdparty/libwebp/src/dsp/cpu.c b/3rdparty/libwebp/src/dsp/cpu.c index 8ba8f68335..816892fbc8 100644 --- a/3rdparty/libwebp/src/dsp/cpu.c +++ b/3rdparty/libwebp/src/dsp/cpu.c @@ -22,6 +22,10 @@ #include #endif +#include + +#include "src/webp/types.h" + //------------------------------------------------------------------------------ // SSE2 detection. // diff --git a/3rdparty/libwebp/src/dsp/cpu.h b/3rdparty/libwebp/src/dsp/cpu.h index c86540f280..1c8709c064 100644 --- a/3rdparty/libwebp/src/dsp/cpu.h +++ b/3rdparty/libwebp/src/dsp/cpu.h @@ -56,6 +56,11 @@ (defined(_M_X64) || defined(_M_IX86)) #define WEBP_MSC_SSE41 // Visual C++ SSE4.1 targets #endif + +#if defined(_MSC_VER) && _MSC_VER >= 1700 && \ + (defined(_M_X64) || defined(_M_IX86)) +#define WEBP_MSC_AVX2 // Visual C++ AVX2 targets +#endif #endif // WEBP_HAVE_* are used to indicate the presence of the instruction set in dsp @@ -80,6 +85,26 @@ #define WEBP_HAVE_SSE41 #endif +#if (defined(__AVX2__) || defined(WEBP_MSC_AVX2)) && \ + (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_AVX2)) +#define WEBP_USE_AVX2 +#endif + +#if defined(WEBP_USE_AVX2) && !defined(WEBP_HAVE_AVX2) +#define WEBP_HAVE_AVX2 +#endif + +#if defined(WEBP_MSC_AVX2) && _MSC_VER <= 1900 +#include +static WEBP_INLINE int _mm256_extract_epi32(__m256i a, const int i) { + return a.m256i_i32[i & 7]; +} +static WEBP_INLINE int _mm256_cvtsi256_si32(__m256i a) { + return _mm256_extract_epi32(a, 0); +} +#endif + +#undef WEBP_MSC_AVX2 #undef WEBP_MSC_SSE41 #undef WEBP_MSC_SSE2 diff --git a/3rdparty/libwebp/src/dsp/dec.c b/3rdparty/libwebp/src/dsp/dec.c index 451d649d58..4f38309980 100644 --- a/3rdparty/libwebp/src/dsp/dec.c +++ b/3rdparty/libwebp/src/dsp/dec.c @@ -12,10 +12,15 @@ // Author: Skal (pascal.massimino@gmail.com) #include +#include +#include -#include "src/dsp/dsp.h" +#include "src/dec/common_dec.h" #include "src/dec/vp8i_dec.h" +#include "src/dsp/cpu.h" +#include "src/dsp/dsp.h" #include "src/utils/utils.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ @@ -38,7 +43,8 @@ static WEBP_INLINE uint8_t clip_8b(int v) { } while (0) #if !WEBP_NEON_OMIT_C_CODE -static void TransformOne_C(const int16_t* in, uint8_t* dst) { +static void TransformOne_C(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { int C[4 * 4], *tmp; int i; tmp = C; @@ -82,7 +88,8 @@ static void TransformOne_C(const int16_t* in, uint8_t* dst) { } // 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) { +static void TransformAC3_C(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { const int a = in[0] + 4; const int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]); const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]); @@ -95,7 +102,8 @@ static void TransformAC3_C(const int16_t* in, uint8_t* dst) { } #undef STORE2 -static void TransformTwo_C(const int16_t* in, uint8_t* dst, int do_two) { +static void TransformTwo_C(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst, int do_two) { TransformOne_C(in, dst); if (do_two) { TransformOne_C(in + 16, dst + 4); @@ -103,13 +111,15 @@ static void TransformTwo_C(const int16_t* in, uint8_t* dst, int do_two) { } #endif // !WEBP_NEON_OMIT_C_CODE -static void TransformUV_C(const int16_t* in, uint8_t* dst) { +static void TransformUV_C(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT 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) { +static void TransformDC_C(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { const int DC = in[0] + 4; int i, j; for (j = 0; j < 4; ++j) { @@ -120,7 +130,8 @@ static void TransformDC_C(const int16_t* in, uint8_t* dst) { } #endif // !WEBP_NEON_OMIT_C_CODE -static void TransformDCUV_C(const int16_t* in, uint8_t* dst) { +static void TransformDCUV_C(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT 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); @@ -133,7 +144,8 @@ static void TransformDCUV_C(const int16_t* in, uint8_t* dst) { // Paragraph 14.3 #if !WEBP_NEON_OMIT_C_CODE -static void TransformWHT_C(const int16_t* in, int16_t* out) { +static void TransformWHT_C(const int16_t* WEBP_RESTRICT in, + int16_t* WEBP_RESTRICT out) { int tmp[16]; int i; for (i = 0; i < 4; ++i) { @@ -161,7 +173,7 @@ static void TransformWHT_C(const int16_t* in, int16_t* out) { } #endif // !WEBP_NEON_OMIT_C_CODE -void (*VP8TransformWHT)(const int16_t* in, int16_t* out); +VP8WHT VP8TransformWHT; //------------------------------------------------------------------------------ // Intra predictions @@ -661,32 +673,32 @@ static void HFilter16i_C(uint8_t* p, int stride, #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) { +static void VFilter8_C(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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) { +static void HFilter8_C(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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) { +static void VFilter8i_C(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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) { +static void HFilter8i_C(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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); } @@ -694,8 +706,8 @@ static void HFilter8i_C(uint8_t* u, uint8_t* v, int stride, //------------------------------------------------------------------------------ -static void DitherCombine8x8_C(const uint8_t* dither, uint8_t* dst, - int dst_stride) { +static void DitherCombine8x8_C(const uint8_t* WEBP_RESTRICT dither, + uint8_t* WEBP_RESTRICT dst, int dst_stride) { int i, j; for (j = 0; j < 8; ++j) { for (i = 0; i < 8; ++i) { @@ -730,8 +742,8 @@ VP8SimpleFilterFunc VP8SimpleHFilter16; VP8SimpleFilterFunc VP8SimpleVFilter16i; VP8SimpleFilterFunc VP8SimpleHFilter16i; -void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst, - int dst_stride); +void (*VP8DitherCombine8x8)(const uint8_t* WEBP_RESTRICT dither, + uint8_t* WEBP_RESTRICT dst, int dst_stride); extern VP8CPUInfo VP8GetCPUInfo; extern void VP8DspInitSSE2(void); diff --git a/3rdparty/libwebp/src/dsp/dec_clip_tables.c b/3rdparty/libwebp/src/dsp/dec_clip_tables.c index 427b74f776..4c816ddbd4 100644 --- a/3rdparty/libwebp/src/dsp/dec_clip_tables.c +++ b/3rdparty/libwebp/src/dsp/dec_clip_tables.c @@ -11,6 +11,8 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include "src/dsp/cpu.h" +#include "src/webp/types.h" #include "src/dsp/dsp.h" // define to 0 to have run-time table initialization diff --git a/3rdparty/libwebp/src/dsp/dec_mips32.c b/3rdparty/libwebp/src/dsp/dec_mips32.c index f0e7de4ac4..89fe900992 100644 --- a/3rdparty/libwebp/src/dsp/dec_mips32.c +++ b/3rdparty/libwebp/src/dsp/dec_mips32.c @@ -133,26 +133,26 @@ static void HFilter16(uint8_t* p, int stride, } // 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) { +static void VFilter8(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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) { +static void HFilter8(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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) { +static void VFilter8i(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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) { +static void HFilter8i(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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); } @@ -215,7 +215,8 @@ static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) { } } -static void TransformOne(const int16_t* in, uint8_t* dst) { +static void TransformOne(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { int temp0, temp1, temp2, temp3, temp4; int temp5, temp6, temp7, temp8, temp9; int temp10, temp11, temp12, temp13, temp14; @@ -532,7 +533,8 @@ static void TransformOne(const int16_t* in, uint8_t* dst) { ); } -static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) { +static void TransformTwo(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst, int do_two) { TransformOne(in, dst); if (do_two) { TransformOne(in + 16, dst + 4); diff --git a/3rdparty/libwebp/src/dsp/dec_mips_dsp_r2.c b/3rdparty/libwebp/src/dsp/dec_mips_dsp_r2.c index 0ba706a2ef..03b5f122b6 100644 --- a/3rdparty/libwebp/src/dsp/dec_mips_dsp_r2.c +++ b/3rdparty/libwebp/src/dsp/dec_mips_dsp_r2.c @@ -21,7 +21,8 @@ static const int kC1 = WEBP_TRANSFORM_AC3_C1; static const int kC2 = WEBP_TRANSFORM_AC3_C2; -static void TransformDC(const int16_t* in, uint8_t* dst) { +static void TransformDC(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9, temp10; __asm__ volatile ( @@ -45,7 +46,8 @@ static void TransformDC(const int16_t* in, uint8_t* dst) { ); } -static void TransformAC3(const int16_t* in, uint8_t* dst) { +static void TransformAC3(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { const int a = in[0] + 4; int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]); const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]); @@ -81,7 +83,8 @@ static void TransformAC3(const int16_t* in, uint8_t* dst) { ); } -static void TransformOne(const int16_t* in, uint8_t* dst) { +static void TransformOne(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9; int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18; @@ -148,7 +151,8 @@ static void TransformOne(const int16_t* in, uint8_t* dst) { ); } -static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) { +static void TransformTwo(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst, int do_two) { TransformOne(in, dst); if (do_two) { TransformOne(in + 16, dst + 4); @@ -434,14 +438,14 @@ static void HFilter16(uint8_t* p, int stride, } // 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) { +static void VFilter8(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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) { +static void HFilter8(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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); } @@ -465,14 +469,14 @@ static void HFilter16i(uint8_t* p, int stride, } } -static void VFilter8i(uint8_t* u, uint8_t* v, int stride, - int thresh, int ithresh, int hev_thresh) { +static void VFilter8i(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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) { +static void HFilter8i(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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); } diff --git a/3rdparty/libwebp/src/dsp/dec_msa.c b/3rdparty/libwebp/src/dsp/dec_msa.c index 58d1730192..422b363228 100644 --- a/3rdparty/libwebp/src/dsp/dec_msa.c +++ b/3rdparty/libwebp/src/dsp/dec_msa.c @@ -38,7 +38,8 @@ BUTTERFLY_4(a1_m, b1_m, c1_m, d1_m, out0, out1, out2, out3); \ } -static void TransformOne(const int16_t* in, uint8_t* dst) { +static void TransformOne(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { v8i16 input0, input1; v4i32 in0, in1, in2, in3, hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3; v4i32 res0, res1, res2, res3; @@ -65,14 +66,16 @@ static void TransformOne(const int16_t* in, uint8_t* dst) { ST4x4_UB(res0, res0, 3, 2, 1, 0, dst, BPS); } -static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) { +static void TransformTwo(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT 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) { +static void TransformWHT(const int16_t* WEBP_RESTRICT in, + int16_t* WEBP_RESTRICT 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 }; @@ -114,13 +117,15 @@ static void TransformWHT(const int16_t* in, int16_t* out) { out[240] = __msa_copy_s_h(out1, 7); } -static void TransformDC(const int16_t* in, uint8_t* dst) { +static void TransformDC(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT 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) { +static void TransformAC3(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { const int a = in[0] + 4; const int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]); const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]); @@ -475,8 +480,8 @@ static void HFilter16i(uint8_t* src_y, int stride, } // 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) { +static void VFilter8(uint8_t* WEBP_RESTRICT src_u, uint8_t* WEBP_RESTRICT 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; @@ -520,8 +525,8 @@ static void VFilter8(uint8_t* src_u, uint8_t* src_v, int 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) { +static void HFilter8(uint8_t* WEBP_RESTRICT src_u, uint8_t* WEBP_RESTRICT 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; @@ -556,7 +561,8 @@ static void HFilter8(uint8_t* src_u, uint8_t* src_v, int stride, ST6x4_UB(tmp7, 0, tmp5, 4, ptmp_src_v, stride); } -static void VFilter8i(uint8_t* src_u, uint8_t* src_v, int stride, +static void VFilter8i(uint8_t* WEBP_RESTRICT src_u, + uint8_t* WEBP_RESTRICT 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; @@ -587,7 +593,8 @@ static void VFilter8i(uint8_t* src_u, uint8_t* src_v, int stride, 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, +static void HFilter8i(uint8_t* WEBP_RESTRICT src_u, + uint8_t* WEBP_RESTRICT 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; diff --git a/3rdparty/libwebp/src/dsp/dec_neon.c b/3rdparty/libwebp/src/dsp/dec_neon.c index 83b3a1f970..f150692a4b 100644 --- a/3rdparty/libwebp/src/dsp/dec_neon.c +++ b/3rdparty/libwebp/src/dsp/dec_neon.c @@ -916,8 +916,8 @@ static void HFilter16i_NEON(uint8_t* p, int stride, #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) { +static void VFilter8_NEON(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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); { @@ -932,7 +932,8 @@ static void VFilter8_NEON(uint8_t* u, uint8_t* v, int stride, Store8x2x2_NEON(oq1, oq2, u + 2 * stride, v + 2 * stride, stride); } } -static void VFilter8i_NEON(uint8_t* u, uint8_t* v, int stride, +static void VFilter8i_NEON(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT v, + int stride, int thresh, int ithresh, int hev_thresh) { uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3; u += 4 * stride; @@ -949,8 +950,8 @@ static void VFilter8i_NEON(uint8_t* u, uint8_t* v, int 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) { +static void HFilter8_NEON(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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); { @@ -964,7 +965,8 @@ static void HFilter8_NEON(uint8_t* u, uint8_t* v, int stride, } } -static void HFilter8i_NEON(uint8_t* u, uint8_t* v, int stride, +static void HFilter8i_NEON(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT v, + int stride, int thresh, int ithresh, int hev_thresh) { uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3; u += 4; @@ -1041,7 +1043,8 @@ static WEBP_INLINE void TransformPass_NEON(int16x8x2_t* const rows) { Transpose8x2_NEON(E0, E1, rows); } -static void TransformOne_NEON(const int16_t* in, uint8_t* dst) { +static void TransformOne_NEON(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { int16x8x2_t rows; INIT_VECTOR2(rows, vld1q_s16(in + 0), vld1q_s16(in + 8)); TransformPass_NEON(&rows); @@ -1051,7 +1054,8 @@ static void TransformOne_NEON(const int16_t* in, uint8_t* dst) { #else -static void TransformOne_NEON(const int16_t* in, uint8_t* dst) { +static void TransformOne_NEON(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { const int kBPS = BPS; // kC1, kC2. Padded because vld1.16 loads 8 bytes const int16_t constants[4] = { kC1, kC2, 0, 0 }; @@ -1184,14 +1188,16 @@ static void TransformOne_NEON(const int16_t* in, uint8_t* dst) { #endif // WEBP_USE_INTRINSICS -static void TransformTwo_NEON(const int16_t* in, uint8_t* dst, int do_two) { +static void TransformTwo_NEON(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT 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) { +static void TransformDC_NEON(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { const int16x8_t DC = vdupq_n_s16(in[0]); Add4x4_NEON(DC, DC, dst); } @@ -1205,7 +1211,8 @@ static void TransformDC_NEON(const int16_t* in, uint8_t* dst) { *dst = vgetq_lane_s32(rows.val[3], col); (dst) += 16; \ } while (0) -static void TransformWHT_NEON(const int16_t* in, int16_t* out) { +static void TransformWHT_NEON(const int16_t* WEBP_RESTRICT in, + int16_t* WEBP_RESTRICT out) { int32x4x4_t tmp; { @@ -1256,7 +1263,8 @@ static void TransformWHT_NEON(const int16_t* in, int16_t* out) { //------------------------------------------------------------------------------ -static void TransformAC3_NEON(const int16_t* in, uint8_t* dst) { +static void TransformAC3_NEON(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { const int16x4_t A = vld1_dup_s16(in); const int16x4_t c4 = vdup_n_s16(WEBP_TRANSFORM_AC3_MUL2(in[4])); const int16x4_t d4 = vdup_n_s16(WEBP_TRANSFORM_AC3_MUL1(in[4])); @@ -1300,18 +1308,19 @@ static void DC4_NEON(uint8_t* dst) { // DC 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] + const uint16x8_t d = 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); + const uint8x8_t L0 = vld1_dup_u8(dst + 0 * BPS - 1); + const uint8x8_t L1 = vld1_dup_u8(dst + 1 * BPS - 1); + const uint8x8_t L2 = vld1_dup_u8(dst + 2 * BPS - 1); + const uint8x8_t L3 = vld1_dup_u8(dst + 3 * BPS - 1); + // L[r] + A[c] - A[-1] + const int16x8_t r0 = vreinterpretq_s16_u16(vaddw_u8(d, L0)); + const int16x8_t r1 = vreinterpretq_s16_u16(vaddw_u8(d, L1)); + const int16x8_t r2 = vreinterpretq_s16_u16(vaddw_u8(d, L2)); + const int16x8_t r3 = vreinterpretq_s16_u16(vaddw_u8(d, L3)); // 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)); @@ -1572,23 +1581,24 @@ 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)); + const uint16x8_t d_lo = vsubl_u8(vget_low_u8(T), TL); + const uint16x8_t d_hi = 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); + const uint8x8_t L0 = vld1_dup_u8(dst + 0 * BPS - 1); + const uint8x8_t L1 = vld1_dup_u8(dst + 1 * BPS - 1); + const uint8x8_t L2 = vld1_dup_u8(dst + 2 * BPS - 1); + const uint8x8_t L3 = vld1_dup_u8(dst + 3 * BPS - 1); + // L[r] + A[c] - A[-1] + const int16x8_t r0_lo = vreinterpretq_s16_u16(vaddw_u8(d_lo, L0)); + const int16x8_t r1_lo = vreinterpretq_s16_u16(vaddw_u8(d_lo, L1)); + const int16x8_t r2_lo = vreinterpretq_s16_u16(vaddw_u8(d_lo, L2)); + const int16x8_t r3_lo = vreinterpretq_s16_u16(vaddw_u8(d_lo, L3)); + const int16x8_t r0_hi = vreinterpretq_s16_u16(vaddw_u8(d_hi, L0)); + const int16x8_t r1_hi = vreinterpretq_s16_u16(vaddw_u8(d_hi, L1)); + const int16x8_t r2_hi = vreinterpretq_s16_u16(vaddw_u8(d_hi, L2)); + const int16x8_t r3_hi = vreinterpretq_s16_u16(vaddw_u8(d_hi, L3)); // 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)); diff --git a/3rdparty/libwebp/src/dsp/dec_sse2.c b/3rdparty/libwebp/src/dsp/dec_sse2.c index ff3a28555b..d5f273e33e 100644 --- a/3rdparty/libwebp/src/dsp/dec_sse2.c +++ b/3rdparty/libwebp/src/dsp/dec_sse2.c @@ -23,14 +23,18 @@ #endif #include -#include "src/dsp/common_sse2.h" + #include "src/dec/vp8i_dec.h" +#include "src/dsp/common_sse2.h" +#include "src/dsp/cpu.h" #include "src/utils/utils.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Transforms (Paragraph 14.4) -static void Transform_SSE2(const int16_t* in, uint8_t* dst, int do_two) { +static void Transform_SSE2(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT 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 @@ -197,7 +201,8 @@ static void Transform_SSE2(const int16_t* in, uint8_t* dst, int do_two) { #if (USE_TRANSFORM_AC3 == 1) -static void TransformAC3(const int16_t* in, uint8_t* dst) { +static void TransformAC3_SSE2(const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { const __m128i A = _mm_set1_epi16(in[0] + 4); const __m128i c4 = _mm_set1_epi16(WEBP_TRANSFORM_AC3_MUL2(in[4])); const __m128i d4 = _mm_set1_epi16(WEBP_TRANSFORM_AC3_MUL1(in[4])); @@ -792,8 +797,8 @@ static void HFilter16i_SSE2(uint8_t* p, int stride, } // 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) { +static void VFilter8_SSE2(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT v, + int stride, int thresh, int ithresh, int hev_thresh) { __m128i mask; __m128i t1, p2, p1, p0, q0, q1, q2; @@ -817,8 +822,8 @@ static void VFilter8_SSE2(uint8_t* u, uint8_t* v, int 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) { +static void HFilter8_SSE2(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT v, + int stride, int thresh, int ithresh, int hev_thresh) { __m128i mask; __m128i p3, p2, p1, p0, q0, q1, q2, q3; @@ -837,7 +842,8 @@ static void HFilter8_SSE2(uint8_t* u, uint8_t* v, int stride, Store16x4_SSE2(&q0, &q1, &q2, &q3, u, v, stride); } -static void VFilter8i_SSE2(uint8_t* u, uint8_t* v, int stride, +static void VFilter8i_SSE2(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT v, + int stride, int thresh, int ithresh, int hev_thresh) { __m128i mask; __m128i t1, t2, p1, p0, q0, q1; @@ -863,7 +869,8 @@ static void VFilter8i_SSE2(uint8_t* u, uint8_t* v, int stride, STOREUV(q1, u, v, 1 * stride); } -static void HFilter8i_SSE2(uint8_t* u, uint8_t* v, int stride, +static void HFilter8i_SSE2(uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT v, + int stride, int thresh, int ithresh, int hev_thresh) { __m128i mask; __m128i t1, t2, p1, p0, q0, q1; diff --git a/3rdparty/libwebp/src/dsp/dec_sse41.c b/3rdparty/libwebp/src/dsp/dec_sse41.c index 08a3630272..4f514b9b38 100644 --- a/3rdparty/libwebp/src/dsp/dec_sse41.c +++ b/3rdparty/libwebp/src/dsp/dec_sse41.c @@ -14,9 +14,12 @@ #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE41) - +#include #include + +#include "src/webp/types.h" #include "src/dec/vp8i_dec.h" +#include "src/dsp/cpu.h" #include "src/utils/utils.h" static void HE16_SSE41(uint8_t* dst) { // horizontal diff --git a/3rdparty/libwebp/src/dsp/dsp.h b/3rdparty/libwebp/src/dsp/dsp.h index 23bc296514..1b37ef4b90 100644 --- a/3rdparty/libwebp/src/dsp/dsp.h +++ b/3rdparty/libwebp/src/dsp/dsp.h @@ -60,53 +60,66 @@ extern "C" { // 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); +typedef void (*VP8Idct)(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst, int do_two); +typedef void (*VP8Fdct)(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT out); +typedef void (*VP8WHT)(const int16_t* WEBP_RESTRICT in, + int16_t* WEBP_RESTRICT 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); +typedef void (*VP8IntraPreds)(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top); +typedef void (*VP8Intra4Preds)(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top); extern VP8Intra4Preds VP8EncPredLuma4; extern VP8IntraPreds VP8EncPredLuma16; extern VP8IntraPreds VP8EncPredChroma8; -typedef int (*VP8Metric)(const uint8_t* pix, const uint8_t* ref); +typedef int (*VP8Metric)(const uint8_t* WEBP_RESTRICT pix, + const uint8_t* WEBP_RESTRICT ref); extern VP8Metric VP8SSE16x16, VP8SSE16x8, VP8SSE8x8, VP8SSE4x4; -typedef int (*VP8WMetric)(const uint8_t* pix, const uint8_t* ref, - const uint16_t* const weights); +typedef int (*VP8WMetric)(const uint8_t* WEBP_RESTRICT pix, + const uint8_t* WEBP_RESTRICT ref, + const uint16_t* WEBP_RESTRICT 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]); +typedef void (*VP8MeanMetric)(const uint8_t* WEBP_RESTRICT ref, + uint32_t dc[4]); extern VP8MeanMetric VP8Mean16x4; -typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst); +typedef void (*VP8BlockCopy)(const uint8_t* WEBP_RESTRICT src, + uint8_t* WEBP_RESTRICT 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); +typedef int (*VP8QuantizeBlock)( + int16_t in[16], int16_t out[16], + const struct VP8Matrix* WEBP_RESTRICT 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); +typedef int (*VP8Quantize2Blocks)( + int16_t in[32], int16_t out[32], + const struct VP8Matrix* WEBP_RESTRICT 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); +typedef int (*VP8QuantizeBlockWHT)( + int16_t in[16], int16_t out[16], + const struct VP8Matrix* WEBP_RESTRICT const mtx); extern VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT; extern const int VP8DspScan[16 + 4 + 4]; @@ -118,9 +131,10 @@ typedef struct { int max_value; int last_non_zero; } VP8Histogram; -typedef void (*VP8CHisto)(const uint8_t* ref, const uint8_t* pred, +typedef void (*VP8CHisto)(const uint8_t* WEBP_RESTRICT ref, + const uint8_t* WEBP_RESTRICT pred, int start_block, int end_block, - VP8Histogram* const histo); + VP8Histogram* WEBP_RESTRICT const histo); extern VP8CHisto VP8CollectHistogram; // General-purpose util function to help VP8CollectHistogram(). void VP8SetHistogramData(const int distribution[MAX_COEFF_THRESH + 1], @@ -138,8 +152,9 @@ 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); +typedef void (*VP8SetResidualCoeffsFunc)( + const int16_t* WEBP_RESTRICT const coeffs, + struct VP8Residual* WEBP_RESTRICT const res); extern VP8SetResidualCoeffsFunc VP8SetResidualCoeffs; // Cost calculation function. @@ -193,9 +208,11 @@ void VP8SSIMDspInit(void); //------------------------------------------------------------------------------ // Decoding -typedef void (*VP8DecIdct)(const int16_t* coeffs, uint8_t* dst); +typedef void (*VP8DecIdct)(const int16_t* WEBP_RESTRICT coeffs, + uint8_t* WEBP_RESTRICT 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); +typedef void (*VP8DecIdct2)(const int16_t* WEBP_RESTRICT coeffs, + uint8_t* WEBP_RESTRICT dst, int do_two); extern VP8DecIdct2 VP8Transform; extern VP8DecIdct VP8TransformAC3; extern VP8DecIdct VP8TransformUV; @@ -233,7 +250,8 @@ 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, +typedef void (*VP8ChromaFilterFunc)(uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT v, int stride, int thresh, int ithresh, int hev_t); // on outer edge extern VP8LumaFilterFunc VP8VFilter16; @@ -253,8 +271,8 @@ extern VP8ChromaFilterFunc VP8HFilter8i; #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); +extern void (*VP8DitherCombine8x8)(const uint8_t* WEBP_RESTRICT dither, + uint8_t* WEBP_RESTRICT dst, int dst_stride); // must be called before anything using the above void VP8DspInit(void); @@ -267,10 +285,10 @@ void VP8DspInit(void); // 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); + const uint8_t* WEBP_RESTRICT top_y, const uint8_t* WEBP_RESTRICT bottom_y, + const uint8_t* WEBP_RESTRICT top_u, const uint8_t* WEBP_RESTRICT top_v, + const uint8_t* WEBP_RESTRICT cur_u, const uint8_t* WEBP_RESTRICT cur_v, + uint8_t* WEBP_RESTRICT top_dst, uint8_t* WEBP_RESTRICT bottom_dst, int len); #ifdef FANCY_UPSAMPLING @@ -280,13 +298,15 @@ 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); +typedef void (*WebPSamplerRowFunc)(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT 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, +void WebPSamplerProcessPlane(const uint8_t* WEBP_RESTRICT y, int y_stride, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, int uv_stride, + uint8_t* WEBP_RESTRICT dst, int dst_stride, int width, int height, WebPSamplerRowFunc func); // Sampling functions to convert rows of YUV to RGB(A) @@ -298,9 +318,10 @@ extern WebPSamplerRowFunc WebPSamplers[/* MODE_LAST */]; 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); +typedef void (*WebPYUV444Converter)(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int len); extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */]; @@ -316,26 +337,35 @@ void WebPInitYUV444Converters(void); // ARGB -> YUV converters // Convert ARGB samples to luma Y. -extern void (*WebPConvertARGBToY)(const uint32_t* argb, uint8_t* y, int width); +extern void (*WebPConvertARGBToY)(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT 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, +extern void (*WebPConvertARGBToUV)(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT 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); +extern void (*WebPConvertRGBA32ToUV)(const uint16_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT 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); +extern void (*WebPConvertRGB24ToY)(const uint8_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT y, int width); +extern void (*WebPConvertBGR24ToY)(const uint8_t* WEBP_RESTRICT bgr, + uint8_t* WEBP_RESTRICT y, int width); // used for plain-C fallback. -extern void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v, +extern void WebPConvertARGBToUV_C(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT v, int src_width, int do_store); -extern void WebPConvertRGBA32ToUV_C(const uint16_t* rgb, - uint8_t* u, uint8_t* v, int width); +extern void WebPConvertRGBA32ToUV_C(const uint16_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT v, int width); // Must be called before using the above. void WebPInitConvertARGBToYUV(void); @@ -348,8 +378,9 @@ 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); +typedef void (*WebPRescalerImportRowFunc)( + struct WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT src); extern WebPRescalerImportRowFunc WebPRescalerImportRowExpand; extern WebPRescalerImportRowFunc WebPRescalerImportRowShrink; @@ -362,16 +393,19 @@ 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 WebPRescalerImportRowExpand_C( + struct WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT src); +extern void WebPRescalerImportRowShrink_C( + struct WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT 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); +extern void WebPRescalerImportRow( + struct WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT src); // Export one row (starting at x_out position) from rescaler. extern void WebPRescalerExportRow(struct WebPRescaler* const wrk); @@ -480,8 +514,9 @@ typedef enum { // Filter types. WEBP_FILTER_FAST } WEBP_FILTER_TYPE; -typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height, - int stride, uint8_t* out); +typedef void (*WebPFilterFunc)(const uint8_t* WEBP_RESTRICT in, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT 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, diff --git a/3rdparty/libwebp/src/dsp/enc.c b/3rdparty/libwebp/src/dsp/enc.c index 395ad05b0b..f1ebb74569 100644 --- a/3rdparty/libwebp/src/dsp/enc.c +++ b/3rdparty/libwebp/src/dsp/enc.c @@ -13,9 +13,13 @@ #include #include // for abs() +#include +#include "src/dsp/cpu.h" #include "src/dsp/dsp.h" #include "src/enc/vp8i_enc.h" +#include "src/utils/utils.h" +#include "src/webp/types.h" static WEBP_INLINE uint8_t clip_8b(int v) { return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255; @@ -59,9 +63,10 @@ void VP8SetHistogramData(const int distribution[MAX_COEFF_THRESH + 1], } #if !WEBP_NEON_OMIT_C_CODE -static void CollectHistogram_C(const uint8_t* ref, const uint8_t* pred, +static void CollectHistogram_C(const uint8_t* WEBP_RESTRICT ref, + const uint8_t* WEBP_RESTRICT pred, int start_block, int end_block, - VP8Histogram* const histo) { + VP8Histogram* WEBP_RESTRICT const histo) { int j; int distribution[MAX_COEFF_THRESH + 1] = { 0 }; for (j = start_block; j < end_block; ++j) { @@ -109,8 +114,9 @@ static WEBP_TSAN_IGNORE_FUNCTION void InitTables(void) { #define STORE(x, y, v) \ dst[(x) + (y) * BPS] = clip_8b(ref[(x) + (y) * BPS] + ((v) >> 3)) -static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, - uint8_t* dst) { +static WEBP_INLINE void ITransformOne(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { int C[4 * 4], *tmp; int i; tmp = C; @@ -146,7 +152,9 @@ static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, } } -static void ITransform_C(const uint8_t* ref, const int16_t* in, uint8_t* dst, +static void ITransform_C(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst, int do_two) { ITransformOne(ref, in, dst); if (do_two) { @@ -154,7 +162,9 @@ static void ITransform_C(const uint8_t* ref, const int16_t* in, uint8_t* dst, } } -static void FTransform_C(const uint8_t* src, const uint8_t* ref, int16_t* out) { +static void FTransform_C(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT out) { int i; int tmp[16]; for (i = 0; i < 4; ++i, src += BPS, ref += BPS) { @@ -184,14 +194,16 @@ static void FTransform_C(const uint8_t* src, const uint8_t* ref, int16_t* out) { } #endif // !WEBP_NEON_OMIT_C_CODE -static void FTransform2_C(const uint8_t* src, const uint8_t* ref, - int16_t* out) { +static void FTransform2_C(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT 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) { +static void FTransformWHT_C(const int16_t* WEBP_RESTRICT in, + int16_t* WEBP_RESTRICT out) { // input is 12b signed int32_t tmp[16]; int i; @@ -234,8 +246,9 @@ static WEBP_INLINE void Fill(uint8_t* dst, int value, int size) { } } -static WEBP_INLINE void VerticalPred(uint8_t* dst, - const uint8_t* top, int size) { +static WEBP_INLINE void VerticalPred(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top, + int size) { int j; if (top != NULL) { for (j = 0; j < size; ++j) memcpy(dst + j * BPS, top, size); @@ -244,8 +257,9 @@ static WEBP_INLINE void VerticalPred(uint8_t* dst, } } -static WEBP_INLINE void HorizontalPred(uint8_t* dst, - const uint8_t* left, int size) { +static WEBP_INLINE void HorizontalPred(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + int size) { if (left != NULL) { int j; for (j = 0; j < size; ++j) { @@ -256,8 +270,9 @@ static WEBP_INLINE void HorizontalPred(uint8_t* dst, } } -static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left, - const uint8_t* top, int size) { +static WEBP_INLINE void TrueMotion(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top, int size) { int y; if (left != NULL) { if (top != NULL) { @@ -286,8 +301,9 @@ static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left, } } -static WEBP_INLINE void DCMode(uint8_t* dst, const uint8_t* left, - const uint8_t* top, +static WEBP_INLINE void DCMode(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top, int size, int round, int shift) { int DC = 0; int j; @@ -312,8 +328,9 @@ static WEBP_INLINE void DCMode(uint8_t* dst, const uint8_t* left, //------------------------------------------------------------------------------ // Chroma 8x8 prediction (paragraph 12.2) -static void IntraChromaPreds_C(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static void IntraChromaPreds_C(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { // U block DCMode(C8DC8 + dst, left, top, 8, 8, 4); VerticalPred(C8VE8 + dst, top, 8); @@ -332,22 +349,28 @@ static void IntraChromaPreds_C(uint8_t* dst, const uint8_t* left, //------------------------------------------------------------------------------ // luma 16x16 prediction (paragraph 12.3) -static void Intra16Preds_C(uint8_t* dst, - const uint8_t* left, const uint8_t* top) { +#if !WEBP_NEON_OMIT_C_CODE || !WEBP_AARCH64 +static void Intra16Preds_C(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT 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); } +#endif // !WEBP_NEON_OMIT_C_CODE || !WEBP_AARCH64 //------------------------------------------------------------------------------ // luma 4x4 prediction +#if !WEBP_NEON_OMIT_C_CODE || !WEBP_AARCH64 || BPS != 32 + #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 +// vertical +static void VE4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { const uint8_t vals[4] = { AVG3(top[-1], top[0], top[1]), AVG3(top[ 0], top[1], top[2]), @@ -360,7 +383,8 @@ static void VE4(uint8_t* dst, const uint8_t* top) { // vertical } } -static void HE4(uint8_t* dst, const uint8_t* top) { // horizontal +// horizontal +static void HE4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { const int X = top[-1]; const int I = top[-2]; const int J = top[-3]; @@ -372,14 +396,14 @@ static void HE4(uint8_t* dst, const uint8_t* top) { // horizontal WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(K, L, L)); } -static void DC4(uint8_t* dst, const uint8_t* top) { +static void DC4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT 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) { +static void RD4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { const int X = top[-1]; const int I = top[-2]; const int J = top[-3]; @@ -398,7 +422,7 @@ static void RD4(uint8_t* dst, const uint8_t* top) { DST(3, 0) = AVG3(D, C, B); } -static void LD4(uint8_t* dst, const uint8_t* top) { +static void LD4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { const int A = top[0]; const int B = top[1]; const int C = top[2]; @@ -416,7 +440,7 @@ static void LD4(uint8_t* dst, const uint8_t* top) { DST(3, 3) = AVG3(G, H, H); } -static void VR4(uint8_t* dst, const uint8_t* top) { +static void VR4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { const int X = top[-1]; const int I = top[-2]; const int J = top[-3]; @@ -438,7 +462,7 @@ static void VR4(uint8_t* dst, const uint8_t* top) { DST(3, 1) = AVG3(B, C, D); } -static void VL4(uint8_t* dst, const uint8_t* top) { +static void VL4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { const int A = top[0]; const int B = top[1]; const int C = top[2]; @@ -460,7 +484,7 @@ static void VL4(uint8_t* dst, const uint8_t* top) { DST(3, 3) = AVG3(F, G, H); } -static void HU4(uint8_t* dst, const uint8_t* top) { +static void HU4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { const int I = top[-2]; const int J = top[-3]; const int K = top[-4]; @@ -475,7 +499,7 @@ static void HU4(uint8_t* dst, const uint8_t* top) { DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L; } -static void HD4(uint8_t* dst, const uint8_t* top) { +static void HD4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { const int X = top[-1]; const int I = top[-2]; const int J = top[-3]; @@ -498,7 +522,7 @@ static void HD4(uint8_t* dst, const uint8_t* top) { DST(1, 3) = AVG3(L, K, J); } -static void TM4(uint8_t* dst, const uint8_t* top) { +static void TM4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int x, y; const uint8_t* const clip = clip1 + 255 - top[-1]; for (y = 0; y < 4; ++y) { @@ -516,7 +540,8 @@ static void TM4(uint8_t* dst, const uint8_t* 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_C(uint8_t* dst, const uint8_t* top) { +static void Intra4Preds_C(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { DC4(I4DC4 + dst, top); TM4(I4TM4 + dst, top); VE4(I4VE4 + dst, top); @@ -529,11 +554,14 @@ static void Intra4Preds_C(uint8_t* dst, const uint8_t* top) { HU4(I4HU4 + dst, top); } +#endif // !WEBP_NEON_OMIT_C_CODE || !WEBP_AARCH64 || BPS != 32 + //------------------------------------------------------------------------------ // Metric #if !WEBP_NEON_OMIT_C_CODE -static WEBP_INLINE int GetSSE(const uint8_t* a, const uint8_t* b, +static WEBP_INLINE int GetSSE(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b, int w, int h) { int count = 0; int y, x; @@ -548,21 +576,25 @@ static WEBP_INLINE int GetSSE(const uint8_t* a, const uint8_t* b, return count; } -static int SSE16x16_C(const uint8_t* a, const uint8_t* b) { +static int SSE16x16_C(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { return GetSSE(a, b, 16, 16); } -static int SSE16x8_C(const uint8_t* a, const uint8_t* b) { +static int SSE16x8_C(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { return GetSSE(a, b, 16, 8); } -static int SSE8x8_C(const uint8_t* a, const uint8_t* b) { +static int SSE8x8_C(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { return GetSSE(a, b, 8, 8); } -static int SSE4x4_C(const uint8_t* a, const uint8_t* b) { +static int SSE4x4_C(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT 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]) { +static void Mean16x4_C(const uint8_t* WEBP_RESTRICT ref, uint32_t dc[4]) { int k, x, y; for (k = 0; k < 4; ++k) { uint32_t avg = 0; @@ -586,7 +618,8 @@ static void Mean16x4_C(const uint8_t* ref, uint32_t dc[4]) { // 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) { +static int TTransform(const uint8_t* WEBP_RESTRICT in, + const uint16_t* WEBP_RESTRICT w) { int sum = 0; int tmp[16]; int i; @@ -620,15 +653,17 @@ static int TTransform(const uint8_t* in, const uint16_t* w) { return sum; } -static int Disto4x4_C(const uint8_t* const a, const uint8_t* const b, - const uint16_t* const w) { +static int Disto4x4_C(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT 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) { +static int Disto16x16_C(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT const w) { int D = 0; int x, y; for (y = 0; y < 16 * BPS; y += 4 * BPS) { @@ -644,23 +679,24 @@ static int Disto16x16_C(const uint8_t* const a, const uint8_t* const b, // Quantization // +#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC 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) { + const VP8Matrix* WEBP_RESTRICT 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]; + 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; @@ -675,9 +711,8 @@ static int QuantizeBlock_C(int16_t in[16], int16_t out[16], 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) { + const VP8Matrix* WEBP_RESTRICT const mtx) { int nz; nz = VP8EncQuantizeBlock(in + 0 * 16, out + 0 * 16, mtx) << 0; nz |= VP8EncQuantizeBlock(in + 1 * 16, out + 1 * 16, mtx) << 1; @@ -688,7 +723,8 @@ static int Quantize2Blocks_C(int16_t in[32], int16_t out[32], //------------------------------------------------------------------------------ // Block copy -static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int w, int h) { +static WEBP_INLINE void Copy(const uint8_t* WEBP_RESTRICT src, + uint8_t* WEBP_RESTRICT dst, int w, int h) { int y; for (y = 0; y < h; ++y) { memcpy(dst, src, w); @@ -697,11 +733,13 @@ static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int w, int h) { } } -static void Copy4x4_C(const uint8_t* src, uint8_t* dst) { +static void Copy4x4_C(const uint8_t* WEBP_RESTRICT src, + uint8_t* WEBP_RESTRICT dst) { Copy(src, dst, 4, 4); } -static void Copy16x8_C(const uint8_t* src, uint8_t* dst) { +static void Copy16x8_C(const uint8_t* WEBP_RESTRICT src, + uint8_t* WEBP_RESTRICT dst) { Copy(src, dst, 16, 8); } @@ -760,14 +798,19 @@ WEBP_DSP_INIT_FUNC(VP8EncDspInit) { #if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC VP8EncQuantizeBlock = QuantizeBlock_C; VP8EncQuantize2Blocks = Quantize2Blocks_C; + VP8EncQuantizeBlockWHT = QuantizeBlock_C; +#endif + +#if !WEBP_NEON_OMIT_C_CODE || !WEBP_AARCH64 || BPS != 32 + VP8EncPredLuma4 = Intra4Preds_C; +#endif +#if !WEBP_NEON_OMIT_C_CODE || !WEBP_AARCH64 + VP8EncPredLuma16 = Intra16Preds_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; diff --git a/3rdparty/libwebp/src/dsp/enc_mips32.c b/3rdparty/libwebp/src/dsp/enc_mips32.c index 50518a5f1a..1a06ff3c13 100644 --- a/3rdparty/libwebp/src/dsp/enc_mips32.c +++ b/3rdparty/libwebp/src/dsp/enc_mips32.c @@ -109,9 +109,9 @@ static const int kC2 = WEBP_TRANSFORM_AC3_C2; "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) { +static WEBP_INLINE void ITransformOne_MIPS32(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT 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; @@ -141,8 +141,9 @@ static WEBP_INLINE void ITransformOne_MIPS32(const uint8_t* ref, ); } -static void ITransform_MIPS32(const uint8_t* ref, const int16_t* in, - uint8_t* dst, int do_two) { +static void ITransform_MIPS32(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst, int do_two) { ITransformOne_MIPS32(ref, in, dst); if (do_two) { ITransformOne_MIPS32(ref + 4, in + 16, dst + 4); @@ -192,11 +193,11 @@ static int QuantizeBlock_MIPS32(int16_t in[16], int16_t out[16], 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]; + 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) @@ -236,7 +237,7 @@ static int QuantizeBlock_MIPS32(int16_t in[16], int16_t out[16], } static int Quantize2Blocks_MIPS32(int16_t in[32], int16_t out[32], - const VP8Matrix* const mtx) { + const VP8Matrix* WEBP_RESTRICT 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; @@ -358,8 +359,9 @@ static int Quantize2Blocks_MIPS32(int16_t in[32], int16_t out[32], "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) { +static int Disto4x4_MIPS32(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT const w) { int tmp[32]; int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; @@ -393,8 +395,9 @@ static int Disto4x4_MIPS32(const uint8_t* const a, const uint8_t* const b, #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) { +static int Disto16x16_MIPS32(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT const w) { int D = 0; int x, y; for (y = 0; y < 16 * BPS; y += 4 * BPS) { @@ -475,8 +478,9 @@ static int Disto16x16_MIPS32(const uint8_t* const a, const uint8_t* const b, "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) { +static void FTransform_MIPS32(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT 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; @@ -537,7 +541,8 @@ static void FTransform_MIPS32(const uint8_t* src, const uint8_t* ref, 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) { +static int SSE16x16_MIPS32(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { int count; int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; @@ -571,7 +576,8 @@ static int SSE16x16_MIPS32(const uint8_t* a, const uint8_t* b) { return count; } -static int SSE16x8_MIPS32(const uint8_t* a, const uint8_t* b) { +static int SSE16x8_MIPS32(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { int count; int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; @@ -597,7 +603,8 @@ static int SSE16x8_MIPS32(const uint8_t* a, const uint8_t* b) { return count; } -static int SSE8x8_MIPS32(const uint8_t* a, const uint8_t* b) { +static int SSE8x8_MIPS32(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { int count; int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; @@ -619,7 +626,8 @@ static int SSE8x8_MIPS32(const uint8_t* a, const uint8_t* b) { return count; } -static int SSE4x4_MIPS32(const uint8_t* a, const uint8_t* b) { +static int SSE4x4_MIPS32(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { int count; int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; diff --git a/3rdparty/libwebp/src/dsp/enc_mips_dsp_r2.c b/3rdparty/libwebp/src/dsp/enc_mips_dsp_r2.c index e1431f3bef..ecfa3da268 100644 --- a/3rdparty/libwebp/src/dsp/enc_mips_dsp_r2.c +++ b/3rdparty/libwebp/src/dsp/enc_mips_dsp_r2.c @@ -141,8 +141,9 @@ static const int kC2 = WEBP_TRANSFORM_AC3_C2; "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) { +static void FTransform_MIPSdspR2(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT out) { const int c2217 = 2217; const int c5352 = 5352; int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; @@ -171,8 +172,9 @@ static void FTransform_MIPSdspR2(const uint8_t* src, const uint8_t* ref, #undef VERTICAL_PASS #undef HORIZONTAL_PASS -static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, - uint8_t* dst) { +static WEBP_INLINE void ITransformOne(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9; int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18; @@ -239,16 +241,18 @@ static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, ); } -static void ITransform_MIPSdspR2(const uint8_t* ref, const int16_t* in, - uint8_t* dst, int do_two) { +static void ITransform_MIPSdspR2(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT 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) { +static int Disto4x4_MIPSdspR2(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT const w) { int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9; int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17; @@ -314,9 +318,9 @@ static int Disto4x4_MIPSdspR2(const uint8_t* const a, const uint8_t* const b, return abs(temp3 - temp17) >> 5; } -static int Disto16x16_MIPSdspR2(const uint8_t* const a, - const uint8_t* const b, - const uint16_t* const w) { +static int Disto16x16_MIPSdspR2(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT const w) { int D = 0; int x, y; for (y = 0; y < 16 * BPS; y += 4 * BPS) { @@ -367,8 +371,8 @@ static int Disto16x16_MIPSdspR2(const uint8_t* const a, } while (0) #define VERTICAL_PRED(DST, TOP, SIZE) \ -static WEBP_INLINE void VerticalPred##SIZE(uint8_t* (DST), \ - const uint8_t* (TOP)) { \ +static WEBP_INLINE void VerticalPred##SIZE( \ + uint8_t* WEBP_RESTRICT (DST), const uint8_t* WEBP_RESTRICT (TOP)) { \ int j; \ if ((TOP)) { \ for (j = 0; j < (SIZE); ++j) memcpy((DST) + j * BPS, (TOP), (SIZE)); \ @@ -383,8 +387,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)) { \ +static WEBP_INLINE void HorizontalPred##SIZE( \ + uint8_t* WEBP_RESTRICT (DST), const uint8_t* WEBP_RESTRICT (LEFT)) { \ if (LEFT) { \ int j; \ for (j = 0; j < (SIZE); ++j) { \ @@ -451,8 +455,9 @@ HORIZONTAL_PRED(dst, left, 16) } 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)) { \ +static WEBP_INLINE void TrueMotion##SIZE(uint8_t* WEBP_RESTRICT (DST), \ + const uint8_t* WEBP_RESTRICT (LEFT), \ + const uint8_t* WEBP_RESTRICT (TOP)) { \ if ((LEFT) != NULL) { \ if ((TOP) != NULL) { \ CLIP_TO_DST((DST), (LEFT), (TOP), (SIZE)); \ @@ -480,8 +485,9 @@ TRUE_MOTION(dst, left, top, 16) #undef CLIP_8B_TO_DST #undef CLIPPING -static WEBP_INLINE void DCMode16(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static WEBP_INLINE void DCMode16(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { int DC, DC1; int temp0, temp1, temp2, temp3; @@ -543,8 +549,9 @@ static WEBP_INLINE void DCMode16(uint8_t* dst, const uint8_t* left, FILL_8_OR_16(dst, DC, 16); } -static WEBP_INLINE void DCMode8(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static WEBP_INLINE void DCMode8(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { int DC, DC1; int temp0, temp1, temp2, temp3; @@ -588,7 +595,7 @@ static WEBP_INLINE void DCMode8(uint8_t* dst, const uint8_t* left, FILL_8_OR_16(dst, DC, 8); } -static void DC4(uint8_t* dst, const uint8_t* top) { +static void DC4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int temp0, temp1; __asm__ volatile( "ulw %[temp0], 0(%[top]) \n\t" @@ -609,7 +616,7 @@ static void DC4(uint8_t* dst, const uint8_t* top) { ); } -static void TM4(uint8_t* dst, const uint8_t* top) { +static void TM4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int a10, a32, temp0, temp1, temp2, temp3, temp4, temp5; const int c35 = 0xff00ff; __asm__ volatile ( @@ -664,7 +671,7 @@ static void TM4(uint8_t* dst, const uint8_t* top) { ); } -static void VE4(uint8_t* dst, const uint8_t* top) { +static void VE4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int temp0, temp1, temp2, temp3, temp4, temp5, temp6; __asm__ volatile( "ulw %[temp0], -1(%[top]) \n\t" @@ -695,7 +702,7 @@ static void VE4(uint8_t* dst, const uint8_t* top) { ); } -static void HE4(uint8_t* dst, const uint8_t* top) { +static void HE4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int temp0, temp1, temp2, temp3, temp4, temp5, temp6; __asm__ volatile( "ulw %[temp0], -4(%[top]) \n\t" @@ -731,7 +738,7 @@ static void HE4(uint8_t* dst, const uint8_t* top) { ); } -static void RD4(uint8_t* dst, const uint8_t* top) { +static void RD4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int temp0, temp1, temp2, temp3, temp4, temp5; int temp6, temp7, temp8, temp9, temp10, temp11; __asm__ volatile( @@ -780,7 +787,7 @@ static void RD4(uint8_t* dst, const uint8_t* top) { ); } -static void VR4(uint8_t* dst, const uint8_t* top) { +static void VR4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int temp0, temp1, temp2, temp3, temp4; int temp5, temp6, temp7, temp8, temp9; __asm__ volatile ( @@ -830,7 +837,7 @@ static void VR4(uint8_t* dst, const uint8_t* top) { ); } -static void LD4(uint8_t* dst, const uint8_t* top) { +static void LD4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int temp0, temp1, temp2, temp3, temp4, temp5; int temp6, temp7, temp8, temp9, temp10, temp11; __asm__ volatile( @@ -877,7 +884,7 @@ static void LD4(uint8_t* dst, const uint8_t* top) { ); } -static void VL4(uint8_t* dst, const uint8_t* top) { +static void VL4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int temp0, temp1, temp2, temp3, temp4; int temp5, temp6, temp7, temp8, temp9; __asm__ volatile ( @@ -926,7 +933,7 @@ static void VL4(uint8_t* dst, const uint8_t* top) { ); } -static void HD4(uint8_t* dst, const uint8_t* top) { +static void HD4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int temp0, temp1, temp2, temp3, temp4; int temp5, temp6, temp7, temp8, temp9; __asm__ volatile ( @@ -974,7 +981,7 @@ static void HD4(uint8_t* dst, const uint8_t* top) { ); } -static void HU4(uint8_t* dst, const uint8_t* top) { +static void HU4(uint8_t* WEBP_RESTRICT dst, const uint8_t* WEBP_RESTRICT top) { int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; __asm__ volatile ( "ulw %[temp0], -5(%[top]) \n\t" @@ -1013,8 +1020,9 @@ static void HU4(uint8_t* dst, const uint8_t* top) { //------------------------------------------------------------------------------ // Chroma 8x8 prediction (paragraph 12.2) -static void IntraChromaPreds_MIPSdspR2(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static void IntraChromaPreds_MIPSdspR2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { // U block DCMode8(C8DC8 + dst, left, top); VerticalPred8(C8VE8 + dst, top); @@ -1033,8 +1041,9 @@ static void IntraChromaPreds_MIPSdspR2(uint8_t* dst, const uint8_t* left, //------------------------------------------------------------------------------ // luma 16x16 prediction (paragraph 12.3) -static void Intra16Preds_MIPSdspR2(uint8_t* dst, - const uint8_t* left, const uint8_t* top) { +static void Intra16Preds_MIPSdspR2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { DCMode16(I16DC16 + dst, left, top); VerticalPred16(I16VE16 + dst, top); HorizontalPred16(I16HE16 + dst, left); @@ -1043,7 +1052,8 @@ static void Intra16Preds_MIPSdspR2(uint8_t* dst, // 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) { +static void Intra4Preds_MIPSdspR2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { DC4(I4DC4 + dst, top); TM4(I4TM4 + dst, top); VE4(I4VE4 + dst, top); @@ -1079,7 +1089,8 @@ static void Intra4Preds_MIPSdspR2(uint8_t* dst, const uint8_t* top) { GET_SSE_INNER(C) \ GET_SSE_INNER(D) -static int SSE16x16_MIPSdspR2(const uint8_t* a, const uint8_t* b) { +static int SSE16x16_MIPSdspR2(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { int count; int temp0, temp1, temp2, temp3; __asm__ volatile ( @@ -1109,7 +1120,8 @@ static int SSE16x16_MIPSdspR2(const uint8_t* a, const uint8_t* b) { return count; } -static int SSE16x8_MIPSdspR2(const uint8_t* a, const uint8_t* b) { +static int SSE16x8_MIPSdspR2(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { int count; int temp0, temp1, temp2, temp3; __asm__ volatile ( @@ -1131,7 +1143,8 @@ static int SSE16x8_MIPSdspR2(const uint8_t* a, const uint8_t* b) { return count; } -static int SSE8x8_MIPSdspR2(const uint8_t* a, const uint8_t* b) { +static int SSE8x8_MIPSdspR2(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { int count; int temp0, temp1, temp2, temp3; __asm__ volatile ( @@ -1149,7 +1162,8 @@ static int SSE8x8_MIPSdspR2(const uint8_t* a, const uint8_t* b) { return count; } -static int SSE4x4_MIPSdspR2(const uint8_t* a, const uint8_t* b) { +static int SSE4x4_MIPSdspR2(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { int count; int temp0, temp1, temp2, temp3; __asm__ volatile ( @@ -1273,7 +1287,7 @@ static int SSE4x4_MIPSdspR2(const uint8_t* a, const uint8_t* b) { "3: \n\t" static int QuantizeBlock_MIPSdspR2(int16_t in[16], int16_t out[16], - const VP8Matrix* const mtx) { + const VP8Matrix* WEBP_RESTRICT const mtx) { int temp0, temp1, temp2, temp3, temp4, temp5,temp6; int sign, coeff, level; int max_level = MAX_LEVEL; @@ -1282,11 +1296,11 @@ static int QuantizeBlock_MIPSdspR2(int16_t in[16], int16_t out[16], 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]; + 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) @@ -1314,7 +1328,7 @@ static int QuantizeBlock_MIPSdspR2(int16_t in[16], int16_t out[16], } static int Quantize2Blocks_MIPSdspR2(int16_t in[32], int16_t out[32], - const VP8Matrix* const mtx) { + const VP8Matrix* WEBP_RESTRICT 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; @@ -1360,7 +1374,8 @@ static int Quantize2Blocks_MIPSdspR2(int16_t in[32], int16_t out[32], "usw %[" #TEMP4 "], " #C "(%[out]) \n\t" \ "usw %[" #TEMP6 "], " #D "(%[out]) \n\t" -static void FTransformWHT_MIPSdspR2(const int16_t* in, int16_t* out) { +static void FTransformWHT_MIPSdspR2(const int16_t* WEBP_RESTRICT in, + int16_t* WEBP_RESTRICT out) { int temp0, temp1, temp2, temp3, temp4; int temp5, temp6, temp7, temp8, temp9; diff --git a/3rdparty/libwebp/src/dsp/enc_msa.c b/3rdparty/libwebp/src/dsp/enc_msa.c index 6f85add4bb..9a54e51a8c 100644 --- a/3rdparty/libwebp/src/dsp/enc_msa.c +++ b/3rdparty/libwebp/src/dsp/enc_msa.c @@ -41,8 +41,9 @@ 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) { +static WEBP_INLINE void ITransformOne(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { v8i16 input0, input1; v4i32 in0, in1, in2, in3, hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3; v4i32 res0, res1, res2, res3; @@ -69,16 +70,18 @@ static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, 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) { +static void ITransform_MSA(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT 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) { +static void FTransform_MSA(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT out) { uint64_t out0, out1, out2, out3; uint32_t in0, in1, in2, in3; v4i32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; @@ -131,7 +134,8 @@ static void FTransform_MSA(const uint8_t* src, const uint8_t* ref, SD4(out0, out1, out2, out3, out, 8); } -static void FTransformWHT_MSA(const int16_t* in, int16_t* out) { +static void FTransformWHT_MSA(const int16_t* WEBP_RESTRICT in, + int16_t* WEBP_RESTRICT out) { v8i16 in0 = { 0 }; v8i16 in1 = { 0 }; v8i16 tmp0, tmp1, tmp2, tmp3; @@ -168,7 +172,8 @@ static void FTransformWHT_MSA(const int16_t* in, int16_t* out) { ST_SH2(out0, out1, out, 8); } -static int TTransform_MSA(const uint8_t* in, const uint16_t* w) { +static int TTransform_MSA(const uint8_t* WEBP_RESTRICT in, + const uint16_t* WEBP_RESTRICT w) { int sum; uint32_t in0_m, in1_m, in2_m, in3_m; v16i8 src0 = { 0 }; @@ -200,15 +205,17 @@ static int TTransform_MSA(const uint8_t* in, const uint16_t* w) { return sum; } -static int Disto4x4_MSA(const uint8_t* const a, const uint8_t* const b, - const uint16_t* const w) { +static int Disto4x4_MSA(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT 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) { +static int Disto16x16_MSA(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT const w) { int D = 0; int x, y; for (y = 0; y < 16 * BPS; y += 4 * BPS) { @@ -259,7 +266,9 @@ static void CollectHistogram_MSA(const uint8_t* ref, const uint8_t* pred, #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 +// vertical +static WEBP_INLINE void VE4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const v16u8 A1 = { 0 }; const uint64_t val_m = LD(top - 1); const v16u8 A = (v16u8)__msa_insert_d((v2i64)A1, 0, val_m); @@ -272,7 +281,9 @@ static WEBP_INLINE void VE4(uint8_t* dst, const uint8_t* top) { // vertical SW4(out, out, out, out, dst, BPS); } -static WEBP_INLINE void HE4(uint8_t* dst, const uint8_t* top) { // horizontal +// horizontal +static WEBP_INLINE void HE4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const int X = top[-1]; const int I = top[-2]; const int J = top[-3]; @@ -284,7 +295,8 @@ static WEBP_INLINE void HE4(uint8_t* dst, const uint8_t* top) { // horizontal WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(K, L, L)); } -static WEBP_INLINE void DC4(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void DC4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { uint32_t dc = 4; int i; for (i = 0; i < 4; ++i) dc += top[i] + top[-5 + i]; @@ -293,7 +305,8 @@ static WEBP_INLINE void DC4(uint8_t* dst, const uint8_t* top) { SW4(dc, dc, dc, dc, dst, BPS); } -static WEBP_INLINE void RD4(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void RD4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT 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); @@ -313,7 +326,8 @@ static WEBP_INLINE void RD4(uint8_t* dst, const uint8_t* top) { SW4(val3, val2, val1, val0, dst, BPS); } -static WEBP_INLINE void LD4(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void LD4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const v16u8 A1 = { 0 }; const uint64_t val_m = LD(top); const v16u8 A = (v16u8)__msa_insert_d((v2i64)A1, 0, val_m); @@ -333,7 +347,8 @@ static WEBP_INLINE void LD4(uint8_t* dst, const uint8_t* top) { SW4(val0, val1, val2, val3, dst, BPS); } -static WEBP_INLINE void VR4(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void VR4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const int X = top[-1]; const int I = top[-2]; const int J = top[-3]; @@ -354,7 +369,8 @@ static WEBP_INLINE void VR4(uint8_t* dst, const uint8_t* top) { DST(3, 1) = AVG3(B, C, D); } -static WEBP_INLINE void VL4(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void VL4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const int A = top[0]; const int B = top[1]; const int C = top[2]; @@ -375,7 +391,8 @@ static WEBP_INLINE void VL4(uint8_t* dst, const uint8_t* top) { DST(3, 3) = AVG3(F, G, H); } -static WEBP_INLINE void HU4(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void HU4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const int I = top[-2]; const int J = top[-3]; const int K = top[-4]; @@ -390,7 +407,8 @@ static WEBP_INLINE void HU4(uint8_t* dst, const uint8_t* top) { 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) { +static WEBP_INLINE void HD4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const int X = top[-1]; const int I = top[-2]; const int J = top[-3]; @@ -411,7 +429,8 @@ static WEBP_INLINE void HD4(uint8_t* dst, const uint8_t* top) { DST(1, 3) = AVG3(L, K, J); } -static WEBP_INLINE void TM4(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void TM4(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const v16i8 zero = { 0 }; const v8i16 TL = (v8i16)__msa_fill_h(top[-1]); const v8i16 L0 = (v8i16)__msa_fill_h(top[-2]); @@ -431,7 +450,8 @@ static WEBP_INLINE void TM4(uint8_t* dst, const uint8_t* top) { #undef AVG3 #undef AVG2 -static void Intra4Preds_MSA(uint8_t* dst, const uint8_t* top) { +static void Intra4Preds_MSA(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { DC4(I4DC4 + dst, top); TM4(I4TM4 + dst, top); VE4(I4VE4 + dst, top); @@ -451,7 +471,8 @@ static void Intra4Preds_MSA(uint8_t* dst, const uint8_t* top) { 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) { +static WEBP_INLINE void VerticalPred16x16(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { if (top != NULL) { const v16u8 out = LD_UB(top); STORE16x16(out, dst); @@ -461,8 +482,8 @@ static WEBP_INLINE void VerticalPred16x16(uint8_t* dst, const uint8_t* top) { } } -static WEBP_INLINE void HorizontalPred16x16(uint8_t* dst, - const uint8_t* left) { +static WEBP_INLINE void HorizontalPred16x16(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left) { if (left != NULL) { int j; for (j = 0; j < 16; j += 4) { @@ -480,8 +501,9 @@ static WEBP_INLINE void HorizontalPred16x16(uint8_t* dst, } } -static WEBP_INLINE void TrueMotion16x16(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static WEBP_INLINE void TrueMotion16x16(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { if (left != NULL) { if (top != NULL) { int j; @@ -519,8 +541,9 @@ static WEBP_INLINE void TrueMotion16x16(uint8_t* dst, const uint8_t* left, } } -static WEBP_INLINE void DCMode16x16(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static WEBP_INLINE void DCMode16x16(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { int DC; v16u8 out; if (top != NULL && left != NULL) { @@ -548,8 +571,9 @@ static WEBP_INLINE void DCMode16x16(uint8_t* dst, const uint8_t* left, STORE16x16(out, dst); } -static void Intra16Preds_MSA(uint8_t* dst, - const uint8_t* left, const uint8_t* top) { +static void Intra16Preds_MSA(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { DCMode16x16(I16DC16 + dst, left, top); VerticalPred16x16(I16VE16 + dst, top); HorizontalPred16x16(I16HE16 + dst, left); @@ -574,7 +598,8 @@ static void Intra16Preds_MSA(uint8_t* dst, SD4(out, out, out, out, dst + 4 * BPS, BPS); \ } while (0) -static WEBP_INLINE void VerticalPred8x8(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void VerticalPred8x8(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { if (top != NULL) { const uint64_t out = LD(top); STORE8x8(out, dst); @@ -584,7 +609,8 @@ static WEBP_INLINE void VerticalPred8x8(uint8_t* dst, const uint8_t* top) { } } -static WEBP_INLINE void HorizontalPred8x8(uint8_t* dst, const uint8_t* left) { +static WEBP_INLINE void HorizontalPred8x8(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left) { if (left != NULL) { int j; for (j = 0; j < 8; j += 4) { @@ -606,8 +632,9 @@ static WEBP_INLINE void HorizontalPred8x8(uint8_t* dst, const uint8_t* left) { } } -static WEBP_INLINE void TrueMotion8x8(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static WEBP_INLINE void TrueMotion8x8(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { if (left != NULL) { if (top != NULL) { int j; @@ -646,8 +673,9 @@ static WEBP_INLINE void TrueMotion8x8(uint8_t* dst, const uint8_t* left, } } -static WEBP_INLINE void DCMode8x8(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static WEBP_INLINE void DCMode8x8(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { uint64_t out; v16u8 src = { 0 }; if (top != NULL && left != NULL) { @@ -670,8 +698,9 @@ static WEBP_INLINE void DCMode8x8(uint8_t* dst, const uint8_t* left, STORE8x8(out, dst); } -static void IntraChromaPreds_MSA(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static void IntraChromaPreds_MSA(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { // U block DCMode8x8(C8DC8 + dst, left, top); VerticalPred8x8(C8VE8 + dst, top); @@ -712,7 +741,8 @@ static void IntraChromaPreds_MSA(uint8_t* dst, const uint8_t* left, DPADD_SH2_SW(tmp2, tmp3, tmp2, tmp3, out2, out3); \ } while (0) -static int SSE16x16_MSA(const uint8_t* a, const uint8_t* b) { +static int SSE16x16_MSA(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { uint32_t sum; v16u8 src0, src1, src2, src3, src4, src5, src6, src7; v16u8 ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7; @@ -739,7 +769,8 @@ static int SSE16x16_MSA(const uint8_t* a, const uint8_t* b) { return sum; } -static int SSE16x8_MSA(const uint8_t* a, const uint8_t* b) { +static int SSE16x8_MSA(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { uint32_t sum; v16u8 src0, src1, src2, src3, src4, src5, src6, src7; v16u8 ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7; @@ -758,7 +789,8 @@ static int SSE16x8_MSA(const uint8_t* a, const uint8_t* b) { return sum; } -static int SSE8x8_MSA(const uint8_t* a, const uint8_t* b) { +static int SSE8x8_MSA(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { uint32_t sum; v16u8 src0, src1, src2, src3, src4, src5, src6, src7; v16u8 ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7; @@ -778,7 +810,8 @@ static int SSE8x8_MSA(const uint8_t* a, const uint8_t* b) { return sum; } -static int SSE4x4_MSA(const uint8_t* a, const uint8_t* b) { +static int SSE4x4_MSA(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { uint32_t sum = 0; uint32_t src0, src1, src2, src3, ref0, ref1, ref2, ref3; v16u8 src = { 0 }, ref = { 0 }, tmp0, tmp1; @@ -801,7 +834,7 @@ static int SSE4x4_MSA(const uint8_t* a, const uint8_t* b) { // Quantization static int QuantizeBlock_MSA(int16_t in[16], int16_t out[16], - const VP8Matrix* const mtx) { + const VP8Matrix* WEBP_RESTRICT const mtx) { int sum; v8i16 in0, in1, sh0, sh1, out0, out1; v8i16 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, sign0, sign1; @@ -812,7 +845,7 @@ static int QuantizeBlock_MSA(int16_t in[16], int16_t out[16], const v8i16 maxlevel = __msa_fill_h(MAX_LEVEL); LD_SH2(&in[0], 8, in0, in1); - LD_SH2(&mtx->sharpen_[0], 8, sh0, sh1); + 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); @@ -820,10 +853,10 @@ static int QuantizeBlock_MSA(int16_t in[16], int16_t out[16], 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 + 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 + 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); @@ -835,7 +868,7 @@ static int QuantizeBlock_MSA(int16_t in[16], int16_t out[16], 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 + LD_SW4(&mtx->zthresh[0], 4, t0, t1, t2, t3); // zthresh t0 = (s0 > t0); t1 = (s1 > t1); t2 = (s2 > t2); @@ -843,7 +876,7 @@ static int QuantizeBlock_MSA(int16_t in[16], int16_t out[16], 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); + 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); @@ -854,7 +887,7 @@ static int QuantizeBlock_MSA(int16_t in[16], int16_t out[16], } static int Quantize2Blocks_MSA(int16_t in[32], int16_t out[32], - const VP8Matrix* const mtx) { + const VP8Matrix* WEBP_RESTRICT const mtx) { int nz; nz = VP8EncQuantizeBlock(in + 0 * 16, out + 0 * 16, mtx) << 0; nz |= VP8EncQuantizeBlock(in + 1 * 16, out + 1 * 16, mtx) << 1; diff --git a/3rdparty/libwebp/src/dsp/enc_neon.c b/3rdparty/libwebp/src/dsp/enc_neon.c index 6f641c9a76..128454d340 100644 --- a/3rdparty/libwebp/src/dsp/enc_neon.c +++ b/3rdparty/libwebp/src/dsp/enc_neon.c @@ -60,8 +60,8 @@ static WEBP_INLINE void SaturateAndStore4x4_NEON(uint8_t* const dst, static WEBP_INLINE void Add4x4_NEON(const int16x8_t row01, const int16x8_t row23, - const uint8_t* const ref, - uint8_t* const dst) { + const uint8_t* WEBP_RESTRICT const ref, + uint8_t* WEBP_RESTRICT const dst) { uint32x2_t dst01 = vdup_n_u32(0); uint32x2_t dst23 = vdup_n_u32(0); @@ -120,8 +120,9 @@ static WEBP_INLINE void TransformPass_NEON(int16x8x2_t* const rows) { Transpose8x2_NEON(E0, E1, rows); } -static void ITransformOne_NEON(const uint8_t* ref, - const int16_t* in, uint8_t* dst) { +static void ITransformOne_NEON(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { int16x8x2_t rows; INIT_VECTOR2(rows, vld1q_s16(in + 0), vld1q_s16(in + 8)); TransformPass_NEON(&rows); @@ -131,8 +132,9 @@ static void ITransformOne_NEON(const uint8_t* ref, #else -static void ITransformOne_NEON(const uint8_t* ref, - const int16_t* in, uint8_t* dst) { +static void ITransformOne_NEON(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { const int kBPS = BPS; const int16_t kC1C2[] = { kC1, kC2, 0, 0 }; @@ -247,8 +249,9 @@ static void ITransformOne_NEON(const uint8_t* ref, #endif // WEBP_USE_INTRINSICS -static void ITransform_NEON(const uint8_t* ref, - const int16_t* in, uint8_t* dst, int do_two) { +static void ITransform_NEON(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst, int do_two) { ITransformOne_NEON(ref, in, dst); if (do_two) { ITransformOne_NEON(ref + 4, in + 16, dst + 4); @@ -294,8 +297,9 @@ static WEBP_INLINE int16x8_t DiffU8ToS16_NEON(const uint8x8_t a, return vreinterpretq_s16_u16(vsubl_u8(a, b)); } -static void FTransform_NEON(const uint8_t* src, const uint8_t* ref, - int16_t* out) { +static void FTransform_NEON(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT out) { int16x8_t d0d1, d3d2; // working 4x4 int16 variables { const uint8x16_t S0 = Load4x4_NEON(src); @@ -364,8 +368,9 @@ static const int32_t kCoeff32[] = { 51000, 51000, 51000, 51000 }; -static void FTransform_NEON(const uint8_t* src, const uint8_t* ref, - int16_t* out) { +static void FTransform_NEON(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT out) { const int kBPS = BPS; const uint8_t* src_ptr = src; const uint8_t* ref_ptr = ref; @@ -484,7 +489,8 @@ static void FTransform_NEON(const uint8_t* src, const uint8_t* ref, src += stride; \ } while (0) -static void FTransformWHT_NEON(const int16_t* src, int16_t* out) { +static void FTransformWHT_NEON(const int16_t* WEBP_RESTRICT src, + int16_t* WEBP_RESTRICT out) { const int stride = 16; const int16x4_t zero = vdup_n_s16(0); int32x4x4_t tmp0; @@ -659,8 +665,9 @@ static WEBP_INLINE int32x2_t DistoSum_NEON(const int16x8x4_t q4_in, // 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) { +static int Disto4x4_NEON(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT 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); @@ -701,8 +708,9 @@ static int Disto4x4_NEON(const uint8_t* const a, const uint8_t* const b, } #undef LOAD_LANE_32b -static int Disto16x16_NEON(const uint8_t* const a, const uint8_t* const b, - const uint16_t* const w) { +static int Disto16x16_NEON(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT const w) { int D = 0; int x, y; for (y = 0; y < 16 * BPS; y += 4 * BPS) { @@ -715,9 +723,10 @@ static int Disto16x16_NEON(const uint8_t* const a, const uint8_t* const b, //------------------------------------------------------------------------------ -static void CollectHistogram_NEON(const uint8_t* ref, const uint8_t* pred, +static void CollectHistogram_NEON(const uint8_t* WEBP_RESTRICT ref, + const uint8_t* WEBP_RESTRICT pred, int start_block, int end_block, - VP8Histogram* const histo) { + VP8Histogram* WEBP_RESTRICT const histo) { const uint16x8_t max_coeff_thresh = vdupq_n_u16(MAX_COEFF_THRESH); int j; int distribution[MAX_COEFF_THRESH + 1] = { 0 }; @@ -747,9 +756,9 @@ static void CollectHistogram_NEON(const uint8_t* ref, const uint8_t* pred, //------------------------------------------------------------------------------ -static WEBP_INLINE void AccumulateSSE16_NEON(const uint8_t* const a, - const uint8_t* const b, - uint32x4_t* const sum) { +static WEBP_INLINE void AccumulateSSE16_NEON( + const uint8_t* WEBP_RESTRICT const a, const uint8_t* WEBP_RESTRICT 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); @@ -775,7 +784,8 @@ static int SumToInt_NEON(uint32x4_t sum) { #endif } -static int SSE16x16_NEON(const uint8_t* a, const uint8_t* b) { +static int SSE16x16_NEON(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { uint32x4_t sum = vdupq_n_u32(0); int y; for (y = 0; y < 16; ++y) { @@ -784,7 +794,8 @@ static int SSE16x16_NEON(const uint8_t* a, const uint8_t* b) { return SumToInt_NEON(sum); } -static int SSE16x8_NEON(const uint8_t* a, const uint8_t* b) { +static int SSE16x8_NEON(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { uint32x4_t sum = vdupq_n_u32(0); int y; for (y = 0; y < 8; ++y) { @@ -793,7 +804,8 @@ static int SSE16x8_NEON(const uint8_t* a, const uint8_t* b) { return SumToInt_NEON(sum); } -static int SSE8x8_NEON(const uint8_t* a, const uint8_t* b) { +static int SSE8x8_NEON(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { uint32x4_t sum = vdupq_n_u32(0); int y; for (y = 0; y < 8; ++y) { @@ -806,7 +818,8 @@ static int SSE8x8_NEON(const uint8_t* a, const uint8_t* b) { return SumToInt_NEON(sum); } -static int SSE4x4_NEON(const uint8_t* a, const uint8_t* b) { +static int SSE4x4_NEON(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { const uint8x16_t a0 = Load4x4_NEON(a); const uint8x16_t b0 = Load4x4_NEON(b); const uint8x16_t abs_diff = vabdq_u8(a0, b0); @@ -825,13 +838,14 @@ static int SSE4x4_NEON(const uint8_t* a, const uint8_t* b) { // 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]); +static int16x8_t Quantize_NEON(int16_t* WEBP_RESTRICT const in, + const VP8Matrix* WEBP_RESTRICT 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) @@ -860,7 +874,7 @@ static const uint8_t kShuffles[4][8] = { }; static int QuantizeBlock_NEON(int16_t in[16], int16_t out[16], - const VP8Matrix* const mtx) { + const VP8Matrix* WEBP_RESTRICT const mtx) { const int16x8_t out0 = Quantize_NEON(in, mtx, 0); const int16x8_t out1 = Quantize_NEON(in, mtx, 8); uint8x8x4_t shuffles; @@ -902,7 +916,7 @@ static int QuantizeBlock_NEON(int16_t in[16], int16_t out[16], } static int Quantize2Blocks_NEON(int16_t in[32], int16_t out[32], - const VP8Matrix* const mtx) { + const VP8Matrix* WEBP_RESTRICT 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; @@ -911,6 +925,293 @@ static int Quantize2Blocks_NEON(int16_t in[32], int16_t out[32], #endif // !WORK_AROUND_GCC +#if WEBP_AARCH64 + +#if BPS == 32 +#define DC4_VE4_HE4_TM4_NEON(dst, tbl, res, lane) \ + do { \ + uint8x16_t r; \ + r = vqtbl2q_u8(qcombined, tbl); \ + r = vreinterpretq_u8_u32( \ + vsetq_lane_u32(vget_lane_u32(vreinterpret_u32_u8(res), lane), \ + vreinterpretq_u32_u8(r), 1)); \ + vst1q_u8(dst, r); \ + } while (0) + +#define RD4_VR4_LD4_VL4_NEON(dst, tbl) \ + do { \ + uint8x16_t r; \ + r = vqtbl2q_u8(qcombined, tbl); \ + vst1q_u8(dst, r); \ + } while (0) + +static WEBP_INLINE uint8x8x2_t Vld1U8x2(const uint8_t* ptr) { +#if LOCAL_CLANG_PREREQ(3, 4) || LOCAL_GCC_PREREQ(8, 5) || defined(_MSC_VER) + return vld1_u8_x2(ptr); +#else + uint8x8x2_t res; + INIT_VECTOR2(res, vld1_u8(ptr + 0 * 8), vld1_u8(ptr + 1 * 8)); + return res; +#endif +} + +static WEBP_INLINE uint8x16x4_t Vld1qU8x4(const uint8_t* ptr) { +#if LOCAL_CLANG_PREREQ(3, 4) || LOCAL_GCC_PREREQ(9, 4) || defined(_MSC_VER) + return vld1q_u8_x4(ptr); +#else + uint8x16x4_t res; + INIT_VECTOR4(res, + vld1q_u8(ptr + 0 * 16), vld1q_u8(ptr + 1 * 16), + vld1q_u8(ptr + 2 * 16), vld1q_u8(ptr + 3 * 16)); + return res; +#endif +} + +static void Intra4Preds_NEON(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + // L K J I X A B C D E F G H + // -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 + static const uint8_t kLookupTbl1[64] = { + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 12, 12, + 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, + 4, 20, 21, 22, 3, 18, 2, 17, 3, 19, 4, 20, 2, 17, 1, 16, + 2, 18, 3, 19, 1, 16, 31, 31, 1, 17, 2, 18, 31, 31, 31, 31 + }; + + static const uint8_t kLookupTbl2[64] = { + 20, 21, 22, 23, 5, 6, 7, 8, 22, 23, 24, 25, 6, 7, 8, 9, + 19, 20, 21, 22, 20, 21, 22, 23, 23, 24, 25, 26, 22, 23, 24, 25, + 18, 19, 20, 21, 19, 5, 6, 7, 24, 25, 26, 27, 7, 8, 9, 26, + 17, 18, 19, 20, 18, 20, 21, 22, 25, 26, 27, 28, 23, 24, 25, 27 + }; + + static const uint8_t kLookupTbl3[64] = { + 30, 30, 30, 30, 0, 0, 0, 0, 21, 22, 23, 24, 19, 19, 19, 19, + 30, 30, 30, 30, 0, 0, 0, 0, 21, 22, 23, 24, 18, 18, 18, 18, + 30, 30, 30, 30, 0, 0, 0, 0, 21, 22, 23, 24, 17, 17, 17, 17, + 30, 30, 30, 30, 0, 0, 0, 0, 21, 22, 23, 24, 16, 16, 16, 16 + }; + + const uint8x16x4_t lookup_avgs1 = Vld1qU8x4(kLookupTbl1); + const uint8x16x4_t lookup_avgs2 = Vld1qU8x4(kLookupTbl2); + const uint8x16x4_t lookup_avgs3 = Vld1qU8x4(kLookupTbl3); + + const uint8x16_t preload = vld1q_u8(top - 5); + uint8x16x2_t qcombined; + uint8x16_t result0, result1; + + uint8x16_t a = vqtbl1q_u8(preload, lookup_avgs1.val[0]); + uint8x16_t b = preload; + uint8x16_t c = vextq_u8(a, a, 2); + + uint8x16_t avg3_all = vrhaddq_u8(vhaddq_u8(a, c), b); + uint8x16_t avg2_all = vrhaddq_u8(a, b); + + uint8x8_t preload_x8, sub_a, sub_c; + uint8_t result_u8; + uint8x8_t res_lo, res_hi; + uint8x16_t full_b; + uint16x8_t sub, sum_lo, sum_hi; + + preload_x8 = vget_low_u8(c); + preload_x8 = vset_lane_u8(vgetq_lane_u8(preload, 0), preload_x8, 3); + + result_u8 = (vaddlv_u8(preload_x8) + 4) >> 3; + + avg3_all = vsetq_lane_u8(vgetq_lane_u8(preload, 0), avg3_all, 15); + avg3_all = vsetq_lane_u8(result_u8, avg3_all, 14); + + qcombined.val[0] = avg2_all; + qcombined.val[1] = avg3_all; + + sub_a = vdup_laneq_u8(preload, 4); + + // preload = {a,b,c,d,...} => full_b = {d,d,d,d,c,c,c,c,b,b,b,b,a,a,a,a} + full_b = vqtbl1q_u8(preload, lookup_avgs1.val[1]); + // preload = {a,b,c,d,...} => sub_c = {a,b,c,d,a,b,c,d,a,b,c,d,a,b,c,d} + sub_c = vreinterpret_u8_u32(vdup_n_u32( + vgetq_lane_u32(vreinterpretq_u32_u8(vextq_u8(preload, preload, 5)), 0))); + + sub = vsubl_u8(sub_c, sub_a); + sum_lo = vaddw_u8(sub, vget_low_u8(full_b)); + res_lo = vqmovun_s16(vreinterpretq_s16_u16(sum_lo)); + + sum_hi = vaddw_u8(sub, vget_high_u8(full_b)); + res_hi = vqmovun_s16(vreinterpretq_s16_u16(sum_hi)); + + // DC4, VE4, HE4, TM4 + DC4_VE4_HE4_TM4_NEON(dst + I4DC4 + BPS * 0, lookup_avgs3.val[0], res_lo, 0); + DC4_VE4_HE4_TM4_NEON(dst + I4DC4 + BPS * 1, lookup_avgs3.val[1], res_lo, 1); + DC4_VE4_HE4_TM4_NEON(dst + I4DC4 + BPS * 2, lookup_avgs3.val[2], res_hi, 0); + DC4_VE4_HE4_TM4_NEON(dst + I4DC4 + BPS * 3, lookup_avgs3.val[3], res_hi, 1); + + // RD4, VR4, LD4, VL4 + RD4_VR4_LD4_VL4_NEON(dst + I4RD4 + BPS * 0, lookup_avgs2.val[0]); + RD4_VR4_LD4_VL4_NEON(dst + I4RD4 + BPS * 1, lookup_avgs2.val[1]); + RD4_VR4_LD4_VL4_NEON(dst + I4RD4 + BPS * 2, lookup_avgs2.val[2]); + RD4_VR4_LD4_VL4_NEON(dst + I4RD4 + BPS * 3, lookup_avgs2.val[3]); + + // HD4, HU4 + result0 = vqtbl2q_u8(qcombined, lookup_avgs1.val[2]); + result1 = vqtbl2q_u8(qcombined, lookup_avgs1.val[3]); + + vst1_u8(dst + I4HD4 + BPS * 0, vget_low_u8(result0)); + vst1_u8(dst + I4HD4 + BPS * 1, vget_high_u8(result0)); + vst1_u8(dst + I4HD4 + BPS * 2, vget_low_u8(result1)); + vst1_u8(dst + I4HD4 + BPS * 3, vget_high_u8(result1)); +} +#endif // BPS == 32 + +static WEBP_INLINE void Fill_NEON(uint8_t* dst, const uint8_t value) { + uint8x16_t a = vdupq_n_u8(value); + int i; + for (i = 0; i < 16; i++) { + vst1q_u8(dst + BPS * i, a); + } +} + +static WEBP_INLINE void Fill16_NEON(uint8_t* dst, const uint8_t* src) { + uint8x16_t a = vld1q_u8(src); + int i; + for (i = 0; i < 16; i++) { + vst1q_u8(dst + BPS * i, a); + } +} + +static WEBP_INLINE void HorizontalPred16_NEON(uint8_t* dst, + const uint8_t* left) { + uint8x16_t a; + + if (left == NULL) { + Fill_NEON(dst, 129); + return; + } + + a = vld1q_u8(left + 0); + vst1q_u8(dst + BPS * 0, vdupq_laneq_u8(a, 0)); + vst1q_u8(dst + BPS * 1, vdupq_laneq_u8(a, 1)); + vst1q_u8(dst + BPS * 2, vdupq_laneq_u8(a, 2)); + vst1q_u8(dst + BPS * 3, vdupq_laneq_u8(a, 3)); + vst1q_u8(dst + BPS * 4, vdupq_laneq_u8(a, 4)); + vst1q_u8(dst + BPS * 5, vdupq_laneq_u8(a, 5)); + vst1q_u8(dst + BPS * 6, vdupq_laneq_u8(a, 6)); + vst1q_u8(dst + BPS * 7, vdupq_laneq_u8(a, 7)); + vst1q_u8(dst + BPS * 8, vdupq_laneq_u8(a, 8)); + vst1q_u8(dst + BPS * 9, vdupq_laneq_u8(a, 9)); + vst1q_u8(dst + BPS * 10, vdupq_laneq_u8(a, 10)); + vst1q_u8(dst + BPS * 11, vdupq_laneq_u8(a, 11)); + vst1q_u8(dst + BPS * 12, vdupq_laneq_u8(a, 12)); + vst1q_u8(dst + BPS * 13, vdupq_laneq_u8(a, 13)); + vst1q_u8(dst + BPS * 14, vdupq_laneq_u8(a, 14)); + vst1q_u8(dst + BPS * 15, vdupq_laneq_u8(a, 15)); +} + +static WEBP_INLINE void VerticalPred16_NEON(uint8_t* dst, const uint8_t* top) { + if (top != NULL) { + Fill16_NEON(dst, top); + } else { + Fill_NEON(dst, 127); + } +} + +static WEBP_INLINE void DCMode_NEON(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + uint8_t s; + + if (top != NULL) { + uint16_t dc; + dc = vaddlvq_u8(vld1q_u8(top)); + if (left != NULL) { + // top and left present. + dc += vaddlvq_u8(vld1q_u8(left)); + s = vqrshrnh_n_u16(dc, 5); + } else { + // top but no left. + s = vqrshrnh_n_u16(dc, 4); + } + } else { + if (left != NULL) { + uint16_t dc; + // left but no top. + dc = vaddlvq_u8(vld1q_u8(left)); + s = vqrshrnh_n_u16(dc, 4); + } else { + // No top, no left, nothing. + s = 0x80; + } + } + Fill_NEON(dst, s); +} + +static WEBP_INLINE void TrueMotionHelper_NEON(uint8_t* dst, + const uint8x8_t outer, + const uint8x8x2_t inner, + const uint16x8_t a, int i, + const int n) { + uint8x8_t d1, d2; + uint16x8_t r1, r2; + + r1 = vaddl_u8(outer, inner.val[0]); + r1 = vqsubq_u16(r1, a); + d1 = vqmovun_s16(vreinterpretq_s16_u16(r1)); + r2 = vaddl_u8(outer, inner.val[1]); + r2 = vqsubq_u16(r2, a); + d2 = vqmovun_s16(vreinterpretq_s16_u16(r2)); + vst1_u8(dst + BPS * (i * 4 + n), d1); + vst1_u8(dst + BPS * (i * 4 + n) + 8, d2); +} + +static WEBP_INLINE void TrueMotion_NEON(uint8_t* dst, const uint8_t* left, + const uint8_t* top) { + int i; + uint16x8_t a; + uint8x8x2_t inner; + + if (left == NULL) { + // 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) { + VerticalPred16_NEON(dst, top); + } else { + Fill_NEON(dst, 129); + } + return; + } + + // left is not NULL. + if (top == NULL) { + HorizontalPred16_NEON(dst, left); + return; + } + + // Neither left nor top are NULL. + a = vdupq_n_u16(left[-1]); + inner = Vld1U8x2(top); + + for (i = 0; i < 4; i++) { + const uint8x8x4_t outer = vld4_dup_u8(&left[i * 4]); + + TrueMotionHelper_NEON(dst, outer.val[0], inner, a, i, 0); + TrueMotionHelper_NEON(dst, outer.val[1], inner, a, i, 1); + TrueMotionHelper_NEON(dst, outer.val[2], inner, a, i, 2); + TrueMotionHelper_NEON(dst, outer.val[3], inner, a, i, 3); + } +} + +static void Intra16Preds_NEON(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { + DCMode_NEON(I16DC16 + dst, left, top); + VerticalPred16_NEON(I16VE16 + dst, top); + HorizontalPred16_NEON(I16HE16 + dst, left); + TrueMotion_NEON(I16TM16 + dst, left, top); +} + +#endif // WEBP_AARCH64 + //------------------------------------------------------------------------------ // Entry point @@ -931,9 +1232,17 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitNEON(void) { VP8SSE8x8 = SSE8x8_NEON; VP8SSE4x4 = SSE4x4_NEON; +#if WEBP_AARCH64 +#if BPS == 32 + VP8EncPredLuma4 = Intra4Preds_NEON; +#endif + VP8EncPredLuma16 = Intra16Preds_NEON; +#endif + #if !defined(WORK_AROUND_GCC) VP8EncQuantizeBlock = QuantizeBlock_NEON; VP8EncQuantize2Blocks = Quantize2Blocks_NEON; + VP8EncQuantizeBlockWHT = QuantizeBlock_NEON; #endif } diff --git a/3rdparty/libwebp/src/dsp/enc_sse2.c b/3rdparty/libwebp/src/dsp/enc_sse2.c index 010624a2f7..6fc04343e6 100644 --- a/3rdparty/libwebp/src/dsp/enc_sse2.c +++ b/3rdparty/libwebp/src/dsp/enc_sse2.c @@ -14,20 +14,26 @@ #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE2) -#include -#include // for abs() #include +#include +#include // for abs() +#include + #include "src/dsp/common_sse2.h" +#include "src/dsp/cpu.h" #include "src/enc/cost_enc.h" #include "src/enc/vp8i_enc.h" +#include "src/utils/utils.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Transforms (Paragraph 14.4) // Does one inverse transform. -static void ITransform_One_SSE2(const uint8_t* ref, const int16_t* in, - uint8_t* dst) { +static void ITransform_One_SSE2(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { // This implementation makes use of 16-bit fixed point versions of two // multiply constants: // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16 @@ -177,8 +183,9 @@ static void ITransform_One_SSE2(const uint8_t* ref, const int16_t* in, } // Does two inverse transforms. -static void ITransform_Two_SSE2(const uint8_t* ref, const int16_t* in, - uint8_t* dst) { +static void ITransform_Two_SSE2(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst) { // This implementation makes use of 16-bit fixed point versions of two // multiply constants: // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16 @@ -316,7 +323,9 @@ static void ITransform_Two_SSE2(const uint8_t* ref, const int16_t* in, } // Does one or two inverse transforms. -static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst, +static void ITransform_SSE2(const uint8_t* WEBP_RESTRICT ref, + const int16_t* WEBP_RESTRICT in, + uint8_t* WEBP_RESTRICT dst, int do_two) { if (do_two) { ITransform_Two_SSE2(ref, in, dst); @@ -373,7 +382,7 @@ static void FTransformPass1_SSE2(const __m128i* const in01, static void FTransformPass2_SSE2(const __m128i* const v01, const __m128i* const v32, - int16_t* out) { + int16_t* WEBP_RESTRICT 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, @@ -424,8 +433,9 @@ static void FTransformPass2_SSE2(const __m128i* const v01, _mm_storeu_si128((__m128i*)&out[8], d2_f3); } -static void FTransform_SSE2(const uint8_t* src, const uint8_t* ref, - int16_t* out) { +static void FTransform_SSE2(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT out) { const __m128i zero = _mm_setzero_si128(); // Load src. const __m128i src0 = _mm_loadl_epi64((const __m128i*)&src[0 * BPS]); @@ -468,8 +478,9 @@ static void FTransform_SSE2(const uint8_t* src, const uint8_t* ref, FTransformPass2_SSE2(&v01, &v32, out); } -static void FTransform2_SSE2(const uint8_t* src, const uint8_t* ref, - int16_t* out) { +static void FTransform2_SSE2(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT ref, + int16_t* WEBP_RESTRICT out) { const __m128i zero = _mm_setzero_si128(); // Load src and convert to 16b. @@ -517,7 +528,8 @@ static void FTransform2_SSE2(const uint8_t* src, const uint8_t* ref, FTransformPass2_SSE2(&v01h, &v32h, out + 16); } -static void FTransformWHTRow_SSE2(const int16_t* const in, __m128i* const out) { +static void FTransformWHTRow_SSE2(const int16_t* WEBP_RESTRICT 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]); @@ -533,7 +545,8 @@ static void FTransformWHTRow_SSE2(const int16_t* const in, __m128i* const out) { *out = _mm_madd_epi16(D, kMult); } -static void FTransformWHT_SSE2(const int16_t* in, int16_t* out) { +static void FTransformWHT_SSE2(const int16_t* WEBP_RESTRICT in, + int16_t* WEBP_RESTRICT out) { // Input is 12b signed. __m128i row0, row1, row2, row3; // Rows are 14b signed. @@ -566,9 +579,10 @@ static void FTransformWHT_SSE2(const int16_t* in, int16_t* out) { // 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, +static void CollectHistogram_SSE2(const uint8_t* WEBP_RESTRICT ref, + const uint8_t* WEBP_RESTRICT pred, int start_block, int end_block, - VP8Histogram* const histo) { + VP8Histogram* WEBP_RESTRICT const histo) { const __m128i zero = _mm_setzero_si128(); const __m128i max_coeff_thresh = _mm_set1_epi16(MAX_COEFF_THRESH); int j; @@ -640,7 +654,8 @@ static WEBP_INLINE void Fill_SSE2(uint8_t* dst, int value, int size) { } } -static WEBP_INLINE void VE8uv_SSE2(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void VE8uv_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { int j; const __m128i top_values = _mm_loadl_epi64((const __m128i*)top); for (j = 0; j < 8; ++j) { @@ -648,7 +663,8 @@ static WEBP_INLINE void VE8uv_SSE2(uint8_t* dst, const uint8_t* top) { } } -static WEBP_INLINE void VE16_SSE2(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void VE16_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const __m128i top_values = _mm_load_si128((const __m128i*)top); int j; for (j = 0; j < 16; ++j) { @@ -656,8 +672,9 @@ static WEBP_INLINE void VE16_SSE2(uint8_t* dst, const uint8_t* top) { } } -static WEBP_INLINE void VerticalPred_SSE2(uint8_t* dst, - const uint8_t* top, int size) { +static WEBP_INLINE void VerticalPred_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top, + int size) { if (top != NULL) { if (size == 8) { VE8uv_SSE2(dst, top); @@ -669,7 +686,8 @@ static WEBP_INLINE void VerticalPred_SSE2(uint8_t* dst, } } -static WEBP_INLINE void HE8uv_SSE2(uint8_t* dst, const uint8_t* left) { +static WEBP_INLINE void HE8uv_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left) { int j; for (j = 0; j < 8; ++j) { const __m128i values = _mm_set1_epi8((char)left[j]); @@ -678,7 +696,8 @@ static WEBP_INLINE void HE8uv_SSE2(uint8_t* dst, const uint8_t* left) { } } -static WEBP_INLINE void HE16_SSE2(uint8_t* dst, const uint8_t* left) { +static WEBP_INLINE void HE16_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left) { int j; for (j = 0; j < 16; ++j) { const __m128i values = _mm_set1_epi8((char)left[j]); @@ -687,8 +706,9 @@ static WEBP_INLINE void HE16_SSE2(uint8_t* dst, const uint8_t* left) { } } -static WEBP_INLINE void HorizontalPred_SSE2(uint8_t* dst, - const uint8_t* left, int size) { +static WEBP_INLINE void HorizontalPred_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + int size) { if (left != NULL) { if (size == 8) { HE8uv_SSE2(dst, left); @@ -700,8 +720,9 @@ static WEBP_INLINE void HorizontalPred_SSE2(uint8_t* dst, } } -static WEBP_INLINE void TM_SSE2(uint8_t* dst, const uint8_t* left, - const uint8_t* top, int size) { +static WEBP_INLINE void TM_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top, int size) { const __m128i zero = _mm_setzero_si128(); int y; if (size == 8) { @@ -728,8 +749,10 @@ static WEBP_INLINE void TM_SSE2(uint8_t* dst, const uint8_t* left, } } -static WEBP_INLINE void TrueMotion_SSE2(uint8_t* dst, const uint8_t* left, - const uint8_t* top, int size) { +static WEBP_INLINE void TrueMotion_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top, + int size) { if (left != NULL) { if (top != NULL) { TM_SSE2(dst, left, top, size); @@ -749,8 +772,9 @@ static WEBP_INLINE void TrueMotion_SSE2(uint8_t* dst, const uint8_t* left, } } -static WEBP_INLINE void DC8uv_SSE2(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static WEBP_INLINE void DC8uv_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT 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); @@ -758,7 +782,8 @@ static WEBP_INLINE void DC8uv_SSE2(uint8_t* dst, const uint8_t* left, Put8x8uv_SSE2(DC >> 4, dst); } -static WEBP_INLINE void DC8uvNoLeft_SSE2(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void DC8uvNoLeft_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT 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); @@ -766,7 +791,8 @@ static WEBP_INLINE void DC8uvNoLeft_SSE2(uint8_t* dst, const uint8_t* top) { Put8x8uv_SSE2(DC >> 3, dst); } -static WEBP_INLINE void DC8uvNoTop_SSE2(uint8_t* dst, const uint8_t* left) { +static WEBP_INLINE void DC8uvNoTop_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left) { // 'left' is contiguous so we can reuse the top summation. DC8uvNoLeft_SSE2(dst, left); } @@ -775,8 +801,9 @@ 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) { +static WEBP_INLINE void DC8uvMode_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { if (top != NULL) { if (left != NULL) { // top and left present DC8uv_SSE2(dst, left, top); @@ -790,8 +817,9 @@ static WEBP_INLINE void DC8uvMode_SSE2(uint8_t* dst, const uint8_t* left, } } -static WEBP_INLINE void DC16_SSE2(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static WEBP_INLINE void DC16_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { const __m128i top_row = _mm_load_si128((const __m128i*)top); const __m128i left_row = _mm_load_si128((const __m128i*)left); const int DC = @@ -799,13 +827,15 @@ static WEBP_INLINE void DC16_SSE2(uint8_t* dst, const uint8_t* left, Put16_SSE2(DC >> 5, dst); } -static WEBP_INLINE void DC16NoLeft_SSE2(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void DC16NoLeft_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT 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) { +static WEBP_INLINE void DC16NoTop_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left) { // 'left' is contiguous so we can reuse the top summation. DC16NoLeft_SSE2(dst, left); } @@ -814,8 +844,9 @@ 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) { +static WEBP_INLINE void DC16Mode_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { if (top != NULL) { if (left != NULL) { // top and left present DC16_SSE2(dst, left, top); @@ -844,8 +875,9 @@ static WEBP_INLINE void DC16Mode_SSE2(uint8_t* dst, const uint8_t* left, // 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 +// vertical +static WEBP_INLINE void VE4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const __m128i one = _mm_set1_epi8(1); const __m128i ABCDEFGH = _mm_loadl_epi64((__m128i*)(top - 1)); const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1); @@ -861,8 +893,9 @@ static WEBP_INLINE void VE4_SSE2(uint8_t* dst, } } -static WEBP_INLINE void HE4_SSE2(uint8_t* dst, - const uint8_t* top) { // horizontal +// horizontal +static WEBP_INLINE void HE4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const int X = top[-1]; const int I = top[-2]; const int J = top[-3]; @@ -874,15 +907,17 @@ static WEBP_INLINE void HE4_SSE2(uint8_t* dst, WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(K, L, L)); } -static WEBP_INLINE void DC4_SSE2(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void DC4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT 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 +// Down-Left +static WEBP_INLINE void LD4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const __m128i one = _mm_set1_epi8(1); const __m128i ABCDEFGH = _mm_loadl_epi64((const __m128i*)top); const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1); @@ -898,8 +933,9 @@ static WEBP_INLINE void LD4_SSE2(uint8_t* dst, WebPInt32ToMem(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 +// Vertical-Right +static WEBP_INLINE void VR4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const __m128i one = _mm_set1_epi8(1); const int I = top[-2]; const int J = top[-3]; @@ -924,8 +960,9 @@ static WEBP_INLINE void VR4_SSE2(uint8_t* dst, DST(0, 3) = AVG3(K, J, I); } -static WEBP_INLINE void VL4_SSE2(uint8_t* dst, - const uint8_t* top) { // Vertical-Left +// Vertical-Left +static WEBP_INLINE void VL4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const __m128i one = _mm_set1_epi8(1); const __m128i ABCDEFGH = _mm_loadl_epi64((const __m128i*)top); const __m128i BCDEFGH_ = _mm_srli_si128(ABCDEFGH, 1); @@ -951,8 +988,9 @@ static WEBP_INLINE void VL4_SSE2(uint8_t* dst, DST(3, 3) = (extra_out >> 8) & 0xff; } -static WEBP_INLINE void RD4_SSE2(uint8_t* dst, - const uint8_t* top) { // Down-right +// Down-right +static WEBP_INLINE void RD4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { 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); @@ -968,7 +1006,8 @@ static WEBP_INLINE void RD4_SSE2(uint8_t* dst, WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3))); } -static WEBP_INLINE void HU4_SSE2(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void HU4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const int I = top[-2]; const int J = top[-3]; const int K = top[-4]; @@ -983,7 +1022,8 @@ static WEBP_INLINE void HU4_SSE2(uint8_t* dst, const uint8_t* top) { 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) { +static WEBP_INLINE void HD4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const int X = top[-1]; const int I = top[-2]; const int J = top[-3]; @@ -1006,7 +1046,8 @@ static WEBP_INLINE void HD4_SSE2(uint8_t* dst, const uint8_t* top) { DST(1, 3) = AVG3(L, K, J); } -static WEBP_INLINE void TM4_SSE2(uint8_t* dst, const uint8_t* top) { +static WEBP_INLINE void TM4_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { const __m128i zero = _mm_setzero_si128(); const __m128i top_values = _mm_cvtsi32_si128(WebPMemToInt32(top)); const __m128i top_base = _mm_unpacklo_epi8(top_values, zero); @@ -1028,7 +1069,8 @@ static WEBP_INLINE void TM4_SSE2(uint8_t* dst, const uint8_t* 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_SSE2(uint8_t* dst, const uint8_t* top) { +static void Intra4Preds_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT top) { DC4_SSE2(I4DC4 + dst, top); TM4_SSE2(I4TM4 + dst, top); VE4_SSE2(I4VE4 + dst, top); @@ -1044,8 +1086,9 @@ static void Intra4Preds_SSE2(uint8_t* dst, const uint8_t* top) { //------------------------------------------------------------------------------ // Chroma 8x8 prediction (paragraph 12.2) -static void IntraChromaPreds_SSE2(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { +static void IntraChromaPreds_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { // U block DC8uvMode_SSE2(C8DC8 + dst, left, top); VerticalPred_SSE2(C8VE8 + dst, top, 8); @@ -1064,8 +1107,9 @@ static void IntraChromaPreds_SSE2(uint8_t* dst, const uint8_t* left, //------------------------------------------------------------------------------ // luma 16x16 prediction (paragraph 12.3) -static void Intra16Preds_SSE2(uint8_t* dst, - const uint8_t* left, const uint8_t* top) { +static void Intra16Preds_SSE2(uint8_t* WEBP_RESTRICT dst, + const uint8_t* WEBP_RESTRICT left, + const uint8_t* WEBP_RESTRICT top) { DC16Mode_SSE2(I16DC16 + dst, left, top); VerticalPred_SSE2(I16VE16 + dst, top, 16); HorizontalPred_SSE2(I16HE16 + dst, left, 16); @@ -1092,7 +1136,8 @@ static WEBP_INLINE void SubtractAndAccumulate_SSE2(const __m128i a, *sum = _mm_add_epi32(sum1, sum2); } -static WEBP_INLINE int SSE_16xN_SSE2(const uint8_t* a, const uint8_t* b, +static WEBP_INLINE int SSE_16xN_SSE2(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b, int num_pairs) { __m128i sum = _mm_setzero_si128(); int32_t tmp[4]; @@ -1114,18 +1159,21 @@ static WEBP_INLINE int SSE_16xN_SSE2(const uint8_t* a, const uint8_t* b, return (tmp[3] + tmp[2] + tmp[1] + tmp[0]); } -static int SSE16x16_SSE2(const uint8_t* a, const uint8_t* b) { +static int SSE16x16_SSE2(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { return SSE_16xN_SSE2(a, b, 8); } -static int SSE16x8_SSE2(const uint8_t* a, const uint8_t* b) { +static int SSE16x8_SSE2(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT 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) { +static int SSE8x8_SSE2(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { const __m128i zero = _mm_setzero_si128(); int num_pairs = 4; __m128i sum = zero; @@ -1152,7 +1200,8 @@ static int SSE8x8_SSE2(const uint8_t* a, const uint8_t* b) { } #undef LOAD_8x16b -static int SSE4x4_SSE2(const uint8_t* a, const uint8_t* b) { +static int SSE4x4_SSE2(const uint8_t* WEBP_RESTRICT a, + const uint8_t* WEBP_RESTRICT b) { const __m128i zero = _mm_setzero_si128(); // Load values. Note that we read 8 pixels instead of 4, @@ -1189,7 +1238,7 @@ static int SSE4x4_SSE2(const uint8_t* a, const uint8_t* b) { //------------------------------------------------------------------------------ -static void Mean16x4_SSE2(const uint8_t* ref, uint32_t dc[4]) { +static void Mean16x4_SSE2(const uint8_t* WEBP_RESTRICT 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]); @@ -1227,8 +1276,9 @@ static void Mean16x4_SSE2(const uint8_t* ref, uint32_t dc[4]) { // 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) { +static int TTransform_SSE2(const uint8_t* WEBP_RESTRICT inA, + const uint8_t* WEBP_RESTRICT inB, + const uint16_t* WEBP_RESTRICT const w) { int32_t sum[4]; __m128i tmp_0, tmp_1, tmp_2, tmp_3; const __m128i zero = _mm_setzero_si128(); @@ -1328,14 +1378,16 @@ static int TTransform_SSE2(const uint8_t* inA, const uint8_t* inB, 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) { +static int Disto4x4_SSE2(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT 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) { +static int Disto16x16_SSE2(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT const w) { int D = 0; int x, y; for (y = 0; y < 16 * BPS; y += 4 * BPS) { @@ -1350,9 +1402,10 @@ static int Disto16x16_SSE2(const uint8_t* const a, const uint8_t* const b, // Quantization // -static WEBP_INLINE int DoQuantizeBlock_SSE2(int16_t in[16], int16_t out[16], - const uint16_t* const sharpen, - const VP8Matrix* const mtx) { +static WEBP_INLINE int DoQuantizeBlock_SSE2( + int16_t in[16], int16_t out[16], + const uint16_t* WEBP_RESTRICT const sharpen, + const VP8Matrix* WEBP_RESTRICT const mtx) { const __m128i max_coeff_2047 = _mm_set1_epi16(MAX_LEVEL); const __m128i zero = _mm_setzero_si128(); __m128i coeff0, coeff8; @@ -1362,10 +1415,10 @@ static WEBP_INLINE int DoQuantizeBlock_SSE2(int16_t in[16], int16_t out[16], // 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]); + 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); @@ -1398,10 +1451,10 @@ static WEBP_INLINE int DoQuantizeBlock_SSE2(int16_t in[16], int16_t out[16], __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]); + 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); @@ -1463,19 +1516,19 @@ static WEBP_INLINE int DoQuantizeBlock_SSE2(int16_t in[16], int16_t out[16], } 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); + const VP8Matrix* WEBP_RESTRICT 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) { + const VP8Matrix* WEBP_RESTRICT 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) { + const VP8Matrix* WEBP_RESTRICT const mtx) { int nz; - const uint16_t* const sharpen = &mtx->sharpen_[0]; + 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; diff --git a/3rdparty/libwebp/src/dsp/enc_sse41.c b/3rdparty/libwebp/src/dsp/enc_sse41.c index 924035a644..4d73e32150 100644 --- a/3rdparty/libwebp/src/dsp/enc_sse41.c +++ b/3rdparty/libwebp/src/dsp/enc_sse41.c @@ -14,18 +14,23 @@ #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE41) +#include #include + #include // for abs() #include "src/dsp/common_sse2.h" +#include "src/dsp/cpu.h" #include "src/enc/vp8i_enc.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Compute susceptibility based on DCT-coeff histograms. -static void CollectHistogram_SSE41(const uint8_t* ref, const uint8_t* pred, +static void CollectHistogram_SSE41(const uint8_t* WEBP_RESTRICT ref, + const uint8_t* WEBP_RESTRICT pred, int start_block, int end_block, - VP8Histogram* const histo) { + VP8Histogram* WEBP_RESTRICT const histo) { const __m128i max_coeff_thresh = _mm_set1_epi16(MAX_COEFF_THRESH); int j; int distribution[MAX_COEFF_THRESH + 1] = { 0 }; @@ -168,14 +173,16 @@ static int TTransform_SSE41(const uint8_t* inA, const uint8_t* inB, 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) { +static int Disto4x4_SSE41(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT 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) { +static int Disto16x16_SSE41(const uint8_t* WEBP_RESTRICT const a, + const uint8_t* WEBP_RESTRICT const b, + const uint16_t* WEBP_RESTRICT const w) { int D = 0; int x, y; for (y = 0; y < 16 * BPS; y += 4 * BPS) { @@ -208,10 +215,10 @@ static WEBP_INLINE int DoQuantizeBlock_SSE41(int16_t in[16], int16_t out[16], // 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]); + 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); @@ -238,10 +245,10 @@ static WEBP_INLINE int DoQuantizeBlock_SSE41(int16_t in[16], int16_t out[16], __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]); + 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); @@ -301,19 +308,19 @@ static WEBP_INLINE int DoQuantizeBlock_SSE41(int16_t in[16], int16_t out[16], #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); + const VP8Matrix* WEBP_RESTRICT 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) { + const VP8Matrix* WEBP_RESTRICT 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) { + const VP8Matrix* WEBP_RESTRICT const mtx) { int nz; - const uint16_t* const sharpen = &mtx->sharpen_[0]; + 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; diff --git a/3rdparty/libwebp/src/dsp/filters.c b/3rdparty/libwebp/src/dsp/filters.c index c9232ff16a..38da5252df 100644 --- a/3rdparty/libwebp/src/dsp/filters.c +++ b/3rdparty/libwebp/src/dsp/filters.c @@ -11,11 +11,14 @@ // // Author: Urvang (urvang@google.com) -#include "src/dsp/dsp.h" #include #include #include +#include "src/dsp/cpu.h" +#include "src/dsp/dsp.h" +#include "src/webp/types.h" + //------------------------------------------------------------------------------ // Helpful macro. @@ -23,55 +26,42 @@ do { \ assert((in) != NULL); \ assert((out) != NULL); \ + assert((in) != (out)); \ assert(width > 0); \ assert(height > 0); \ assert(stride >= width); \ - assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ - (void)height; /* Silence unused warning. */ \ } while (0) #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) { +static WEBP_INLINE void PredictLine_C(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT pred, + uint8_t* WEBP_RESTRICT dst, int length) { 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]); - } + 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, +static WEBP_INLINE void DoHorizontalFilter_C(const uint8_t* WEBP_RESTRICT 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; + uint8_t* WEBP_RESTRICT out) { + const uint8_t* preds = in; + int row; DCHECK(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; - } + // Leftmost pixel is the same as input for topmost scanline. + out[0] = in[0]; + PredictLine_C(in + 1, preds, out + 1, width - 1); + preds += stride; + in += stride; + out += stride; // Filter line-by-line. - while (row < last_row) { + for (row = 1; row < height; ++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; + PredictLine_C(in, preds - stride, out, 1); + PredictLine_C(in + 1, preds, out + 1, width - 1); preds += stride; in += stride; out += stride; @@ -81,35 +71,23 @@ static WEBP_INLINE void DoHorizontalFilter_C(const uint8_t* in, //------------------------------------------------------------------------------ // Vertical filter. -static WEBP_INLINE void DoVerticalFilter_C(const uint8_t* in, +static WEBP_INLINE void DoVerticalFilter_C(const uint8_t* WEBP_RESTRICT 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; + uint8_t* WEBP_RESTRICT out) { + const uint8_t* preds = in; + int row; DCHECK(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; - } + // 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); + in += stride; + out += stride; // Filter line-by-line. - while (row < last_row) { - PredictLine_C(in, preds, out, width, inverse); - ++row; + for (row = 1; row < height; ++row) { + PredictLine_C(in, preds, out, width); preds += stride; in += stride; out += stride; @@ -126,40 +104,31 @@ static WEBP_INLINE int GradientPredictor_C(uint8_t a, uint8_t b, uint8_t c) { } #if !WEBP_NEON_OMIT_C_CODE -static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in, +static WEBP_INLINE void DoGradientFilter_C(const uint8_t* WEBP_RESTRICT 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; + uint8_t* WEBP_RESTRICT out) { + const uint8_t* preds = in; + int row; DCHECK(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; - } + out[0] = in[0]; + PredictLine_C(in + 1, preds, out + 1, width - 1); + preds += stride; + in += stride; + out += stride; // Filter line-by-line. - while (row < last_row) { + for (row = 1; row < height; ++row) { int w; // leftmost pixel: predict from above. - PredictLine_C(in, preds - stride, out, 1, inverse); + PredictLine_C(in, preds - stride, out, 1); 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)); + out[w] = (uint8_t)(in[w] - pred); } - ++row; preds += stride; in += stride; out += stride; @@ -172,20 +141,22 @@ static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in, //------------------------------------------------------------------------------ #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 HorizontalFilter_C(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoHorizontalFilter_C(data, width, height, stride, 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 VerticalFilter_C(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoVerticalFilter_C(data, width, height, stride, 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); +static void GradientFilter_C(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoGradientFilter_C(data, width, height, stride, filtered_data); } #endif // !WEBP_NEON_OMIT_C_CODE diff --git a/3rdparty/libwebp/src/dsp/filters_mips_dsp_r2.c b/3rdparty/libwebp/src/dsp/filters_mips_dsp_r2.c index eca866f578..c62bb872dc 100644 --- a/3rdparty/libwebp/src/dsp/filters_mips_dsp_r2.c +++ b/3rdparty/libwebp/src/dsp/filters_mips_dsp_r2.c @@ -26,13 +26,12 @@ #define DCHECK(in, out) \ do { \ - assert(in != NULL); \ - assert(out != NULL); \ + assert((in) != NULL); \ + assert((out) != NULL); \ + assert((in) != (out)); \ assert(width > 0); \ assert(height > 0); \ assert(stride >= width); \ - assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ - (void)height; /* Silence unused warning. */ \ } while (0) #define DO_PREDICT_LINE(SRC, DST, LENGTH, INVERSE) do { \ @@ -103,7 +102,8 @@ ); \ } while (0) -static WEBP_INLINE void PredictLine_MIPSdspR2(const uint8_t* src, uint8_t* dst, +static WEBP_INLINE void PredictLine_MIPSdspR2(const uint8_t* WEBP_RESTRICT src, + uint8_t* WEBP_RESTRICT dst, int length) { DO_PREDICT_LINE(src, dst, length, 0); } @@ -184,99 +184,75 @@ static WEBP_INLINE void PredictLine_MIPSdspR2(const uint8_t* src, uint8_t* dst, // Horizontal filter. #define FILTER_LINE_BY_LINE do { \ - while (row < last_row) { \ + for (row = 1; row < height; ++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; +static WEBP_INLINE void DoHorizontalFilter_MIPSdspR2( + const uint8_t* WEBP_RESTRICT in, int width, int height, int stride, + uint8_t* WEBP_RESTRICT out) { + const uint8_t* preds = in; + int row; DCHECK(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; - } + // Leftmost pixel is the same as input for topmost scanline. + out[0] = in[0]; + PredictLine_MIPSdspR2(in + 1, out + 1, width - 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); +static void HorizontalFilter_MIPSdspR2(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoHorizontalFilter_MIPSdspR2(data, width, height, stride, filtered_data); } //------------------------------------------------------------------------------ // Vertical filter. #define FILTER_LINE_BY_LINE do { \ - while (row < last_row) { \ + for (row = 1; row < height; ++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; +static WEBP_INLINE void DoVerticalFilter_MIPSdspR2( + const uint8_t* WEBP_RESTRICT in, int width, int height, int stride, + uint8_t* WEBP_RESTRICT out) { + const uint8_t* preds = in; + int row; DCHECK(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; - } + // 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); + in += stride; + out += 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); +static void VerticalFilter_MIPSdspR2(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoVerticalFilter_MIPSdspR2(data, width, height, stride, filtered_data); } //------------------------------------------------------------------------------ @@ -297,7 +273,7 @@ static int GradientPredictor_MIPSdspR2(uint8_t a, uint8_t b, uint8_t c) { } #define FILTER_LINE_BY_LINE(PREDS, OPERATION) do { \ - while (row < last_row) { \ + for (row = 1; row < height; ++row) { \ int w; \ PREDICT_LINE_ONE_PASS(in, PREDS - stride, out); \ for (w = 1; w < width; ++w) { \ @@ -306,42 +282,34 @@ static int GradientPredictor_MIPSdspR2(uint8_t a, uint8_t b, uint8_t c) { 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, +static void DoGradientFilter_MIPSdspR2(const uint8_t* WEBP_RESTRICT 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; + uint8_t* WEBP_RESTRICT out) { + const uint8_t* preds = in; + int row; DCHECK(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; - } + out[0] = in[0]; + PredictLine_MIPSdspR2(in + 1, out + 1, width - 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 GradientFilter_MIPSdspR2(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoGradientFilter_MIPSdspR2(data, width, height, stride, filtered_data); } //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/dsp/filters_msa.c b/3rdparty/libwebp/src/dsp/filters_msa.c index 33a1b20b70..ae3d3699c7 100644 --- a/3rdparty/libwebp/src/dsp/filters_msa.c +++ b/3rdparty/libwebp/src/dsp/filters_msa.c @@ -21,7 +21,8 @@ static WEBP_INLINE void PredictLineInverse0(const uint8_t* src, const uint8_t* pred, - uint8_t* dst, int length) { + uint8_t* WEBP_RESTRICT dst, + int length) { v16u8 src0, pred0, dst0; assert(length >= 0); while (length >= 32) { @@ -58,8 +59,9 @@ static WEBP_INLINE void PredictLineInverse0(const uint8_t* src, #define DCHECK(in, out) \ do { \ - assert(in != NULL); \ - assert(out != NULL); \ + assert((in) != NULL); \ + assert((out) != NULL); \ + assert((in) != (out)); \ assert(width > 0); \ assert(height > 0); \ assert(stride >= width); \ @@ -68,8 +70,9 @@ static WEBP_INLINE void PredictLineInverse0(const uint8_t* src, //------------------------------------------------------------------------------ // Horrizontal filter -static void HorizontalFilter_MSA(const uint8_t* data, int width, int height, - int stride, uint8_t* filtered_data) { +static void HorizontalFilter_MSA(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { const uint8_t* preds = data; const uint8_t* in = data; uint8_t* out = filtered_data; @@ -99,8 +102,8 @@ static void HorizontalFilter_MSA(const uint8_t* data, int width, int height, static WEBP_INLINE void PredictLineGradient(const uint8_t* pinput, const uint8_t* ppred, - uint8_t* poutput, int stride, - int size) { + uint8_t* WEBP_RESTRICT poutput, + int stride, int size) { int w; const v16i8 zero = { 0 }; while (size >= 16) { @@ -131,8 +134,9 @@ static WEBP_INLINE void PredictLineGradient(const uint8_t* pinput, } -static void GradientFilter_MSA(const uint8_t* data, int width, int height, - int stride, uint8_t* filtered_data) { +static void GradientFilter_MSA(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { const uint8_t* in = data; const uint8_t* preds = data; uint8_t* out = filtered_data; @@ -159,8 +163,9 @@ static void GradientFilter_MSA(const uint8_t* data, int width, int height, //------------------------------------------------------------------------------ // Vertical filter -static void VerticalFilter_MSA(const uint8_t* data, int width, int height, - int stride, uint8_t* filtered_data) { +static void VerticalFilter_MSA(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { const uint8_t* in = data; const uint8_t* preds = data; uint8_t* out = filtered_data; diff --git a/3rdparty/libwebp/src/dsp/filters_neon.c b/3rdparty/libwebp/src/dsp/filters_neon.c index b49e515af1..4df1017260 100644 --- a/3rdparty/libwebp/src/dsp/filters_neon.c +++ b/3rdparty/libwebp/src/dsp/filters_neon.c @@ -23,13 +23,12 @@ #define DCHECK(in, out) \ do { \ - assert(in != NULL); \ - assert(out != NULL); \ + assert((in) != NULL); \ + assert((out) != NULL); \ + assert((in) != (out)); \ assert(width > 0); \ assert(height > 0); \ assert(stride >= width); \ - assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ - (void)height; /* Silence unused warning. */ \ } while (0) // load eight u8 and widen to s16 @@ -46,7 +45,7 @@ #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) { + uint8_t* WEBP_RESTRICT dst, int length) { int i; assert(length >= 0); for (i = 0; i + 16 <= length; i += 16) { @@ -59,86 +58,70 @@ static void PredictLine_NEON(const uint8_t* src, const uint8_t* pred, } // 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) { +static void PredictLineLeft_NEON(const uint8_t* WEBP_RESTRICT src, + uint8_t* WEBP_RESTRICT 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; +static WEBP_INLINE void DoHorizontalFilter_NEON( + const uint8_t* WEBP_RESTRICT in, int width, int height, int stride, + uint8_t* WEBP_RESTRICT out) { + int row; DCHECK(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; - } + // Leftmost pixel is the same as input for topmost scanline. + out[0] = in[0]; + PredictLineLeft_NEON(in + 1, out + 1, width - 1); + in += stride; + out += stride; // Filter line-by-line. - while (row < last_row) { + for (row = 1; row < height; ++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); +static void HorizontalFilter_NEON(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoHorizontalFilter_NEON(data, width, height, stride, filtered_data); } //------------------------------------------------------------------------------ // Vertical filter. -static WEBP_INLINE void DoVerticalFilter_NEON(const uint8_t* in, +static WEBP_INLINE void DoVerticalFilter_NEON(const uint8_t* WEBP_RESTRICT 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; + uint8_t* WEBP_RESTRICT out) { + int row; DCHECK(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; - } + // 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); + in += stride; + out += stride; // Filter line-by-line. - while (row < last_row) { + for (row = 1; row < height; ++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); +static void VerticalFilter_NEON(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoVerticalFilter_NEON(data, width, height, stride, filtered_data); } //------------------------------------------------------------------------------ @@ -151,7 +134,8 @@ static WEBP_INLINE int GradientPredictor_C(uint8_t a, uint8_t b, uint8_t c) { static void GradientPredictDirect_NEON(const uint8_t* const row, const uint8_t* const top, - uint8_t* const out, int length) { + uint8_t* WEBP_RESTRICT const out, + int length) { int i; for (i = 0; i + 8 <= length; i += 8) { const uint8x8_t A = vld1_u8(&row[i - 1]); @@ -167,40 +151,31 @@ static void GradientPredictDirect_NEON(const uint8_t* const row, } } -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; +static WEBP_INLINE void DoGradientFilter_NEON(const uint8_t* WEBP_RESTRICT in, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT out) { + int row; DCHECK(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; - } + out[0] = in[0]; + PredictLineLeft_NEON(in + 1, out + 1, width - 1); + in += stride; + out += stride; // Filter line-by-line. - while (row < last_row) { + for (row = 1; row < height; ++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); +static void GradientFilter_NEON(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoGradientFilter_NEON(data, width, height, stride, filtered_data); } #undef DCHECK diff --git a/3rdparty/libwebp/src/dsp/filters_sse2.c b/3rdparty/libwebp/src/dsp/filters_sse2.c index bb4b5d5874..b9a7aefdd9 100644 --- a/3rdparty/libwebp/src/dsp/filters_sse2.c +++ b/3rdparty/libwebp/src/dsp/filters_sse2.c @@ -20,6 +20,9 @@ #include #include +#include "src/dsp/cpu.h" +#include "src/webp/types.h" + //------------------------------------------------------------------------------ // Helpful macro. @@ -27,15 +30,15 @@ do { \ assert((in) != NULL); \ assert((out) != NULL); \ + assert((in) != (out)); \ assert(width > 0); \ assert(height > 0); \ assert(stride >= width); \ - assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ - (void)height; /* Silence unused warning. */ \ } while (0) -static void PredictLineTop_SSE2(const uint8_t* src, const uint8_t* pred, - uint8_t* dst, int length) { +static void PredictLineTop_SSE2(const uint8_t* WEBP_RESTRICT src, + const uint8_t* WEBP_RESTRICT pred, + uint8_t* WEBP_RESTRICT dst, int length) { int i; const int max_pos = length & ~31; assert(length >= 0); @@ -53,7 +56,8 @@ static void PredictLineTop_SSE2(const uint8_t* src, const uint8_t* pred, } // 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) { +static void PredictLineLeft_SSE2(const uint8_t* WEBP_RESTRICT src, + uint8_t* WEBP_RESTRICT dst, int length) { int i; const int max_pos = length & ~31; assert(length >= 0); @@ -73,32 +77,23 @@ static void PredictLineLeft_SSE2(const uint8_t* src, uint8_t* dst, int length) { //------------------------------------------------------------------------------ // 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; +static WEBP_INLINE void DoHorizontalFilter_SSE2( + const uint8_t* WEBP_RESTRICT in, int width, int height, int stride, + uint8_t* WEBP_RESTRICT out) { + int row; DCHECK(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; - } + // Leftmost pixel is the same as input for topmost scanline. + out[0] = in[0]; + PredictLineLeft_SSE2(in + 1, out + 1, width - 1); + in += stride; + out += stride; // Filter line-by-line. - while (row < last_row) { + for (row = 1; row < height; ++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; } @@ -107,30 +102,22 @@ static WEBP_INLINE void DoHorizontalFilter_SSE2(const uint8_t* in, //------------------------------------------------------------------------------ // Vertical filter. -static WEBP_INLINE void DoVerticalFilter_SSE2(const uint8_t* in, +static WEBP_INLINE void DoVerticalFilter_SSE2(const uint8_t* WEBP_RESTRICT 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; + uint8_t* WEBP_RESTRICT out) { + int row; DCHECK(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; - } + // 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); + in += stride; + out += stride; // Filter line-by-line. - while (row < last_row) { + for (row = 1; row < height; ++row) { PredictLineTop_SSE2(in, in - stride, out, width); - ++row; in += stride; out += stride; } @@ -146,7 +133,8 @@ static WEBP_INLINE int GradientPredictor_SSE2(uint8_t a, uint8_t b, uint8_t c) { static void GradientPredictDirect_SSE2(const uint8_t* const row, const uint8_t* const top, - uint8_t* const out, int length) { + uint8_t* WEBP_RESTRICT const out, + int length) { const int max_pos = length & ~7; int i; const __m128i zero = _mm_setzero_si128(); @@ -170,30 +158,22 @@ static void GradientPredictDirect_SSE2(const uint8_t* const row, } } -static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* in, +static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* WEBP_RESTRICT 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; + uint8_t* WEBP_RESTRICT out) { + int row; DCHECK(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; - } + out[0] = in[0]; + PredictLineLeft_SSE2(in + 1, out + 1, width - 1); + in += stride; + out += stride; // Filter line-by-line. - while (row < last_row) { + for (row = 1; row < height; ++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; } @@ -203,20 +183,22 @@ static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* in, //------------------------------------------------------------------------------ -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 HorizontalFilter_SSE2(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoHorizontalFilter_SSE2(data, width, height, stride, 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 VerticalFilter_SSE2(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoVerticalFilter_SSE2(data, width, height, stride, 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); +static void GradientFilter_SSE2(const uint8_t* WEBP_RESTRICT data, + int width, int height, int stride, + uint8_t* WEBP_RESTRICT filtered_data) { + DoGradientFilter_SSE2(data, width, height, stride, filtered_data); } //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/dsp/lossless.c b/3rdparty/libwebp/src/dsp/lossless.c index 9f81209453..1a3d800c3f 100644 --- a/3rdparty/libwebp/src/dsp/lossless.c +++ b/3rdparty/libwebp/src/dsp/lossless.c @@ -13,15 +13,21 @@ // Jyrki Alakuijala (jyrki@google.com) // Urvang Joshi (urvang@google.com) -#include "src/dsp/dsp.h" +#include "src/dsp/lossless.h" #include -#include #include +#include + #include "src/dec/vp8li_dec.h" -#include "src/utils/endian_inl_utils.h" -#include "src/dsp/lossless.h" +#include "src/dsp/cpu.h" +#include "src/dsp/dsp.h" #include "src/dsp/lossless_common.h" +#include "src/utils/endian_inl_utils.h" +#include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Image transforms. @@ -107,14 +113,14 @@ static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) { //------------------------------------------------------------------------------ // Predictors -uint32_t VP8LPredictor0_C(const uint32_t* const left, - const uint32_t* const top) { +static 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) { +static uint32_t VP8LPredictor1_C(const uint32_t* const left, + const uint32_t* const top) { (void)top; return *left; } @@ -182,13 +188,13 @@ uint32_t VP8LPredictor13_C(const uint32_t* const left, } static void PredictorAdd0_C(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT 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 num_pixels, uint32_t* WEBP_RESTRICT out) { int i; uint32_t left = out[-1]; (void)upper; @@ -215,7 +221,7 @@ GENERATE_PREDICTOR_ADD(VP8LPredictor13_C, PredictorAdd13_C) 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_; + 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); @@ -226,11 +232,11 @@ static void PredictorInverseTransform_C(const VP8LTransform* const transform, { int y = y_start; - const int tile_width = 1 << transform->bits_; + const int tile_width = 1 << transform->bits; const int mask = tile_width - 1; - const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_); + const int tiles_per_row = VP8LSubSampleSize(width, transform->bits); const uint32_t* pred_mode_base = - transform->data_ + (y >> transform->bits_) * tiles_per_row; + transform->data + (y >> transform->bits) * tiles_per_row; while (y < y_end) { const uint32_t* pred_mode_src = pred_mode_base; @@ -278,9 +284,9 @@ static WEBP_INLINE int ColorTransformDelta(int8_t color_pred, 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; + 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, @@ -293,10 +299,10 @@ void VP8LTransformColorInverse_C(const VP8LMultipliers* const m, const uint32_t red = argb >> 16; int new_red = red & 0xff; int new_blue = argb & 0xff; - new_red += ColorTransformDelta((int8_t)m->green_to_red_, green); + new_red += ColorTransformDelta((int8_t)m->green_to_red, green); new_red &= 0xff; - new_blue += ColorTransformDelta((int8_t)m->green_to_blue_, green); - new_blue += ColorTransformDelta((int8_t)m->red_to_blue_, (int8_t)new_red); + new_blue += ColorTransformDelta((int8_t)m->green_to_blue, green); + new_blue += ColorTransformDelta((int8_t)m->red_to_blue, (int8_t)new_red); new_blue &= 0xff; dst[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); } @@ -306,15 +312,15 @@ void VP8LTransformColorInverse_C(const VP8LMultipliers* const m, 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 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_); + 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; + transform->data + (y >> transform->bits) * tiles_per_row; while (y < y_end) { const uint32_t* pred = pred_row; @@ -356,11 +362,11 @@ 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_; \ + 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 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) { \ @@ -391,16 +397,16 @@ COLOR_INDEX_INVERSE(VP8LColorIndexInverseTransformAlpha, MapAlpha_C, , 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_; + const int width = transform->xsize; assert(row_start < row_end); - assert(row_end <= transform->ysize_); - switch (transform->type_) { + assert(row_end <= transform->ysize); + switch (transform->type) { case SUBTRACT_GREEN_TRANSFORM: 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_) { + 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, @@ -411,15 +417,15 @@ void VP8LInverseTransform(const VP8LTransform* const transform, ColorSpaceInverseTransform_C(transform, row_start, row_end, in, out); break; case COLOR_INDEXING_TRANSFORM: - if (in == out && transform->bits_ > 0) { + 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_. + // 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_); + 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); @@ -441,8 +447,8 @@ static int is_big_endian(void) { return (tmp.b[0] != 1); } -void VP8LConvertBGRAToRGB_C(const uint32_t* src, - int num_pixels, uint8_t* dst) { +void VP8LConvertBGRAToRGB_C(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; @@ -452,8 +458,8 @@ void VP8LConvertBGRAToRGB_C(const uint32_t* src, } } -void VP8LConvertBGRAToRGBA_C(const uint32_t* src, - int num_pixels, uint8_t* dst) { +void VP8LConvertBGRAToRGBA_C(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; @@ -464,8 +470,8 @@ void VP8LConvertBGRAToRGBA_C(const uint32_t* src, } } -void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src, - int num_pixels, uint8_t* dst) { +void VP8LConvertBGRAToRGBA4444_C(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; @@ -481,8 +487,8 @@ void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src, } } -void VP8LConvertBGRAToRGB565_C(const uint32_t* src, - int num_pixels, uint8_t* dst) { +void VP8LConvertBGRAToRGB565_C(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; @@ -498,8 +504,8 @@ void VP8LConvertBGRAToRGB565_C(const uint32_t* src, } } -void VP8LConvertBGRAToBGR_C(const uint32_t* src, - int num_pixels, uint8_t* dst) { +void VP8LConvertBGRAToBGR_C(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const uint32_t* const src_end = src + num_pixels; while (src < src_end) { const uint32_t argb = *src++; @@ -509,8 +515,8 @@ void VP8LConvertBGRAToBGR_C(const uint32_t* src, } } -static void CopyOrSwap(const uint32_t* src, int num_pixels, uint8_t* dst, - int swap_on_big_endian) { +static void CopyOrSwap(const uint32_t* WEBP_RESTRICT src, int num_pixels, + uint8_t* WEBP_RESTRICT 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) { @@ -571,16 +577,21 @@ void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, //------------------------------------------------------------------------------ VP8LProcessDecBlueAndRedFunc VP8LAddGreenToBlueAndRed; +VP8LProcessDecBlueAndRedFunc VP8LAddGreenToBlueAndRed_SSE; VP8LPredictorAddSubFunc VP8LPredictorsAdd[16]; +VP8LPredictorAddSubFunc VP8LPredictorsAdd_SSE[16]; VP8LPredictorFunc VP8LPredictors[16]; // exposed plain-C implementations VP8LPredictorAddSubFunc VP8LPredictorsAdd_C[16]; VP8LTransformColorInverseFunc VP8LTransformColorInverse; +VP8LTransformColorInverseFunc VP8LTransformColorInverse_SSE; VP8LConvertFunc VP8LConvertBGRAToRGB; +VP8LConvertFunc VP8LConvertBGRAToRGB_SSE; VP8LConvertFunc VP8LConvertBGRAToRGBA; +VP8LConvertFunc VP8LConvertBGRAToRGBA_SSE; VP8LConvertFunc VP8LConvertBGRAToRGBA4444; VP8LConvertFunc VP8LConvertBGRAToRGB565; VP8LConvertFunc VP8LConvertBGRAToBGR; @@ -591,6 +602,7 @@ VP8LMapAlphaFunc VP8LMapColor8b; extern VP8CPUInfo VP8GetCPUInfo; extern void VP8LDspInitSSE2(void); extern void VP8LDspInitSSE41(void); +extern void VP8LDspInitAVX2(void); extern void VP8LDspInitNEON(void); extern void VP8LDspInitMIPSdspR2(void); extern void VP8LDspInitMSA(void); @@ -643,6 +655,11 @@ WEBP_DSP_INIT_FUNC(VP8LDspInit) { #if defined(WEBP_HAVE_SSE41) if (VP8GetCPUInfo(kSSE4_1)) { VP8LDspInitSSE41(); +#if defined(WEBP_HAVE_AVX2) + if (VP8GetCPUInfo(kAVX2)) { + VP8LDspInitAVX2(); + } +#endif } #endif } diff --git a/3rdparty/libwebp/src/dsp/lossless.h b/3rdparty/libwebp/src/dsp/lossless.h index 0bf10a1a3d..c66ec5da64 100644 --- a/3rdparty/libwebp/src/dsp/lossless.h +++ b/3rdparty/libwebp/src/dsp/lossless.h @@ -15,12 +15,10 @@ #ifndef WEBP_DSP_LOSSLESS_H_ #define WEBP_DSP_LOSSLESS_H_ +#include "src/dsp/dsp.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 @@ -32,10 +30,6 @@ 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, @@ -64,25 +58,28 @@ uint32_t VP8LPredictor13_C(const uint32_t* const left, // 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); + uint32_t* WEBP_RESTRICT out); extern VP8LPredictorAddSubFunc VP8LPredictorsAdd[16]; extern VP8LPredictorAddSubFunc VP8LPredictorsAdd_C[16]; +extern VP8LPredictorAddSubFunc VP8LPredictorsAdd_SSE[16]; typedef void (*VP8LProcessDecBlueAndRedFunc)(const uint32_t* src, int num_pixels, uint32_t* dst); extern VP8LProcessDecBlueAndRedFunc VP8LAddGreenToBlueAndRed; +extern VP8LProcessDecBlueAndRedFunc VP8LAddGreenToBlueAndRed_SSE; 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_; + 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; +extern VP8LTransformColorInverseFunc VP8LTransformColorInverse_SSE; struct VP8LTransform; // Defined in dec/vp8li.h. @@ -95,13 +92,15 @@ void VP8LInverseTransform(const struct VP8LTransform* const transform, 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); +typedef void (*VP8LConvertFunc)(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst); extern VP8LConvertFunc VP8LConvertBGRAToRGB; extern VP8LConvertFunc VP8LConvertBGRAToRGBA; extern VP8LConvertFunc VP8LConvertBGRAToRGBA4444; extern VP8LConvertFunc VP8LConvertBGRAToRGB565; extern VP8LConvertFunc VP8LConvertBGRAToBGR; +extern VP8LConvertFunc VP8LConvertBGRAToRGB_SSE; +extern VP8LConvertFunc VP8LConvertBGRAToRGBA_SSE; // Converts from BGRA to other color spaces. void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, @@ -131,13 +130,16 @@ 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 VP8LConvertBGRAToRGB_C(const uint32_t* WEBP_RESTRICT src, int num_pixels, + uint8_t* WEBP_RESTRICT dst); +void VP8LConvertBGRAToRGBA_C(const uint32_t* WEBP_RESTRICT src, int num_pixels, + uint8_t* WEBP_RESTRICT dst); +void VP8LConvertBGRAToRGBA4444_C(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst); +void VP8LConvertBGRAToRGB565_C(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst); +void VP8LConvertBGRAToBGR_C(const uint32_t* WEBP_RESTRICT src, int num_pixels, + uint8_t* WEBP_RESTRICT dst); void VP8LAddGreenToBlueAndRed_C(const uint32_t* src, int num_pixels, uint32_t* dst); @@ -149,48 +151,55 @@ void VP8LDspInit(void); 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 VP8LProcessEncBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed_SSE; +typedef void (*VP8LTransformColorFunc)( + const VP8LMultipliers* WEBP_RESTRICT const m, uint32_t* WEBP_RESTRICT dst, + int num_pixels); extern VP8LTransformColorFunc VP8LTransformColor; +extern VP8LTransformColorFunc VP8LTransformColor_SSE; typedef void (*VP8LCollectColorBlueTransformsFunc)( - const uint32_t* argb, int stride, + const uint32_t* WEBP_RESTRICT argb, int stride, int tile_width, int tile_height, - int green_to_blue, int red_to_blue, int histo[]); + int green_to_blue, int red_to_blue, uint32_t histo[]); extern VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms; +extern VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms_SSE; typedef void (*VP8LCollectColorRedTransformsFunc)( - const uint32_t* argb, int stride, + const uint32_t* WEBP_RESTRICT argb, int stride, int tile_width, int tile_height, - int green_to_red, int histo[]); + int green_to_red, uint32_t histo[]); extern VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms; +extern VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms_SSE; // Expose some C-only fallback functions -void VP8LTransformColor_C(const VP8LMultipliers* const m, - uint32_t* data, int num_pixels); +void VP8LTransformColor_C(const VP8LMultipliers* WEBP_RESTRICT const m, + uint32_t* WEBP_RESTRICT data, int num_pixels); void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels); -void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride, +void VP8LCollectColorRedTransforms_C(const uint32_t* WEBP_RESTRICT 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 green_to_red, uint32_t histo[]); +void VP8LCollectColorBlueTransforms_C(const uint32_t* WEBP_RESTRICT argb, + int stride, int tile_width, int tile_height, int green_to_blue, int red_to_blue, - int histo[]); + uint32_t histo[]); extern VP8LPredictorAddSubFunc VP8LPredictorsSub[16]; extern VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16]; +extern VP8LPredictorAddSubFunc VP8LPredictorsSub_SSE[16]; // ----------------------------------------------------------------------------- // Huffman-cost related functions. typedef uint32_t (*VP8LCostFunc)(const uint32_t* population, int length); -typedef uint32_t (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y, - int length); -typedef float (*VP8LCombinedShannonEntropyFunc)(const int X[256], - const int Y[256]); +typedef uint64_t (*VP8LCombinedShannonEntropyFunc)(const uint32_t X[256], + const uint32_t Y[256]); +typedef uint64_t (*VP8LShannonEntropyFunc)(const uint32_t* X, int length); extern VP8LCostFunc VP8LExtraCost; -extern VP8LCostCombinedFunc VP8LExtraCostCombined; extern VP8LCombinedShannonEntropyFunc VP8LCombinedShannonEntropy; +extern VP8LShannonEntropyFunc VP8LShannonEntropy; typedef struct { // small struct to hold counters int counts[2]; // index: 0=zero streak, 1=non-zero streak @@ -198,7 +207,7 @@ typedef struct { // small struct to hold counters } VP8LStreaks; typedef struct { // small struct to hold bit entropy results - float entropy; // entropy + uint64_t 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 @@ -212,26 +221,27 @@ void VP8LBitEntropyInit(VP8LBitEntropy* const entropy); // codec specific heuristics. typedef void (*VP8LGetCombinedEntropyUnrefinedFunc)( const uint32_t X[], const uint32_t Y[], int length, - VP8LBitEntropy* const bit_entropy, VP8LStreaks* const stats); + VP8LBitEntropy* WEBP_RESTRICT const bit_entropy, + VP8LStreaks* WEBP_RESTRICT 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); +typedef void (*VP8LGetEntropyUnrefinedFunc)( + const uint32_t X[], int length, + VP8LBitEntropy* WEBP_RESTRICT const bit_entropy, + VP8LStreaks* WEBP_RESTRICT const stats); extern VP8LGetEntropyUnrefinedFunc VP8LGetEntropyUnrefined; -void VP8LBitsEntropyUnrefined(const uint32_t* const array, int n, - VP8LBitEntropy* const entropy); +void VP8LBitsEntropyUnrefined(const uint32_t* WEBP_RESTRICT const array, int n, + VP8LBitEntropy* WEBP_RESTRICT const entropy); -typedef void (*VP8LAddVectorFunc)(const uint32_t* a, const uint32_t* b, - uint32_t* out, int size); +typedef void (*VP8LAddVectorFunc)(const uint32_t* WEBP_RESTRICT a, + const uint32_t* WEBP_RESTRICT b, + uint32_t* WEBP_RESTRICT out, int size); extern VP8LAddVectorFunc VP8LAddVector; -typedef void (*VP8LAddVectorEqFunc)(const uint32_t* a, uint32_t* out, int size); +typedef void (*VP8LAddVectorEqFunc)(const uint32_t* WEBP_RESTRICT a, + uint32_t* WEBP_RESTRICT out, int size); extern VP8LAddVectorEqFunc VP8LAddVectorEq; -void VP8LHistogramAdd(const VP8LHistogram* const a, - const VP8LHistogram* const b, - VP8LHistogram* const out); // ----------------------------------------------------------------------------- // PrefixEncode() @@ -241,11 +251,13 @@ typedef int (*VP8LVectorMismatchFunc)(const uint32_t* const array1, // 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); +typedef void (*VP8LBundleColorMapFunc)(const uint8_t* WEBP_RESTRICT const row, + int width, int xbits, + uint32_t* WEBP_RESTRICT dst); extern VP8LBundleColorMapFunc VP8LBundleColorMap; -void VP8LBundleColorMap_C(const uint8_t* const row, int width, int xbits, - uint32_t* dst); +extern VP8LBundleColorMapFunc VP8LBundleColorMap_SSE; +void VP8LBundleColorMap_C(const uint8_t* WEBP_RESTRICT const row, + int width, int xbits, uint32_t* WEBP_RESTRICT dst); // Must be called before calling any of the above methods. void VP8LEncDspInit(void); diff --git a/3rdparty/libwebp/src/dsp/lossless_avx2.c b/3rdparty/libwebp/src/dsp/lossless_avx2.c new file mode 100644 index 0000000000..dc866049e8 --- /dev/null +++ b/3rdparty/libwebp/src/dsp/lossless_avx2.c @@ -0,0 +1,443 @@ +// Copyright 2025 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// AVX2 variant of methods for lossless decoder +// +// Author: Vincent Rabaud (vrabaud@google.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_AVX2) + +#include +#include + +#include "src/dsp/cpu.h" +#include "src/dsp/lossless.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" + +//------------------------------------------------------------------------------ +// Predictor Transform + +static WEBP_INLINE void Average2_m256i(const __m256i* const a0, + const __m256i* const a1, + __m256i* const avg) { + // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1) + const __m256i ones = _mm256_set1_epi8(1); + const __m256i avg1 = _mm256_avg_epu8(*a0, *a1); + const __m256i one = _mm256_and_si256(_mm256_xor_si256(*a0, *a1), ones); + *avg = _mm256_sub_epi8(avg1, one); +} + +// Batch versions of those functions. + +// Predictor0: ARGB_BLACK. +static void PredictorAdd0_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i; + const __m256i black = _mm256_set1_epi32((int)ARGB_BLACK); + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + const __m256i res = _mm256_add_epi8(src, black); + _mm256_storeu_si256((__m256i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsAdd_SSE[0](in + i, NULL, num_pixels - i, out + i); + } + (void)upper; +} + +// Predictor1: left. +static void PredictorAdd1_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i; + __m256i prev = _mm256_set1_epi32((int)out[-1]); + for (i = 0; i + 8 <= num_pixels; i += 8) { + // h | g | f | e | d | c | b | a + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + // g | f | e | 0 | c | b | a | 0 + const __m256i shift0 = _mm256_slli_si256(src, 4); + // g + h | f + g | e + f | e | c + d | b + c | a + b | a + const __m256i sum0 = _mm256_add_epi8(src, shift0); + // e + f | e | 0 | 0 | a + b | a | 0 | 0 + const __m256i shift1 = _mm256_slli_si256(sum0, 8); + // e + f + g + h | e + f + g | e + f | e | a + b + c + d | a + b + c | a + b + // | a + const __m256i sum1 = _mm256_add_epi8(sum0, shift1); + // Add a + b + c + d to the upper lane. + const int32_t sum_abcd = _mm256_extract_epi32(sum1, 3); + const __m256i sum2 = _mm256_add_epi8( + sum1, + _mm256_set_epi32(sum_abcd, sum_abcd, sum_abcd, sum_abcd, 0, 0, 0, 0)); + + const __m256i res = _mm256_add_epi8(sum2, prev); + _mm256_storeu_si256((__m256i*)&out[i], res); + // replicate last res output in prev. + prev = _mm256_permutevar8x32_epi32( + res, _mm256_set_epi32(7, 7, 7, 7, 7, 7, 7, 7)); + } + if (i != num_pixels) { + VP8LPredictorsAdd_SSE[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##_AVX2(const uint32_t* in, \ + const uint32_t* upper, int num_pixels, \ + uint32_t* WEBP_RESTRICT out) { \ + int i; \ + for (i = 0; i + 8 <= num_pixels; i += 8) { \ + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ + const __m256i other = _mm256_loadu_si256((const __m256i*)&(IN)); \ + const __m256i res = _mm256_add_epi8(src, other); \ + _mm256_storeu_si256((__m256i*)&out[i], res); \ + } \ + if (i != num_pixels) { \ + VP8LPredictorsAdd_SSE[(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. + +#define GENERATE_PREDICTOR_2(X, IN) \ + static void PredictorAdd##X##_AVX2(const uint32_t* in, \ + const uint32_t* upper, int num_pixels, \ + uint32_t* WEBP_RESTRICT out) { \ + int i; \ + for (i = 0; i + 8 <= num_pixels; i += 8) { \ + const __m256i Tother = _mm256_loadu_si256((const __m256i*)&(IN)); \ + const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); \ + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ + __m256i avg, res; \ + Average2_m256i(&T, &Tother, &avg); \ + res = _mm256_add_epi8(avg, src); \ + _mm256_storeu_si256((__m256i*)&out[i], res); \ + } \ + if (i != num_pixels) { \ + VP8LPredictorsAdd_SSE[(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 { \ + __m256i avgLTL, avg; \ + Average2_m256i(&L, &TL, &avgLTL); \ + Average2_m256i(&avgTTR, &avgLTL, &avg); \ + L = _mm256_add_epi8(avg, src); \ + out[i + (OUT)] = (uint32_t)_mm256_cvtsi256_si32(L); \ + } while (0) + +#define DO_PRED10_SHIFT \ + do { \ + /* Rotate the pre-computed values for the next iteration.*/ \ + avgTTR = _mm256_srli_si256(avgTTR, 4); \ + TL = _mm256_srli_si256(TL, 4); \ + src = _mm256_srli_si256(src, 4); \ + } while (0) + +static void PredictorAdd10_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i, j; + __m256i L = _mm256_setr_epi32((int)out[-1], 0, 0, 0, 0, 0, 0, 0); + for (i = 0; i + 8 <= num_pixels; i += 8) { + __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); + const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); + const __m256i TR = _mm256_loadu_si256((const __m256i*)&upper[i + 1]); + __m256i avgTTR; + Average2_m256i(&T, &TR, &avgTTR); + { + const __m256i avgTTR_bak = avgTTR; + const __m256i TL_bak = TL; + const __m256i src_bak = src; + for (j = 0; j < 4; ++j) { + DO_PRED10(j); + DO_PRED10_SHIFT; + } + avgTTR = _mm256_permute2x128_si256(avgTTR_bak, avgTTR_bak, 1); + TL = _mm256_permute2x128_si256(TL_bak, TL_bak, 1); + src = _mm256_permute2x128_si256(src_bak, src_bak, 1); + for (; j < 8; ++j) { + DO_PRED10(j); + DO_PRED10_SHIFT; + } + } + } + if (i != num_pixels) { + VP8LPredictorsAdd_SSE[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 __m256i L_lo = _mm256_unpacklo_epi32(L, T); \ + const __m256i TL_lo = _mm256_unpacklo_epi32(TL, T); \ + const __m256i pb = _mm256_sad_epu8(L_lo, TL_lo); /* pb = sum |L-TL|*/ \ + const __m256i mask = _mm256_cmpgt_epi32(pb, pa); \ + const __m256i A = _mm256_and_si256(mask, L); \ + const __m256i B = _mm256_andnot_si256(mask, T); \ + const __m256i pred = _mm256_or_si256(A, B); /* pred = (pa > b)? L : T*/ \ + L = _mm256_add_epi8(src, pred); \ + out[i + (OUT)] = (uint32_t)_mm256_cvtsi256_si32(L); \ + } while (0) + +#define DO_PRED11_SHIFT \ + do { \ + /* Shift the pre-computed value for the next iteration.*/ \ + T = _mm256_srli_si256(T, 4); \ + TL = _mm256_srli_si256(TL, 4); \ + src = _mm256_srli_si256(src, 4); \ + pa = _mm256_srli_si256(pa, 4); \ + } while (0) + +static void PredictorAdd11_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i, j; + __m256i pa; + __m256i L = _mm256_setr_epi32((int)out[-1], 0, 0, 0, 0, 0, 0, 0); + for (i = 0; i + 8 <= num_pixels; i += 8) { + __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); + __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); + __m256i src = _mm256_loadu_si256((const __m256i*)&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 __m256i T_lo = _mm256_unpacklo_epi32(T, T); + const __m256i TL_lo = _mm256_unpacklo_epi32(TL, T); + const __m256i T_hi = _mm256_unpackhi_epi32(T, T); + const __m256i TL_hi = _mm256_unpackhi_epi32(TL, T); + const __m256i s_lo = _mm256_sad_epu8(T_lo, TL_lo); + const __m256i s_hi = _mm256_sad_epu8(T_hi, TL_hi); + pa = _mm256_packs_epi32(s_lo, s_hi); // pa = sum |T-TL| + } + { + const __m256i T_bak = T; + const __m256i TL_bak = TL; + const __m256i src_bak = src; + const __m256i pa_bak = pa; + for (j = 0; j < 4; ++j) { + DO_PRED11(j); + DO_PRED11_SHIFT; + } + T = _mm256_permute2x128_si256(T_bak, T_bak, 1); + TL = _mm256_permute2x128_si256(TL_bak, TL_bak, 1); + src = _mm256_permute2x128_si256(src_bak, src_bak, 1); + pa = _mm256_permute2x128_si256(pa_bak, pa_bak, 1); + for (; j < 8; ++j) { + DO_PRED11(j); + DO_PRED11_SHIFT; + } + } + } + if (i != num_pixels) { + VP8LPredictorsAdd_SSE[11](in + i, upper + i, num_pixels - i, out + i); + } +} +#undef DO_PRED11 +#undef DO_PRED11_SHIFT + +// Predictor12: ClampedAddSubtractFull. +#define DO_PRED12(DIFF, OUT) \ + do { \ + const __m256i all = _mm256_add_epi16(L, (DIFF)); \ + const __m256i alls = _mm256_packus_epi16(all, all); \ + const __m256i res = _mm256_add_epi8(src, alls); \ + out[i + (OUT)] = (uint32_t)_mm256_cvtsi256_si32(res); \ + L = _mm256_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) = _mm256_srli_si256(DIFF, 8); \ + src = _mm256_srli_si256(src, 4); \ + } while (0) + +static void PredictorAdd12_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i; + const __m256i zero = _mm256_setzero_si256(); + const __m256i L8 = _mm256_setr_epi32((int)out[-1], 0, 0, 0, 0, 0, 0, 0); + __m256i L = _mm256_unpacklo_epi8(L8, zero); + for (i = 0; i + 8 <= num_pixels; i += 8) { + // Load 8 pixels at a time. + __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); + const __m256i T_lo = _mm256_unpacklo_epi8(T, zero); + const __m256i T_hi = _mm256_unpackhi_epi8(T, zero); + const __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); + const __m256i TL_lo = _mm256_unpacklo_epi8(TL, zero); + const __m256i TL_hi = _mm256_unpackhi_epi8(TL, zero); + __m256i diff_lo = _mm256_sub_epi16(T_lo, TL_lo); + __m256i diff_hi = _mm256_sub_epi16(T_hi, TL_hi); + const __m256i diff_lo_bak = diff_lo; + const __m256i diff_hi_bak = diff_hi; + const __m256i src_bak = src; + DO_PRED12(diff_lo, 0); + DO_PRED12_SHIFT(diff_lo, 0); + DO_PRED12(diff_lo, 1); + DO_PRED12_SHIFT(diff_lo, 0); + DO_PRED12(diff_hi, 2); + DO_PRED12_SHIFT(diff_hi, 0); + DO_PRED12(diff_hi, 3); + DO_PRED12_SHIFT(diff_hi, 0); + + // Process the upper lane. + diff_lo = _mm256_permute2x128_si256(diff_lo_bak, diff_lo_bak, 1); + diff_hi = _mm256_permute2x128_si256(diff_hi_bak, diff_hi_bak, 1); + src = _mm256_permute2x128_si256(src_bak, src_bak, 1); + + DO_PRED12(diff_lo, 4); + DO_PRED12_SHIFT(diff_lo, 0); + DO_PRED12(diff_lo, 5); + DO_PRED12_SHIFT(diff_lo, 1); + DO_PRED12(diff_hi, 6); + DO_PRED12_SHIFT(diff_hi, 0); + DO_PRED12(diff_hi, 7); + } + if (i != num_pixels) { + VP8LPredictorsAdd_SSE[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. + +//------------------------------------------------------------------------------ +// Subtract-Green Transform + +static void AddGreenToBlueAndRed_AVX2(const uint32_t* const src, int num_pixels, + uint32_t* dst) { + int i; + const __m256i kCstShuffle = _mm256_set_epi8( + -1, 29, -1, 29, -1, 25, -1, 25, -1, 21, -1, 21, -1, 17, -1, 17, -1, 13, + -1, 13, -1, 9, -1, 9, -1, 5, -1, 5, -1, 1, -1, 1); + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i in = _mm256_loadu_si256((const __m256i*)&src[i]); // argb + const __m256i in_0g0g = _mm256_shuffle_epi8(in, kCstShuffle); // 0g0g + const __m256i out = _mm256_add_epi8(in, in_0g0g); + _mm256_storeu_si256((__m256i*)&dst[i], out); + } + // fallthrough and finish off with SSE. + if (i != num_pixels) { + VP8LAddGreenToBlueAndRed_SSE(src + i, num_pixels - i, dst + i); + } +} + +//------------------------------------------------------------------------------ +// Color Transform + +static void TransformColorInverse_AVX2(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 __m256i mults_rb = + _mm256_set1_epi32((int)((uint32_t)CST(green_to_red) << 16 | + (CST(green_to_blue) & 0xffff))); + const __m256i mults_b2 = _mm256_set1_epi32(CST(red_to_blue)); +#undef CST + const __m256i mask_ag = _mm256_set1_epi32((int)0xff00ff00); + const __m256i perm1 = _mm256_setr_epi8( + -1, 1, -1, 1, -1, 5, -1, 5, -1, 9, -1, 9, -1, 13, -1, 13, -1, 17, -1, 17, + -1, 21, -1, 21, -1, 25, -1, 25, -1, 29, -1, 29); + const __m256i perm2 = _mm256_setr_epi8( + -1, 2, -1, -1, -1, 6, -1, -1, -1, 10, -1, -1, -1, 14, -1, -1, -1, 18, -1, + -1, -1, 22, -1, -1, -1, 26, -1, -1, -1, 30, -1, -1); + int i; + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i A = _mm256_loadu_si256((const __m256i*)(src + i)); + const __m256i B = _mm256_shuffle_epi8(A, perm1); // argb -> g0g0 + const __m256i C = _mm256_mulhi_epi16(B, mults_rb); + const __m256i D = _mm256_add_epi8(A, C); + const __m256i E = _mm256_shuffle_epi8(D, perm2); + const __m256i F = _mm256_mulhi_epi16(E, mults_b2); + const __m256i G = _mm256_add_epi8(D, F); + const __m256i out = _mm256_blendv_epi8(G, A, mask_ag); + _mm256_storeu_si256((__m256i*)&dst[i], out); + } + // Fall-back to SSE-version for left-overs. + if (i != num_pixels) { + VP8LTransformColorInverse_SSE(m, src + i, num_pixels - i, dst + i); + } +} + +//------------------------------------------------------------------------------ +// Color-space conversion functions + +static void ConvertBGRAToRGBA_AVX2(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { + const __m256i* in = (const __m256i*)src; + __m256i* out = (__m256i*)dst; + while (num_pixels >= 8) { + const __m256i A = _mm256_loadu_si256(in++); + const __m256i B = _mm256_shuffle_epi8( + A, + _mm256_set_epi8(15, 12, 13, 14, 11, 8, 9, 10, 7, 4, 5, 6, 3, 0, 1, 2, + 15, 12, 13, 14, 11, 8, 9, 10, 7, 4, 5, 6, 3, 0, 1, 2)); + _mm256_storeu_si256(out++, B); + num_pixels -= 8; + } + // left-overs + if (num_pixels > 0) { + VP8LConvertBGRAToRGBA_SSE((const uint32_t*)in, num_pixels, (uint8_t*)out); + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LDspInitAVX2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitAVX2(void) { + VP8LPredictorsAdd[0] = PredictorAdd0_AVX2; + VP8LPredictorsAdd[1] = PredictorAdd1_AVX2; + VP8LPredictorsAdd[2] = PredictorAdd2_AVX2; + VP8LPredictorsAdd[3] = PredictorAdd3_AVX2; + VP8LPredictorsAdd[4] = PredictorAdd4_AVX2; + VP8LPredictorsAdd[8] = PredictorAdd8_AVX2; + VP8LPredictorsAdd[9] = PredictorAdd9_AVX2; + VP8LPredictorsAdd[10] = PredictorAdd10_AVX2; + VP8LPredictorsAdd[11] = PredictorAdd11_AVX2; + VP8LPredictorsAdd[12] = PredictorAdd12_AVX2; + + VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed_AVX2; + VP8LTransformColorInverse = TransformColorInverse_AVX2; + VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA_AVX2; +} + +#else // !WEBP_USE_AVX2 + +WEBP_DSP_INIT_STUB(VP8LDspInitAVX2) + +#endif // WEBP_USE_AVX2 diff --git a/3rdparty/libwebp/src/dsp/lossless_common.h b/3rdparty/libwebp/src/dsp/lossless_common.h index d6139b2b57..c856679d1e 100644 --- a/3rdparty/libwebp/src/dsp/lossless_common.h +++ b/3rdparty/libwebp/src/dsp/lossless_common.h @@ -16,6 +16,9 @@ #ifndef WEBP_DSP_LOSSLESS_COMMON_H_ #define WEBP_DSP_LOSSLESS_COMMON_H_ +#include +#include + #include "src/dsp/cpu.h" #include "src/utils/utils.h" #include "src/webp/types.h" @@ -73,23 +76,44 @@ static WEBP_INLINE int VP8LNearLosslessBits(int near_lossless_quality) { // Keeping a high threshold for now. #define APPROX_LOG_WITH_CORRECTION_MAX 65536 #define APPROX_LOG_MAX 4096 +// VP8LFastLog2 and VP8LFastSLog2 are used on elements from image histograms. +// The histogram values cannot exceed the maximum number of pixels, which +// is (1 << 14) * (1 << 14). Therefore S * log(S) < (1 << 33). +// No more than 32 bits of precision should be chosen. +// To match the original float implementation, 23 bits of precision are used. +#define LOG_2_PRECISION_BITS 23 #define LOG_2_RECIPROCAL 1.44269504088896338700465094007086 +// LOG_2_RECIPROCAL * (1 << LOG_2_PRECISION_BITS) +#define LOG_2_RECIPROCAL_FIXED_DOUBLE 12102203.161561485379934310913085937500 +#define LOG_2_RECIPROCAL_FIXED ((uint64_t)12102203) #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 const uint32_t kLog2Table[LOG_LOOKUP_IDX_MAX]; +extern const uint64_t kSLog2Table[LOG_LOOKUP_IDX_MAX]; +typedef uint32_t (*VP8LFastLog2SlowFunc)(uint32_t v); +typedef uint64_t (*VP8LFastSLog2SlowFunc)(uint32_t v); extern VP8LFastLog2SlowFunc VP8LFastLog2Slow; -extern VP8LFastLog2SlowFunc VP8LFastSLog2Slow; +extern VP8LFastSLog2SlowFunc VP8LFastSLog2Slow; -static WEBP_INLINE float VP8LFastLog2(uint32_t v) { +static WEBP_INLINE uint32_t 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) { +static WEBP_INLINE uint64_t VP8LFastSLog2(uint32_t v) { return (v < LOG_LOOKUP_IDX_MAX) ? kSLog2Table[v] : VP8LFastSLog2Slow(v); } +static WEBP_INLINE uint64_t RightShiftRound(uint64_t v, uint32_t shift) { + return (v + (1ull << shift >> 1)) >> shift; +} + +static WEBP_INLINE int64_t DivRound(int64_t a, int64_t b) { + return ((a < 0) == (b < 0)) ? ((a + b / 2) / b) : ((a - b / 2) / b); +} + +#define WEBP_INT64_MAX ((int64_t)((1ull << 63) - 1)) +#define WEBP_UINT64_MAX (~0ull) + // ----------------------------------------------------------------------------- // PrefixEncode() @@ -116,8 +140,8 @@ static WEBP_INLINE void VP8LPrefixEncodeNoLUT(int distance, int* const code, #define PREFIX_LOOKUP_IDX_MAX 512 typedef struct { - int8_t code_; - int8_t extra_bits_; + int8_t code; + int8_t extra_bits; } VP8LPrefixCode; // These tables are derived using VP8LPrefixEncodeNoLUT. @@ -127,8 +151,8 @@ 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_; + *code = prefix_code.code; + *extra_bits = prefix_code.extra_bits; } else { VP8LPrefixEncodeBitsNoLUT(distance, code, extra_bits); } @@ -139,8 +163,8 @@ static WEBP_INLINE void VP8LPrefixEncode(int distance, int* const code, 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_; + *code = prefix_code.code; + *extra_bits = prefix_code.extra_bits; *extra_bits_value = kPrefixEncodeExtraBitsValue[distance]; } else { VP8LPrefixEncodeNoLUT(distance, code, extra_bits, extra_bits_value); @@ -173,15 +197,15 @@ uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { // 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); \ - } \ +#define GENERATE_PREDICTOR_ADD(PREDICTOR, PREDICTOR_ADD) \ +static void PREDICTOR_ADD(const uint32_t* in, const uint32_t* upper, \ + int num_pixels, uint32_t* WEBP_RESTRICT 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 diff --git a/3rdparty/libwebp/src/dsp/lossless_enc.c b/3rdparty/libwebp/src/dsp/lossless_enc.c index 997d56c2ad..13a98a705f 100644 --- a/3rdparty/libwebp/src/dsp/lossless_enc.c +++ b/3rdparty/libwebp/src/dsp/lossless_enc.c @@ -13,214 +13,137 @@ // 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 + +#include "src/dsp/cpu.h" +#include "src/dsp/dsp.h" #include "src/dsp/lossless.h" #include "src/dsp/lossless_common.h" -#include "src/dsp/yuv.h" +#include "src/enc/histogram_enc.h" +#include "src/utils/utils.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.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 +// lookup table for small values of log2(int) * (1 << LOG_2_PRECISION_BITS). +// Obtained in Python with: +// a = [ str(round((1<<23)*math.log2(i))) if i else "0" for i in range(256)] +// print(',\n'.join([' '+','.join(v) +// for v in batched([i.rjust(9) for i in a],7)])) +const uint32_t kLog2Table[LOG_LOOKUP_IDX_MAX] = { + 0, 0, 8388608, 13295629, 16777216, 19477745, 21684237, + 23549800, 25165824, 26591258, 27866353, 29019816, 30072845, 31041538, + 31938408, 32773374, 33554432, 34288123, 34979866, 35634199, 36254961, + 36845429, 37408424, 37946388, 38461453, 38955489, 39430146, 39886887, + 40327016, 40751698, 41161982, 41558811, 41943040, 42315445, 42676731, + 43027545, 43368474, 43700062, 44022807, 44337167, 44643569, 44942404, + 45234037, 45518808, 45797032, 46069003, 46334996, 46595268, 46850061, + 47099600, 47344097, 47583753, 47818754, 48049279, 48275495, 48497560, + 48715624, 48929828, 49140306, 49347187, 49550590, 49750631, 49947419, + 50141058, 50331648, 50519283, 50704053, 50886044, 51065339, 51242017, + 51416153, 51587818, 51757082, 51924012, 52088670, 52251118, 52411415, + 52569616, 52725775, 52879946, 53032177, 53182516, 53331012, 53477707, + 53622645, 53765868, 53907416, 54047327, 54185640, 54322389, 54457611, + 54591338, 54723604, 54854440, 54983876, 55111943, 55238669, 55364082, + 55488208, 55611074, 55732705, 55853126, 55972361, 56090432, 56207362, + 56323174, 56437887, 56551524, 56664103, 56775645, 56886168, 56995691, + 57104232, 57211808, 57318436, 57424133, 57528914, 57632796, 57735795, + 57837923, 57939198, 58039632, 58139239, 58238033, 58336027, 58433234, + 58529666, 58625336, 58720256, 58814437, 58907891, 59000628, 59092661, + 59183999, 59274652, 59364632, 59453947, 59542609, 59630625, 59718006, + 59804761, 59890898, 59976426, 60061354, 60145690, 60229443, 60312620, + 60395229, 60477278, 60558775, 60639726, 60720140, 60800023, 60879382, + 60958224, 61036555, 61114383, 61191714, 61268554, 61344908, 61420785, + 61496188, 61571124, 61645600, 61719620, 61793189, 61866315, 61939001, + 62011253, 62083076, 62154476, 62225457, 62296024, 62366182, 62435935, + 62505289, 62574248, 62642816, 62710997, 62778797, 62846219, 62913267, + 62979946, 63046260, 63112212, 63177807, 63243048, 63307939, 63372484, + 63436687, 63500551, 63564080, 63627277, 63690146, 63752690, 63814912, + 63876816, 63938405, 63999682, 64060650, 64121313, 64181673, 64241734, + 64301498, 64360969, 64420148, 64479040, 64537646, 64595970, 64654014, + 64711782, 64769274, 64826495, 64883447, 64940132, 64996553, 65052711, + 65108611, 65164253, 65219641, 65274776, 65329662, 65384299, 65438691, + 65492840, 65546747, 65600416, 65653847, 65707044, 65760008, 65812741, + 65865245, 65917522, 65969575, 66021404, 66073013, 66124403, 66175575, + 66226531, 66277275, 66327806, 66378127, 66428240, 66478146, 66527847, + 66577345, 66626641, 66675737, 66724635, 66773336, 66821842, 66870154, + 66918274, 66966204, 67013944, 67061497 }; -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 +// lookup table for small values of int*log2(int) * (1 << LOG_2_PRECISION_BITS). +// Obtained in Python with: +// a=[ "%d"%i if i<(1<<32) else "%dull"%i +// for i in [ round((1<= LOG_LOOKUP_IDX_MAX); if (v < APPROX_LOG_WITH_CORRECTION_MAX) { + const uint64_t orig_v = v; + uint64_t correction; #if !defined(WEBP_HAVE_SLOW_CLZ_CTZ) // use clz if available - const int log_cnt = BitsLog2Floor(v) - 7; + const uint64_t 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; + uint64_t 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; @@ -354,45 +273,43 @@ static float FastSLog2Slow_C(uint32_t 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; + correction = LOG_2_RECIPROCAL_FIXED * (orig_v & (y - 1)); + return orig_v * (kLog2Table[v] + (log_cnt << LOG_2_PRECISION_BITS)) + + correction; } else { - return (float)(LOG_2_RECIPROCAL * v * log((double)v)); + return (uint64_t)(LOG_2_RECIPROCAL_FIXED_DOUBLE * v * log((double)v) + .5); } } -static float FastLog2Slow_C(uint32_t v) { +static uint32_t FastLog2Slow_C(uint32_t v) { assert(v >= LOG_LOOKUP_IDX_MAX); if (v < APPROX_LOG_WITH_CORRECTION_MAX) { + const uint32_t orig_v = v; + uint32_t log_2; #if !defined(WEBP_HAVE_SLOW_CLZ_CTZ) // use clz if available - const int log_cnt = BitsLog2Floor(v) - 7; + const uint32_t 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 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; + log_2 = kLog2Table[v] + (log_cnt << LOG_2_PRECISION_BITS); 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; + const uint64_t correction = LOG_2_RECIPROCAL_FIXED * (orig_v & (y - 1)); + log_2 += (uint32_t)DivRound(correction, orig_v); } - return (float)log_2; + return log_2; } else { - return (float)(LOG_2_RECIPROCAL * log((double)v)); + return (uint32_t)(LOG_2_RECIPROCAL_FIXED_DOUBLE * log((double)v) + .5); } } @@ -400,37 +317,53 @@ static float FastLog2Slow_C(uint32_t 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]) { +static uint64_t CombinedShannonEntropy_C(const uint32_t X[256], + const uint32_t Y[256]) { int i; - float retval = 0.f; - int sumX = 0, sumXY = 0; + uint64_t retval = 0; + uint32_t sumX = 0, sumXY = 0; for (i = 0; i < 256; ++i) { - const int x = X[i]; + const uint32_t x = X[i]; if (x != 0) { - const int xy = x + Y[i]; + const uint32_t xy = x + Y[i]; sumX += x; - retval -= VP8LFastSLog2(x); + retval += VP8LFastSLog2(x); sumXY += xy; - retval -= VP8LFastSLog2(xy); + retval += VP8LFastSLog2(xy); } else if (Y[i] != 0) { sumXY += Y[i]; - retval -= VP8LFastSLog2(Y[i]); + retval += VP8LFastSLog2(Y[i]); } } - retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY); + retval = VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY) - retval; + return retval; +} + +static uint64_t ShannonEntropy_C(const uint32_t* X, int n) { + int i; + uint64_t retval = 0; + uint32_t sumX = 0; + for (i = 0; i < n; ++i) { + const int x = X[i]; + if (x != 0) { + sumX += x; + retval += VP8LFastSLog2(x); + } + } + retval = VP8LFastSLog2(sumX) - retval; return retval; } void VP8LBitEntropyInit(VP8LBitEntropy* const entropy) { - entropy->entropy = 0.; + 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) { +void VP8LBitsEntropyUnrefined(const uint32_t* WEBP_RESTRICT const array, int n, + VP8LBitEntropy* WEBP_RESTRICT const entropy) { int i; VP8LBitEntropyInit(entropy); @@ -440,18 +373,20 @@ void VP8LBitsEntropyUnrefined(const uint32_t* const array, int n, entropy->sum += array[i]; entropy->nonzero_code = i; ++entropy->nonzeros; - entropy->entropy -= VP8LFastSLog2(array[i]); + entropy->entropy += VP8LFastSLog2(array[i]); if (entropy->max_val < array[i]) { entropy->max_val = array[i]; } } } - entropy->entropy += VP8LFastSLog2(entropy->sum); + entropy->entropy = VP8LFastSLog2(entropy->sum) - entropy->entropy; } 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) { + uint32_t val, int i, uint32_t* WEBP_RESTRICT const val_prev, + int* WEBP_RESTRICT const i_prev, + VP8LBitEntropy* WEBP_RESTRICT const bit_entropy, + VP8LStreaks* WEBP_RESTRICT const stats) { const int streak = i - *i_prev; // Gather info for the bit entropy. @@ -459,7 +394,7 @@ static WEBP_INLINE void GetEntropyUnrefinedHelper( bit_entropy->sum += (*val_prev) * streak; bit_entropy->nonzeros += streak; bit_entropy->nonzero_code = *i_prev; - bit_entropy->entropy -= VP8LFastSLog2(*val_prev) * streak; + bit_entropy->entropy += VP8LFastSLog2(*val_prev) * streak; if (bit_entropy->max_val < *val_prev) { bit_entropy->max_val = *val_prev; } @@ -473,9 +408,10 @@ static WEBP_INLINE void GetEntropyUnrefinedHelper( *i_prev = i; } -static void GetEntropyUnrefined_C(const uint32_t X[], int length, - VP8LBitEntropy* const bit_entropy, - VP8LStreaks* const stats) { +static void GetEntropyUnrefined_C( + const uint32_t X[], int length, + VP8LBitEntropy* WEBP_RESTRICT const bit_entropy, + VP8LStreaks* WEBP_RESTRICT const stats) { int i; int i_prev = 0; uint32_t x_prev = X[0]; @@ -491,14 +427,13 @@ static void GetEntropyUnrefined_C(const uint32_t X[], int length, } GetEntropyUnrefinedHelper(0, i, &x_prev, &i_prev, bit_entropy, stats); - bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum); + bit_entropy->entropy = VP8LFastSLog2(bit_entropy->sum) - bit_entropy->entropy; } -static void GetCombinedEntropyUnrefined_C(const uint32_t X[], - const uint32_t Y[], - int length, - VP8LBitEntropy* const bit_entropy, - VP8LStreaks* const stats) { +static void GetCombinedEntropyUnrefined_C( + const uint32_t X[], const uint32_t Y[], int length, + VP8LBitEntropy* WEBP_RESTRICT const bit_entropy, + VP8LStreaks* WEBP_RESTRICT const stats) { int i = 1; int i_prev = 0; uint32_t xy_prev = X[0] + Y[0]; @@ -514,7 +449,7 @@ static void GetCombinedEntropyUnrefined_C(const uint32_t X[], } GetEntropyUnrefinedHelper(0, i, &xy_prev, &i_prev, bit_entropy, stats); - bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum); + bit_entropy->entropy = VP8LFastSLog2(bit_entropy->sum) - bit_entropy->entropy; } //------------------------------------------------------------------------------ @@ -538,8 +473,8 @@ 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) { +void VP8LTransformColor_C(const VP8LMultipliers* WEBP_RESTRICT const m, + uint32_t* WEBP_RESTRICT data, int num_pixels) { int i; for (i = 0; i < num_pixels; ++i) { const uint32_t argb = data[i]; @@ -547,10 +482,10 @@ void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data, const int8_t red = U32ToS8(argb >> 16); int new_red = red & 0xff; int new_blue = argb & 0xff; - new_red -= ColorTransformDelta((int8_t)m->green_to_red_, green); + new_red -= ColorTransformDelta((int8_t)m->green_to_red, green); new_red &= 0xff; - new_blue -= ColorTransformDelta((int8_t)m->green_to_blue_, green); - new_blue -= ColorTransformDelta((int8_t)m->red_to_blue_, red); + new_blue -= ColorTransformDelta((int8_t)m->green_to_blue, green); + new_blue -= ColorTransformDelta((int8_t)m->red_to_blue, red); new_blue &= 0xff; data[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); } @@ -575,9 +510,10 @@ static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue, return (new_blue & 0xff); } -void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride, +void VP8LCollectColorRedTransforms_C(const uint32_t* WEBP_RESTRICT argb, + int stride, int tile_width, int tile_height, - int green_to_red, int histo[]) { + int green_to_red, uint32_t histo[]) { while (tile_height-- > 0) { int x; for (x = 0; x < tile_width; ++x) { @@ -587,10 +523,11 @@ void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride, } } -void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride, +void VP8LCollectColorBlueTransforms_C(const uint32_t* WEBP_RESTRICT argb, + int stride, int tile_width, int tile_height, int green_to_blue, int red_to_blue, - int histo[]) { + uint32_t histo[]) { while (tile_height-- > 0) { int x; for (x = 0; x < tile_width; ++x) { @@ -614,8 +551,8 @@ static int VectorMismatch_C(const uint32_t* const array1, } // 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) { +void VP8LBundleColorMap_C(const uint8_t* WEBP_RESTRICT const row, + int width, int xbits, uint32_t* WEBP_RESTRICT dst) { int x; if (xbits > 0) { const int bit_depth = 1 << (3 - xbits); @@ -646,95 +583,33 @@ static uint32_t ExtraCost_C(const uint32_t* population, int length) { return cost; } -static uint32_t ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y, - int length) { - int i; - uint32_t cost = X[4] + Y[4] + X[5] + Y[5]; - assert(length % 2 == 0); - for (i = 2; i < length / 2 - 1; ++i) { - const int xy0 = X[2 * i + 2] + Y[2 * i + 2]; - const int xy1 = X[2 * i + 3] + Y[2 * i + 3]; - cost += i * (xy0 + xy1); - } - return cost; -} - //------------------------------------------------------------------------------ -static void AddVector_C(const uint32_t* a, const uint32_t* b, uint32_t* out, - int size) { +static void AddVector_C(const uint32_t* WEBP_RESTRICT a, + const uint32_t* WEBP_RESTRICT b, + uint32_t* WEBP_RESTRICT 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) { +static void AddVectorEq_C(const uint32_t* WEBP_RESTRICT a, + uint32_t* WEBP_RESTRICT 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 num_pixels, uint32_t* WEBP_RESTRICT 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 num_pixels, uint32_t* WEBP_RESTRICT out) { int i; for (i = 0; i < num_pixels; ++i) out[i] = VP8LSubPixels(in[i], in[i - 1]); (void)upper; @@ -745,7 +620,8 @@ static void PredictorSub1_C(const uint32_t* in, const uint32_t* upper, #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 num_pixels, \ + uint32_t* WEBP_RESTRICT out) { \ int x; \ assert(upper != NULL); \ for (x = 0; x < num_pixels; ++x) { \ @@ -771,18 +647,22 @@ GENERATE_PREDICTOR_SUB(13) //------------------------------------------------------------------------------ VP8LProcessEncBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed; +VP8LProcessEncBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed_SSE; VP8LTransformColorFunc VP8LTransformColor; +VP8LTransformColorFunc VP8LTransformColor_SSE; VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms; +VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms_SSE; VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms; +VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms_SSE; VP8LFastLog2SlowFunc VP8LFastLog2Slow; -VP8LFastLog2SlowFunc VP8LFastSLog2Slow; +VP8LFastSLog2SlowFunc VP8LFastSLog2Slow; VP8LCostFunc VP8LExtraCost; -VP8LCostCombinedFunc VP8LExtraCostCombined; VP8LCombinedShannonEntropyFunc VP8LCombinedShannonEntropy; +VP8LShannonEntropyFunc VP8LShannonEntropy; VP8LGetEntropyUnrefinedFunc VP8LGetEntropyUnrefined; VP8LGetCombinedEntropyUnrefinedFunc VP8LGetCombinedEntropyUnrefined; @@ -792,13 +672,16 @@ VP8LAddVectorEqFunc VP8LAddVectorEq; VP8LVectorMismatchFunc VP8LVectorMismatch; VP8LBundleColorMapFunc VP8LBundleColorMap; +VP8LBundleColorMapFunc VP8LBundleColorMap_SSE; VP8LPredictorAddSubFunc VP8LPredictorsSub[16]; VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16]; +VP8LPredictorAddSubFunc VP8LPredictorsSub_SSE[16]; extern VP8CPUInfo VP8GetCPUInfo; extern void VP8LEncDspInitSSE2(void); extern void VP8LEncDspInitSSE41(void); +extern void VP8LEncDspInitAVX2(void); extern void VP8LEncDspInitNEON(void); extern void VP8LEncDspInitMIPS32(void); extern void VP8LEncDspInitMIPSdspR2(void); @@ -820,8 +703,8 @@ WEBP_DSP_INIT_FUNC(VP8LEncDspInit) { VP8LFastSLog2Slow = FastSLog2Slow_C; VP8LExtraCost = ExtraCost_C; - VP8LExtraCostCombined = ExtraCostCombined_C; VP8LCombinedShannonEntropy = CombinedShannonEntropy_C; + VP8LShannonEntropy = ShannonEntropy_C; VP8LGetEntropyUnrefined = GetEntropyUnrefined_C; VP8LGetCombinedEntropyUnrefined = GetCombinedEntropyUnrefined_C; @@ -874,6 +757,11 @@ WEBP_DSP_INIT_FUNC(VP8LEncDspInit) { #if defined(WEBP_HAVE_SSE41) if (VP8GetCPUInfo(kSSE4_1)) { VP8LEncDspInitSSE41(); +#if defined(WEBP_HAVE_AVX2) + if (VP8GetCPUInfo(kAVX2)) { + VP8LEncDspInitAVX2(); + } +#endif } #endif } @@ -909,8 +797,8 @@ WEBP_DSP_INIT_FUNC(VP8LEncDspInit) { assert(VP8LFastLog2Slow != NULL); assert(VP8LFastSLog2Slow != NULL); assert(VP8LExtraCost != NULL); - assert(VP8LExtraCostCombined != NULL); assert(VP8LCombinedShannonEntropy != NULL); + assert(VP8LShannonEntropy != NULL); assert(VP8LGetEntropyUnrefined != NULL); assert(VP8LGetCombinedEntropyUnrefined != NULL); assert(VP8LAddVector != NULL); diff --git a/3rdparty/libwebp/src/dsp/lossless_enc_avx2.c b/3rdparty/libwebp/src/dsp/lossless_enc_avx2.c new file mode 100644 index 0000000000..32f9fae019 --- /dev/null +++ b/3rdparty/libwebp/src/dsp/lossless_enc_avx2.c @@ -0,0 +1,736 @@ +// Copyright 2025 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// AVX2 variant of methods for lossless encoder +// +// Author: Vincent Rabaud (vrabaud@google.com) + +#include "src/dsp/dsp.h" + +#if defined(WEBP_USE_AVX2) +#include +#include + +#include +#include + +#include "src/dsp/cpu.h" +#include "src/dsp/lossless.h" +#include "src/dsp/lossless_common.h" +#include "src/utils/utils.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" + +//------------------------------------------------------------------------------ +// Subtract-Green Transform + +static void SubtractGreenFromBlueAndRed_AVX2(uint32_t* argb_data, + int num_pixels) { + int i; + const __m256i kCstShuffle = _mm256_set_epi8( + -1, 29, -1, 29, -1, 25, -1, 25, -1, 21, -1, 21, -1, 17, -1, 17, -1, 13, + -1, 13, -1, 9, -1, 9, -1, 5, -1, 5, -1, 1, -1, 1); + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i in = _mm256_loadu_si256((__m256i*)&argb_data[i]); // argb + const __m256i in_0g0g = _mm256_shuffle_epi8(in, kCstShuffle); + const __m256i out = _mm256_sub_epi8(in, in_0g0g); + _mm256_storeu_si256((__m256i*)&argb_data[i], out); + } + // fallthrough and finish off with plain-SSE + if (i != num_pixels) { + VP8LSubtractGreenFromBlueAndRed_SSE(argb_data + i, num_pixels - i); + } +} + +//------------------------------------------------------------------------------ +// Color Transform + +// For sign-extended multiplying constants, pre-shifted by 5: +#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5) + +#define MK_CST_16(HI, LO) \ + _mm256_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff))) + +static void TransformColor_AVX2(const VP8LMultipliers* WEBP_RESTRICT const m, + uint32_t* WEBP_RESTRICT argb_data, + int num_pixels) { + const __m256i mults_rb = + MK_CST_16(CST_5b(m->green_to_red), CST_5b(m->green_to_blue)); + const __m256i mults_b2 = MK_CST_16(CST_5b(m->red_to_blue), 0); + const __m256i mask_rb = _mm256_set1_epi32(0x00ff00ff); // red-blue masks + const __m256i kCstShuffle = _mm256_set_epi8( + 29, -1, 29, -1, 25, -1, 25, -1, 21, -1, 21, -1, 17, -1, 17, -1, 13, -1, + 13, -1, 9, -1, 9, -1, 5, -1, 5, -1, 1, -1, 1, -1); + int i; + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i in = _mm256_loadu_si256((__m256i*)&argb_data[i]); // argb + const __m256i A = _mm256_shuffle_epi8(in, kCstShuffle); // g0g0 + const __m256i B = _mm256_mulhi_epi16(A, mults_rb); // x dr x db1 + const __m256i C = _mm256_slli_epi16(in, 8); // r 0 b 0 + const __m256i D = _mm256_mulhi_epi16(C, mults_b2); // x db2 0 0 + const __m256i E = _mm256_srli_epi32(D, 16); // 0 0 x db2 + const __m256i F = _mm256_add_epi8(E, B); // x dr x db + const __m256i G = _mm256_and_si256(F, mask_rb); // 0 dr 0 db + const __m256i out = _mm256_sub_epi8(in, G); + _mm256_storeu_si256((__m256i*)&argb_data[i], out); + } + // fallthrough and finish off with plain-C + if (i != num_pixels) { + VP8LTransformColor_SSE(m, argb_data + i, num_pixels - i); + } +} + +//------------------------------------------------------------------------------ +#define SPAN 16 +static void CollectColorBlueTransforms_AVX2(const uint32_t* WEBP_RESTRICT argb, + int stride, int tile_width, + int tile_height, int green_to_blue, + int red_to_blue, uint32_t histo[]) { + const __m256i mult = + MK_CST_16(CST_5b(red_to_blue) + 256, CST_5b(green_to_blue)); + const __m256i perm = _mm256_setr_epi8( + -1, 1, -1, 2, -1, 5, -1, 6, -1, 9, -1, 10, -1, 13, -1, 14, -1, 17, -1, 18, + -1, 21, -1, 22, -1, 25, -1, 26, -1, 29, -1, 30); + if (tile_width >= 8) { + int y, i; + for (y = 0; y < tile_height; ++y) { + uint8_t values[32]; + const uint32_t* const src = argb + y * stride; + const __m256i A1 = _mm256_loadu_si256((const __m256i*)src); + const __m256i B1 = _mm256_shuffle_epi8(A1, perm); + const __m256i C1 = _mm256_mulhi_epi16(B1, mult); + const __m256i D1 = _mm256_sub_epi16(A1, C1); + __m256i E = _mm256_add_epi16(_mm256_srli_epi32(D1, 16), D1); + int x; + for (x = 8; x + 8 <= tile_width; x += 8) { + const __m256i A2 = _mm256_loadu_si256((const __m256i*)(src + x)); + __m256i B2, C2, D2; + _mm256_storeu_si256((__m256i*)values, E); + for (i = 0; i < 32; i += 4) ++histo[values[i]]; + B2 = _mm256_shuffle_epi8(A2, perm); + C2 = _mm256_mulhi_epi16(B2, mult); + D2 = _mm256_sub_epi16(A2, C2); + E = _mm256_add_epi16(_mm256_srli_epi32(D2, 16), D2); + } + _mm256_storeu_si256((__m256i*)values, E); + for (i = 0; i < 32; i += 4) ++histo[values[i]]; + } + } + { + const int left_over = tile_width & 7; + if (left_over > 0) { + VP8LCollectColorBlueTransforms_SSE(argb + tile_width - left_over, stride, + left_over, tile_height, green_to_blue, + red_to_blue, histo); + } + } +} + +static void CollectColorRedTransforms_AVX2(const uint32_t* WEBP_RESTRICT argb, + int stride, int tile_width, + int tile_height, int green_to_red, + uint32_t histo[]) { + const __m256i mult = MK_CST_16(0, CST_5b(green_to_red)); + const __m256i mask_g = _mm256_set1_epi32(0x0000ff00); + if (tile_width >= 8) { + int y, i; + for (y = 0; y < tile_height; ++y) { + uint8_t values[32]; + const uint32_t* const src = argb + y * stride; + const __m256i A1 = _mm256_loadu_si256((const __m256i*)src); + const __m256i B1 = _mm256_and_si256(A1, mask_g); + const __m256i C1 = _mm256_madd_epi16(B1, mult); + __m256i D = _mm256_sub_epi16(A1, C1); + int x; + for (x = 8; x + 8 <= tile_width; x += 8) { + const __m256i A2 = _mm256_loadu_si256((const __m256i*)(src + x)); + __m256i B2, C2; + _mm256_storeu_si256((__m256i*)values, D); + for (i = 2; i < 32; i += 4) ++histo[values[i]]; + B2 = _mm256_and_si256(A2, mask_g); + C2 = _mm256_madd_epi16(B2, mult); + D = _mm256_sub_epi16(A2, C2); + } + _mm256_storeu_si256((__m256i*)values, D); + for (i = 2; i < 32; i += 4) ++histo[values[i]]; + } + } + { + const int left_over = tile_width & 7; + if (left_over > 0) { + VP8LCollectColorRedTransforms_SSE(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 _mm256_add_epi32). +// But that's ok since the histogram values are less than 1<<28 (max picture +// size). +static void AddVector_AVX2(const uint32_t* WEBP_RESTRICT a, + const uint32_t* WEBP_RESTRICT b, + uint32_t* WEBP_RESTRICT out, int size) { + int i = 0; + int aligned_size = size & ~31; + // Size is, at minimum, NUM_DISTANCE_CODES (40) and may be as large as + // NUM_LITERAL_CODES (256) + NUM_LENGTH_CODES (24) + (0 or a non-zero power of + // 2). See the usage in VP8LHistogramAdd(). + assert(size >= 32); + assert(size % 2 == 0); + + do { + const __m256i a0 = _mm256_loadu_si256((const __m256i*)&a[i + 0]); + const __m256i a1 = _mm256_loadu_si256((const __m256i*)&a[i + 8]); + const __m256i a2 = _mm256_loadu_si256((const __m256i*)&a[i + 16]); + const __m256i a3 = _mm256_loadu_si256((const __m256i*)&a[i + 24]); + const __m256i b0 = _mm256_loadu_si256((const __m256i*)&b[i + 0]); + const __m256i b1 = _mm256_loadu_si256((const __m256i*)&b[i + 8]); + const __m256i b2 = _mm256_loadu_si256((const __m256i*)&b[i + 16]); + const __m256i b3 = _mm256_loadu_si256((const __m256i*)&b[i + 24]); + _mm256_storeu_si256((__m256i*)&out[i + 0], _mm256_add_epi32(a0, b0)); + _mm256_storeu_si256((__m256i*)&out[i + 8], _mm256_add_epi32(a1, b1)); + _mm256_storeu_si256((__m256i*)&out[i + 16], _mm256_add_epi32(a2, b2)); + _mm256_storeu_si256((__m256i*)&out[i + 24], _mm256_add_epi32(a3, b3)); + i += 32; + } while (i != aligned_size); + + if ((size & 16) != 0) { + const __m256i a0 = _mm256_loadu_si256((const __m256i*)&a[i + 0]); + const __m256i a1 = _mm256_loadu_si256((const __m256i*)&a[i + 8]); + const __m256i b0 = _mm256_loadu_si256((const __m256i*)&b[i + 0]); + const __m256i b1 = _mm256_loadu_si256((const __m256i*)&b[i + 8]); + _mm256_storeu_si256((__m256i*)&out[i + 0], _mm256_add_epi32(a0, b0)); + _mm256_storeu_si256((__m256i*)&out[i + 8], _mm256_add_epi32(a1, b1)); + i += 16; + } + + size &= 15; + if (size == 8) { + const __m256i a0 = _mm256_loadu_si256((const __m256i*)&a[i]); + const __m256i b0 = _mm256_loadu_si256((const __m256i*)&b[i]); + _mm256_storeu_si256((__m256i*)&out[i], _mm256_add_epi32(a0, b0)); + } else { + for (; size--; ++i) { + out[i] = a[i] + b[i]; + } + } +} + +static void AddVectorEq_AVX2(const uint32_t* WEBP_RESTRICT a, + uint32_t* WEBP_RESTRICT out, int size) { + int i = 0; + int aligned_size = size & ~31; + // Size is, at minimum, NUM_DISTANCE_CODES (40) and may be as large as + // NUM_LITERAL_CODES (256) + NUM_LENGTH_CODES (24) + (0 or a non-zero power of + // 2). See the usage in VP8LHistogramAdd(). + assert(size >= 32); + assert(size % 2 == 0); + + do { + const __m256i a0 = _mm256_loadu_si256((const __m256i*)&a[i + 0]); + const __m256i a1 = _mm256_loadu_si256((const __m256i*)&a[i + 8]); + const __m256i a2 = _mm256_loadu_si256((const __m256i*)&a[i + 16]); + const __m256i a3 = _mm256_loadu_si256((const __m256i*)&a[i + 24]); + const __m256i b0 = _mm256_loadu_si256((const __m256i*)&out[i + 0]); + const __m256i b1 = _mm256_loadu_si256((const __m256i*)&out[i + 8]); + const __m256i b2 = _mm256_loadu_si256((const __m256i*)&out[i + 16]); + const __m256i b3 = _mm256_loadu_si256((const __m256i*)&out[i + 24]); + _mm256_storeu_si256((__m256i*)&out[i + 0], _mm256_add_epi32(a0, b0)); + _mm256_storeu_si256((__m256i*)&out[i + 8], _mm256_add_epi32(a1, b1)); + _mm256_storeu_si256((__m256i*)&out[i + 16], _mm256_add_epi32(a2, b2)); + _mm256_storeu_si256((__m256i*)&out[i + 24], _mm256_add_epi32(a3, b3)); + i += 32; + } while (i != aligned_size); + + if ((size & 16) != 0) { + const __m256i a0 = _mm256_loadu_si256((const __m256i*)&a[i + 0]); + const __m256i a1 = _mm256_loadu_si256((const __m256i*)&a[i + 8]); + const __m256i b0 = _mm256_loadu_si256((const __m256i*)&out[i + 0]); + const __m256i b1 = _mm256_loadu_si256((const __m256i*)&out[i + 8]); + _mm256_storeu_si256((__m256i*)&out[i + 0], _mm256_add_epi32(a0, b0)); + _mm256_storeu_si256((__m256i*)&out[i + 8], _mm256_add_epi32(a1, b1)); + i += 16; + } + + size &= 15; + if (size == 8) { + const __m256i a0 = _mm256_loadu_si256((const __m256i*)&a[i]); + const __m256i b0 = _mm256_loadu_si256((const __m256i*)&out[i]); + _mm256_storeu_si256((__m256i*)&out[i], _mm256_add_epi32(a0, b0)); + } else { + for (; size--; ++i) { + out[i] += a[i]; + } + } +} + +//------------------------------------------------------------------------------ +// Entropy + +#if !defined(WEBP_HAVE_SLOW_CLZ_CTZ) + +static uint64_t CombinedShannonEntropy_AVX2(const uint32_t X[256], + const uint32_t Y[256]) { + int i; + uint64_t retval = 0; + uint32_t sumX = 0, sumXY = 0; + const __m256i zero = _mm256_setzero_si256(); + + for (i = 0; i < 256; i += 32) { + const __m256i x0 = _mm256_loadu_si256((const __m256i*)(X + i + 0)); + const __m256i y0 = _mm256_loadu_si256((const __m256i*)(Y + i + 0)); + const __m256i x1 = _mm256_loadu_si256((const __m256i*)(X + i + 8)); + const __m256i y1 = _mm256_loadu_si256((const __m256i*)(Y + i + 8)); + const __m256i x2 = _mm256_loadu_si256((const __m256i*)(X + i + 16)); + const __m256i y2 = _mm256_loadu_si256((const __m256i*)(Y + i + 16)); + const __m256i x3 = _mm256_loadu_si256((const __m256i*)(X + i + 24)); + const __m256i y3 = _mm256_loadu_si256((const __m256i*)(Y + i + 24)); + const __m256i x4 = _mm256_packs_epi16(_mm256_packs_epi32(x0, x1), + _mm256_packs_epi32(x2, x3)); + const __m256i y4 = _mm256_packs_epi16(_mm256_packs_epi32(y0, y1), + _mm256_packs_epi32(y2, y3)); + // Packed pixels are actually in order: ... 17 16 12 11 10 9 8 3 2 1 0 + const __m256i x5 = _mm256_permutevar8x32_epi32( + x4, _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0)); + const __m256i y5 = _mm256_permutevar8x32_epi32( + y4, _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0)); + const uint32_t mx = + (uint32_t)_mm256_movemask_epi8(_mm256_cmpgt_epi8(x5, zero)); + uint32_t my = + (uint32_t)_mm256_movemask_epi8(_mm256_cmpgt_epi8(y5, zero)) | mx; + while (my) { + const int32_t j = BitsCtz(my); + uint32_t 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) - retval; + return retval; +} + +#else + +#define DONT_USE_COMBINED_SHANNON_ENTROPY_SSE2_FUNC // won't be faster + +#endif + +//------------------------------------------------------------------------------ + +static int VectorMismatch_AVX2(const uint32_t* const array1, + const uint32_t* const array2, int length) { + int match_len; + + if (length >= 24) { + __m256i A0 = _mm256_loadu_si256((const __m256i*)&array1[0]); + __m256i A1 = _mm256_loadu_si256((const __m256i*)&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 __m256i cmpA = _mm256_cmpeq_epi32(A0, A1); + const __m256i B0 = + _mm256_loadu_si256((const __m256i*)&array1[match_len + 8]); + const __m256i B1 = + _mm256_loadu_si256((const __m256i*)&array2[match_len + 8]); + if ((uint32_t)_mm256_movemask_epi8(cmpA) != 0xffffffff) break; + match_len += 8; + + { + const __m256i cmpB = _mm256_cmpeq_epi32(B0, B1); + A0 = _mm256_loadu_si256((const __m256i*)&array1[match_len + 8]); + A1 = _mm256_loadu_si256((const __m256i*)&array2[match_len + 8]); + if ((uint32_t)_mm256_movemask_epi8(cmpB) != 0xffffffff) break; + match_len += 8; + } + } while (match_len + 24 < length); + } else { + match_len = 0; + // Unroll the potential first two loops. + if (length >= 8 && + (uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi32( + _mm256_loadu_si256((const __m256i*)&array1[0]), + _mm256_loadu_si256((const __m256i*)&array2[0]))) == 0xffffffff) { + match_len = 8; + if (length >= 16 && + (uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi32( + _mm256_loadu_si256((const __m256i*)&array1[8]), + _mm256_loadu_si256((const __m256i*)&array2[8]))) == 0xffffffff) { + match_len = 16; + } + } + } + + 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_AVX2(const uint8_t* WEBP_RESTRICT const row, + int width, int xbits, + uint32_t* WEBP_RESTRICT dst) { + int x = 0; + assert(xbits >= 0); + assert(xbits <= 3); + switch (xbits) { + case 0: { + const __m256i ff = _mm256_set1_epi16((short)0xff00); + const __m256i zero = _mm256_setzero_si256(); + // Store 0xff000000 | (row[x] << 8). + for (x = 0; x + 32 <= width; x += 32, dst += 32) { + const __m256i in = _mm256_loadu_si256((const __m256i*)&row[x]); + const __m256i in_lo = _mm256_unpacklo_epi8(zero, in); + const __m256i dst0 = _mm256_unpacklo_epi16(in_lo, ff); + const __m256i dst1 = _mm256_unpackhi_epi16(in_lo, ff); + const __m256i in_hi = _mm256_unpackhi_epi8(zero, in); + const __m256i dst2 = _mm256_unpacklo_epi16(in_hi, ff); + const __m256i dst3 = _mm256_unpackhi_epi16(in_hi, ff); + _mm256_storeu2_m128i((__m128i*)&dst[16], (__m128i*)&dst[0], dst0); + _mm256_storeu2_m128i((__m128i*)&dst[20], (__m128i*)&dst[4], dst1); + _mm256_storeu2_m128i((__m128i*)&dst[24], (__m128i*)&dst[8], dst2); + _mm256_storeu2_m128i((__m128i*)&dst[28], (__m128i*)&dst[12], dst3); + } + break; + } + case 1: { + const __m256i ff = _mm256_set1_epi16((short)0xff00); + const __m256i mul = _mm256_set1_epi16(0x110); + for (x = 0; x + 32 <= width; x += 32, dst += 16) { + // 0a0b | (where a/b are 4 bits). + const __m256i in = _mm256_loadu_si256((const __m256i*)&row[x]); + const __m256i tmp = _mm256_mullo_epi16(in, mul); // aba0 + const __m256i pack = _mm256_and_si256(tmp, ff); // ab00 + const __m256i dst0 = _mm256_unpacklo_epi16(pack, ff); + const __m256i dst1 = _mm256_unpackhi_epi16(pack, ff); + _mm256_storeu2_m128i((__m128i*)&dst[8], (__m128i*)&dst[0], dst0); + _mm256_storeu2_m128i((__m128i*)&dst[12], (__m128i*)&dst[4], dst1); + } + break; + } + case 2: { + const __m256i mask_or = _mm256_set1_epi32((int)0xff000000); + const __m256i mul_cst = _mm256_set1_epi16(0x0104); + const __m256i mask_mul = _mm256_set1_epi16(0x0f00); + for (x = 0; x + 32 <= width; x += 32, dst += 8) { + // 000a000b000c000d | (where a/b/c/d are 2 bits). + const __m256i in = _mm256_loadu_si256((const __m256i*)&row[x]); + const __m256i mul = + _mm256_mullo_epi16(in, mul_cst); // 00ab00b000cd00d0 + const __m256i tmp = + _mm256_and_si256(mul, mask_mul); // 00ab000000cd0000 + const __m256i shift = _mm256_srli_epi32(tmp, 12); // 00000000ab000000 + const __m256i pack = _mm256_or_si256(shift, tmp); // 00000000abcd0000 + // Convert to 0xff00**00. + const __m256i res = _mm256_or_si256(pack, mask_or); + _mm256_storeu_si256((__m256i*)dst, res); + } + break; + } + default: { + assert(xbits == 3); + for (x = 0; x + 32 <= width; x += 32, dst += 4) { + // 0000000a00000000b... | (where a/b are 1 bit). + const __m256i in = _mm256_loadu_si256((const __m256i*)&row[x]); + const __m256i shift = _mm256_slli_epi64(in, 7); + const uint32_t move = _mm256_movemask_epi8(shift); + dst[0] = 0xff000000 | ((move & 0xff) << 8); + dst[1] = 0xff000000 | (move & 0xff00); + dst[2] = 0xff000000 | ((move & 0xff0000) >> 8); + dst[3] = 0xff000000 | ((move & 0xff000000) >> 16); + } + break; + } + } + if (x != width) { + VP8LBundleColorMap_SSE(row + x, width - x, xbits, dst); + } +} + +//------------------------------------------------------------------------------ +// Batch version of Predictor Transform subtraction + +static WEBP_INLINE void Average2_m256i(const __m256i* const a0, + const __m256i* const a1, + __m256i* const avg) { + // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1) + const __m256i ones = _mm256_set1_epi8(1); + const __m256i avg1 = _mm256_avg_epu8(*a0, *a1); + const __m256i one = _mm256_and_si256(_mm256_xor_si256(*a0, *a1), ones); + *avg = _mm256_sub_epi8(avg1, one); +} + +// Predictor0: ARGB_BLACK. +static void PredictorSub0_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i; + const __m256i black = _mm256_set1_epi32((int)ARGB_BLACK); + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + const __m256i res = _mm256_sub_epi8(src, black); + _mm256_storeu_si256((__m256i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_SSE[0](in + i, NULL, num_pixels - i, out + i); + } + (void)upper; +} + +#define GENERATE_PREDICTOR_1(X, IN) \ + static void PredictorSub##X##_AVX2( \ + const uint32_t* const in, const uint32_t* const upper, int num_pixels, \ + uint32_t* WEBP_RESTRICT const out) { \ + int i; \ + for (i = 0; i + 8 <= num_pixels; i += 8) { \ + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ + const __m256i pred = _mm256_loadu_si256((const __m256i*)&(IN)); \ + const __m256i res = _mm256_sub_epi8(src, pred); \ + _mm256_storeu_si256((__m256i*)&out[i], res); \ + } \ + if (i != num_pixels) { \ + VP8LPredictorsSub_SSE[(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_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i; + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i L = _mm256_loadu_si256((const __m256i*)&in[i - 1]); + const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); + const __m256i TR = _mm256_loadu_si256((const __m256i*)&upper[i + 1]); + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + __m256i avg, pred, res; + Average2_m256i(&L, &TR, &avg); + Average2_m256i(&avg, &T, &pred); + res = _mm256_sub_epi8(src, pred); + _mm256_storeu_si256((__m256i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_SSE[5](in + i, upper + i, num_pixels - i, out + i); + } +} + +#define GENERATE_PREDICTOR_2(X, A, B) \ + static void PredictorSub##X##_AVX2(const uint32_t* in, \ + const uint32_t* upper, int num_pixels, \ + uint32_t* WEBP_RESTRICT out) { \ + int i; \ + for (i = 0; i + 8 <= num_pixels; i += 8) { \ + const __m256i tA = _mm256_loadu_si256((const __m256i*)&(A)); \ + const __m256i tB = _mm256_loadu_si256((const __m256i*)&(B)); \ + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ + __m256i pred, res; \ + Average2_m256i(&tA, &tB, &pred); \ + res = _mm256_sub_epi8(src, pred); \ + _mm256_storeu_si256((__m256i*)&out[i], res); \ + } \ + if (i != num_pixels) { \ + VP8LPredictorsSub_SSE[(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_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i; + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i L = _mm256_loadu_si256((const __m256i*)&in[i - 1]); + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + const __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); + const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); + const __m256i TR = _mm256_loadu_si256((const __m256i*)&upper[i + 1]); + __m256i avgTTR, avgLTL, avg, res; + Average2_m256i(&T, &TR, &avgTTR); + Average2_m256i(&L, &TL, &avgLTL); + Average2_m256i(&avgTTR, &avgLTL, &avg); + res = _mm256_sub_epi8(src, avg); + _mm256_storeu_si256((__m256i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_SSE[10](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Predictor11: select. +static void GetSumAbsDiff32_AVX2(const __m256i* const A, const __m256i* const B, + __m256i* 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 __m256i A_lo = _mm256_unpacklo_epi32(*A, *A); + const __m256i B_lo = _mm256_unpacklo_epi32(*B, *A); + const __m256i A_hi = _mm256_unpackhi_epi32(*A, *A); + const __m256i B_hi = _mm256_unpackhi_epi32(*B, *A); + const __m256i s_lo = _mm256_sad_epu8(A_lo, B_lo); + const __m256i s_hi = _mm256_sad_epu8(A_hi, B_hi); + *out = _mm256_packs_epi32(s_lo, s_hi); +} + +static void PredictorSub11_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i; + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i L = _mm256_loadu_si256((const __m256i*)&in[i - 1]); + const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); + const __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + __m256i pa, pb; + GetSumAbsDiff32_AVX2(&T, &TL, &pa); // pa = sum |T-TL| + GetSumAbsDiff32_AVX2(&L, &TL, &pb); // pb = sum |L-TL| + { + const __m256i mask = _mm256_cmpgt_epi32(pb, pa); + const __m256i A = _mm256_and_si256(mask, L); + const __m256i B = _mm256_andnot_si256(mask, T); + const __m256i pred = _mm256_or_si256(A, B); // pred = (L > T)? L : T + const __m256i res = _mm256_sub_epi8(src, pred); + _mm256_storeu_si256((__m256i*)&out[i], res); + } + } + if (i != num_pixels) { + VP8LPredictorsSub_SSE[11](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Predictor12: ClampedSubSubtractFull. +static void PredictorSub12_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i; + const __m256i zero = _mm256_setzero_si256(); + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + const __m256i L = _mm256_loadu_si256((const __m256i*)&in[i - 1]); + const __m256i L_lo = _mm256_unpacklo_epi8(L, zero); + const __m256i L_hi = _mm256_unpackhi_epi8(L, zero); + const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); + const __m256i T_lo = _mm256_unpacklo_epi8(T, zero); + const __m256i T_hi = _mm256_unpackhi_epi8(T, zero); + const __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); + const __m256i TL_lo = _mm256_unpacklo_epi8(TL, zero); + const __m256i TL_hi = _mm256_unpackhi_epi8(TL, zero); + const __m256i diff_lo = _mm256_sub_epi16(T_lo, TL_lo); + const __m256i diff_hi = _mm256_sub_epi16(T_hi, TL_hi); + const __m256i pred_lo = _mm256_add_epi16(L_lo, diff_lo); + const __m256i pred_hi = _mm256_add_epi16(L_hi, diff_hi); + const __m256i pred = _mm256_packus_epi16(pred_lo, pred_hi); + const __m256i res = _mm256_sub_epi8(src, pred); + _mm256_storeu_si256((__m256i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_SSE[12](in + i, upper + i, num_pixels - i, out + i); + } +} + +// Predictors13: ClampedAddSubtractHalf +static void PredictorSub13_AVX2(const uint32_t* in, const uint32_t* upper, + int num_pixels, uint32_t* WEBP_RESTRICT out) { + int i; + const __m256i zero = _mm256_setzero_si256(); + for (i = 0; i + 8 <= num_pixels; i += 8) { + const __m256i L = _mm256_loadu_si256((const __m256i*)&in[i - 1]); + const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); + const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); + const __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); + // lo. + const __m256i L_lo = _mm256_unpacklo_epi8(L, zero); + const __m256i T_lo = _mm256_unpacklo_epi8(T, zero); + const __m256i TL_lo = _mm256_unpacklo_epi8(TL, zero); + const __m256i sum_lo = _mm256_add_epi16(T_lo, L_lo); + const __m256i avg_lo = _mm256_srli_epi16(sum_lo, 1); + const __m256i A1_lo = _mm256_sub_epi16(avg_lo, TL_lo); + const __m256i bit_fix_lo = _mm256_cmpgt_epi16(TL_lo, avg_lo); + const __m256i A2_lo = _mm256_sub_epi16(A1_lo, bit_fix_lo); + const __m256i A3_lo = _mm256_srai_epi16(A2_lo, 1); + const __m256i A4_lo = _mm256_add_epi16(avg_lo, A3_lo); + // hi. + const __m256i L_hi = _mm256_unpackhi_epi8(L, zero); + const __m256i T_hi = _mm256_unpackhi_epi8(T, zero); + const __m256i TL_hi = _mm256_unpackhi_epi8(TL, zero); + const __m256i sum_hi = _mm256_add_epi16(T_hi, L_hi); + const __m256i avg_hi = _mm256_srli_epi16(sum_hi, 1); + const __m256i A1_hi = _mm256_sub_epi16(avg_hi, TL_hi); + const __m256i bit_fix_hi = _mm256_cmpgt_epi16(TL_hi, avg_hi); + const __m256i A2_hi = _mm256_sub_epi16(A1_hi, bit_fix_hi); + const __m256i A3_hi = _mm256_srai_epi16(A2_hi, 1); + const __m256i A4_hi = _mm256_add_epi16(avg_hi, A3_hi); + + const __m256i pred = _mm256_packus_epi16(A4_lo, A4_hi); + const __m256i res = _mm256_sub_epi8(src, pred); + _mm256_storeu_si256((__m256i*)&out[i], res); + } + if (i != num_pixels) { + VP8LPredictorsSub_SSE[13](in + i, upper + i, num_pixels - i, out + i); + } +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8LEncDspInitAVX2(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitAVX2(void) { + VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_AVX2; + VP8LTransformColor = TransformColor_AVX2; + VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_AVX2; + VP8LCollectColorRedTransforms = CollectColorRedTransforms_AVX2; + VP8LAddVector = AddVector_AVX2; + VP8LAddVectorEq = AddVectorEq_AVX2; + VP8LCombinedShannonEntropy = CombinedShannonEntropy_AVX2; + VP8LVectorMismatch = VectorMismatch_AVX2; + VP8LBundleColorMap = BundleColorMap_AVX2; + + VP8LPredictorsSub[0] = PredictorSub0_AVX2; + VP8LPredictorsSub[1] = PredictorSub1_AVX2; + VP8LPredictorsSub[2] = PredictorSub2_AVX2; + VP8LPredictorsSub[3] = PredictorSub3_AVX2; + VP8LPredictorsSub[4] = PredictorSub4_AVX2; + VP8LPredictorsSub[5] = PredictorSub5_AVX2; + VP8LPredictorsSub[6] = PredictorSub6_AVX2; + VP8LPredictorsSub[7] = PredictorSub7_AVX2; + VP8LPredictorsSub[8] = PredictorSub8_AVX2; + VP8LPredictorsSub[9] = PredictorSub9_AVX2; + VP8LPredictorsSub[10] = PredictorSub10_AVX2; + VP8LPredictorsSub[11] = PredictorSub11_AVX2; + VP8LPredictorsSub[12] = PredictorSub12_AVX2; + VP8LPredictorsSub[13] = PredictorSub13_AVX2; + VP8LPredictorsSub[14] = PredictorSub0_AVX2; // <- padding security sentinels + VP8LPredictorsSub[15] = PredictorSub0_AVX2; +} + +#else // !WEBP_USE_AVX2 + +WEBP_DSP_INIT_STUB(VP8LEncDspInitAVX2) + +#endif // WEBP_USE_AVX2 diff --git a/3rdparty/libwebp/src/dsp/lossless_enc_mips32.c b/3rdparty/libwebp/src/dsp/lossless_enc_mips32.c index e10f12da9d..bf2ed0acbb 100644 --- a/3rdparty/libwebp/src/dsp/lossless_enc_mips32.c +++ b/3rdparty/libwebp/src/dsp/lossless_enc_mips32.c @@ -23,12 +23,12 @@ #include #include -static float FastSLog2Slow_MIPS32(uint32_t v) { +static uint64_t FastSLog2Slow_MIPS32(uint32_t v) { assert(v >= LOG_LOOKUP_IDX_MAX); if (v < APPROX_LOG_WITH_CORRECTION_MAX) { - uint32_t log_cnt, y, correction; + uint32_t log_cnt, y; + uint64_t correction; const int c24 = 24; - const float v_f = (float)v; uint32_t temp; // Xf = 256 = 2^8 @@ -49,22 +49,23 @@ static float FastSLog2Slow_MIPS32(uint32_t 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; + correction = LOG_2_RECIPROCAL_FIXED * (v & (y - 1)); + return (uint64_t)v * (kLog2Table[temp] + + ((uint64_t)log_cnt << LOG_2_PRECISION_BITS)) + + correction; } else { - return (float)(LOG_2_RECIPROCAL * v * log((double)v)); + return (uint64_t)(LOG_2_RECIPROCAL_FIXED_DOUBLE * v * log((double)v) + .5); } } -static float FastLog2Slow_MIPS32(uint32_t v) { +static uint32_t 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 log_2; uint32_t temp; __asm__ volatile( @@ -78,17 +79,16 @@ static float FastLog2Slow_MIPS32(uint32_t v) { : [c24]"r"(c24), [v]"r"(v) ); - log_2 = kLog2Table[temp] + log_cnt; + log_2 = kLog2Table[temp] + (log_cnt << LOG_2_PRECISION_BITS); 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; + const uint64_t correction = LOG_2_RECIPROCAL_FIXED * (v & (y - 1)); + log_2 += (uint32_t)DivRound(correction, v); } - return (float)log_2; + return log_2; } else { - return (float)(LOG_2_RECIPROCAL * log((double)v)); + return (uint32_t)(LOG_2_RECIPROCAL_FIXED_DOUBLE * log((double)v) + .5); } } @@ -133,59 +133,6 @@ static uint32_t ExtraCost_MIPS32(const uint32_t* const population, int length) { return ((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 cost; -static uint32_t 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 ((int64_t)temp0 << 32 | temp1); -} - #define HUFFMAN_COST_PASS \ __asm__ volatile( \ "sll %[temp1], %[temp0], 3 \n\t" \ @@ -215,8 +162,10 @@ static uint32_t ExtraCostCombined_MIPS32(const uint32_t* const X, // 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) { + uint32_t val, int i, uint32_t* WEBP_RESTRICT const val_prev, + int* WEBP_RESTRICT const i_prev, + VP8LBitEntropy* WEBP_RESTRICT const bit_entropy, + VP8LStreaks* WEBP_RESTRICT const stats) { int* const pstreaks = &stats->streaks[0][0]; int* const pcnts = &stats->counts[0]; int temp0, temp1, temp2, temp3; @@ -227,7 +176,7 @@ static WEBP_INLINE void GetEntropyUnrefinedHelper( bit_entropy->sum += (*val_prev) * streak; bit_entropy->nonzeros += streak; bit_entropy->nonzero_code = *i_prev; - bit_entropy->entropy -= VP8LFastSLog2(*val_prev) * streak; + bit_entropy->entropy += VP8LFastSLog2(*val_prev) * streak; if (bit_entropy->max_val < *val_prev) { bit_entropy->max_val = *val_prev; } @@ -241,9 +190,10 @@ static WEBP_INLINE void GetEntropyUnrefinedHelper( *i_prev = i; } -static void GetEntropyUnrefined_MIPS32(const uint32_t X[], int length, - VP8LBitEntropy* const bit_entropy, - VP8LStreaks* const stats) { +static void GetEntropyUnrefined_MIPS32( + const uint32_t X[], int length, + VP8LBitEntropy* WEBP_RESTRICT const bit_entropy, + VP8LStreaks* WEBP_RESTRICT const stats) { int i; int i_prev = 0; uint32_t x_prev = X[0]; @@ -259,14 +209,13 @@ static void GetEntropyUnrefined_MIPS32(const uint32_t X[], int length, } GetEntropyUnrefinedHelper(0, i, &x_prev, &i_prev, bit_entropy, stats); - bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum); + bit_entropy->entropy = VP8LFastSLog2(bit_entropy->sum) - bit_entropy->entropy; } -static void GetCombinedEntropyUnrefined_MIPS32(const uint32_t X[], - const uint32_t Y[], - int length, - VP8LBitEntropy* const entropy, - VP8LStreaks* const stats) { +static void GetCombinedEntropyUnrefined_MIPS32( + const uint32_t X[], const uint32_t Y[], int length, + VP8LBitEntropy* WEBP_RESTRICT const entropy, + VP8LStreaks* WEBP_RESTRICT const stats) { int i = 1; int i_prev = 0; uint32_t xy_prev = X[0] + Y[0]; @@ -282,7 +231,7 @@ static void GetCombinedEntropyUnrefined_MIPS32(const uint32_t X[], } GetEntropyUnrefinedHelper(0, i, &xy_prev, &i_prev, entropy, stats); - entropy->entropy += VP8LFastSLog2(entropy->sum); + entropy->entropy = VP8LFastSLog2(entropy->sum) - entropy->entropy; } #define ASM_START \ @@ -296,7 +245,7 @@ static void GetCombinedEntropyUnrefined_MIPS32(const uint32_t X[], // A..D - offsets // E - temp variable to tell macro // if pointer should be incremented -// literal_ and successive histograms could be unaligned +// '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" \ @@ -344,8 +293,9 @@ static void GetCombinedEntropyUnrefined_MIPS32(const uint32_t X[], 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) { +static void AddVector_MIPS32(const uint32_t* WEBP_RESTRICT pa, + const uint32_t* WEBP_RESTRICT pb, + uint32_t* WEBP_RESTRICT pout, int size) { uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; const int end = ((size) / 4) * 4; const uint32_t* const LoopEnd = pa + end; @@ -356,7 +306,8 @@ static void AddVector_MIPS32(const uint32_t* pa, const uint32_t* pb, for (i = 0; i < size - end; ++i) pout[i] = pa[i] + pb[i]; } -static void AddVectorEq_MIPS32(const uint32_t* pa, uint32_t* pout, int size) { +static void AddVectorEq_MIPS32(const uint32_t* WEBP_RESTRICT pa, + uint32_t* WEBP_RESTRICT pout, int size) { uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; const int end = ((size) / 4) * 4; const uint32_t* const LoopEnd = pa + end; @@ -383,7 +334,6 @@ 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; diff --git a/3rdparty/libwebp/src/dsp/lossless_enc_mips_dsp_r2.c b/3rdparty/libwebp/src/dsp/lossless_enc_mips_dsp_r2.c index 5855e6ae15..b93e98bf86 100644 --- a/3rdparty/libwebp/src/dsp/lossless_enc_mips_dsp_r2.c +++ b/3rdparty/libwebp/src/dsp/lossless_enc_mips_dsp_r2.c @@ -78,13 +78,14 @@ static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred, return (uint32_t)((int)(color_pred) * color) >> 5; } -static void TransformColor_MIPSdspR2(const VP8LMultipliers* const m, - uint32_t* data, int num_pixels) { +static void TransformColor_MIPSdspR2( + const VP8LMultipliers* WEBP_RESTRICT const m, uint32_t* WEBP_RESTRICT 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_; + 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" @@ -151,10 +152,10 @@ static void TransformColor_MIPSdspR2(const VP8LMultipliers* const m, const uint32_t red = argb_ >> 16; uint32_t new_blue = argb_; new_red = red; - new_red -= ColorTransformDelta(m->green_to_red_, green); + 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 -= 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); } @@ -171,13 +172,10 @@ static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue, 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[]) { +static void CollectColorBlueTransforms_MIPSdspR2( + const uint32_t* WEBP_RESTRICT argb, int stride, + int tile_width, int tile_height, + int green_to_blue, int red_to_blue, uint32_t 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; @@ -225,12 +223,9 @@ static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red, 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[]) { +static void CollectColorRedTransforms_MIPSdspR2( + const uint32_t* WEBP_RESTRICT argb, int stride, + int tile_width, int tile_height, int green_to_red, uint32_t histo[]) { const int gtr = (green_to_red << 16) | (green_to_red & 0xffff); while (tile_height-- > 0) { int x; diff --git a/3rdparty/libwebp/src/dsp/lossless_enc_msa.c b/3rdparty/libwebp/src/dsp/lossless_enc_msa.c index 600dddfb59..722a98b2ee 100644 --- a/3rdparty/libwebp/src/dsp/lossless_enc_msa.c +++ b/3rdparty/libwebp/src/dsp/lossless_enc_msa.c @@ -48,12 +48,12 @@ dst = VSHF_UB(src, t0, mask1); \ } while (0) -static void TransformColor_MSA(const VP8LMultipliers* const m, uint32_t* data, - int num_pixels) { +static void TransformColor_MSA(const VP8LMultipliers* WEBP_RESTRICT const m, + uint32_t* WEBP_RESTRICT 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 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, diff --git a/3rdparty/libwebp/src/dsp/lossless_enc_neon.c b/3rdparty/libwebp/src/dsp/lossless_enc_neon.c index e32c7961a2..ff1858eba7 100644 --- a/3rdparty/libwebp/src/dsp/lossless_enc_neon.c +++ b/3rdparty/libwebp/src/dsp/lossless_enc_neon.c @@ -72,20 +72,21 @@ static void SubtractGreenFromBlueAndRed_NEON(uint32_t* argb_data, //------------------------------------------------------------------------------ // Color Transform -static void TransformColor_NEON(const VP8LMultipliers* const m, - uint32_t* argb_data, int num_pixels) { +static void TransformColor_NEON(const VP8LMultipliers* WEBP_RESTRICT const m, + uint32_t* WEBP_RESTRICT 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_) + 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_), + 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 diff --git a/3rdparty/libwebp/src/dsp/lossless_enc_sse2.c b/3rdparty/libwebp/src/dsp/lossless_enc_sse2.c index 66cbaab772..88276f4f6b 100644 --- a/3rdparty/libwebp/src/dsp/lossless_enc_sse2.c +++ b/3rdparty/libwebp/src/dsp/lossless_enc_sse2.c @@ -14,11 +14,17 @@ #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE2) -#include #include + +#include +#include + +#include "src/dsp/cpu.h" #include "src/dsp/lossless.h" -#include "src/dsp/common_sse2.h" #include "src/dsp/lossless_common.h" +#include "src/utils/utils.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" // For sign-extended multiplying constants, pre-shifted by 5: #define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5) @@ -49,11 +55,12 @@ static void SubtractGreenFromBlueAndRed_SSE2(uint32_t* argb_data, #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); +static void TransformColor_SSE2(const VP8LMultipliers* WEBP_RESTRICT const m, + uint32_t* WEBP_RESTRICT 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((int)0xff00ff00); // alpha-green masks const __m128i mask_rb = _mm_set1_epi32(0x00ff00ff); // red-blue masks int i; @@ -79,10 +86,11 @@ static void TransformColor_SSE2(const VP8LMultipliers* const m, //------------------------------------------------------------------------------ #define SPAN 8 -static void CollectColorBlueTransforms_SSE2(const uint32_t* argb, int stride, +static void CollectColorBlueTransforms_SSE2(const uint32_t* WEBP_RESTRICT argb, + int stride, int tile_width, int tile_height, int green_to_blue, int red_to_blue, - int histo[]) { + uint32_t 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 @@ -126,9 +134,10 @@ static void CollectColorBlueTransforms_SSE2(const uint32_t* argb, int stride, } } -static void CollectColorRedTransforms_SSE2(const uint32_t* argb, int stride, +static void CollectColorRedTransforms_SSE2(const uint32_t* WEBP_RESTRICT argb, + int stride, int tile_width, int tile_height, - int green_to_red, int histo[]) { + int green_to_red, uint32_t 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); @@ -172,75 +181,113 @@ static void CollectColorRedTransforms_SSE2(const uint32_t* argb, int stride, // 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) { +static void AddVector_SSE2(const uint32_t* WEBP_RESTRICT a, + const uint32_t* WEBP_RESTRICT b, + uint32_t* WEBP_RESTRICT out, int size) { + int i = 0; + int aligned_size = size & ~15; + // Size is, at minimum, NUM_DISTANCE_CODES (40) and may be as large as + // NUM_LITERAL_CODES (256) + NUM_LENGTH_CODES (24) + (0 or a non-zero power of + // 2). See the usage in VP8LHistogramAdd(). + assert(size >= 16); + assert(size % 2 == 0); + + do { 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 + i += 16; + } while (i != aligned_size); + + if ((size & 8) != 0) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i + 0]); + const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]); + const __m128i b0 = _mm_loadu_si128((const __m128i*)&b[i + 0]); + const __m128i b1 = _mm_loadu_si128((const __m128i*)&b[i + 4]); + _mm_storeu_si128((__m128i*)&out[i + 0], _mm_add_epi32(a0, b0)); + _mm_storeu_si128((__m128i*)&out[i + 4], _mm_add_epi32(a1, b1)); + i += 8; } - for (; i < size; ++i) { - out[i] = a[i] + b[i]; + + size &= 7; + if (size == 4) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i]); + const __m128i b0 = _mm_loadu_si128((const __m128i*)&b[i]); + _mm_storeu_si128((__m128i*)&out[i], _mm_add_epi32(a0, b0)); + } else if (size == 2) { + const __m128i a0 = _mm_loadl_epi64((const __m128i*)&a[i]); + const __m128i b0 = _mm_loadl_epi64((const __m128i*)&b[i]); + _mm_storel_epi64((__m128i*)&out[i], _mm_add_epi32(a0, b0)); } } -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) { +static void AddVectorEq_SSE2(const uint32_t* WEBP_RESTRICT a, + uint32_t* WEBP_RESTRICT out, int size) { + int i = 0; + int aligned_size = size & ~15; + // Size is, at minimum, NUM_DISTANCE_CODES (40) and may be as large as + // NUM_LITERAL_CODES (256) + NUM_LENGTH_CODES (24) + (0 or a non-zero power of + // 2). See the usage in VP8LHistogramAdd(). + assert(size >= 16); + assert(size % 2 == 0); + + do { 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 + i += 16; + } while (i != aligned_size); + + if ((size & 8) != 0) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i + 0]); + const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]); + const __m128i b0 = _mm_loadu_si128((const __m128i*)&out[i + 0]); + const __m128i b1 = _mm_loadu_si128((const __m128i*)&out[i + 4]); + _mm_storeu_si128((__m128i*)&out[i + 0], _mm_add_epi32(a0, b0)); + _mm_storeu_si128((__m128i*)&out[i + 4], _mm_add_epi32(a1, b1)); + i += 8; } - for (; i < size; ++i) { - out[i] += a[i]; + + size &= 7; + if (size == 4) { + const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i]); + const __m128i b0 = _mm_loadu_si128((const __m128i*)&out[i]); + _mm_storeu_si128((__m128i*)&out[i], _mm_add_epi32(a0, b0)); + } else if (size == 2) { + const __m128i a0 = _mm_loadl_epi64((const __m128i*)&a[i]); + const __m128i b0 = _mm_loadl_epi64((const __m128i*)&out[i]); + _mm_storel_epi64((__m128i*)&out[i], _mm_add_epi32(a0, b0)); } } -#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)) +#if !defined(WEBP_HAVE_SLOW_CLZ_CTZ) -static float CombinedShannonEntropy_SSE2(const int X[256], const int Y[256]) { +static uint64_t CombinedShannonEntropy_SSE2(const uint32_t X[256], + const uint32_t Y[256]) { int i; - float retval = 0.f; - int sumX = 0, sumXY = 0; + uint64_t retval = 0; + uint32_t sumX = 0, sumXY = 0; const __m128i zero = _mm_setzero_si128(); for (i = 0; i < 256; i += 16) { @@ -260,19 +307,19 @@ static float CombinedShannonEntropy_SSE2(const int X[256], const int Y[256]) { int32_t my = _mm_movemask_epi8(_mm_cmpgt_epi8(y4, zero)) | mx; while (my) { const int32_t j = BitsCtz(my); - int xy; + uint32_t xy; if ((mx >> j) & 1) { const int x = X[i + j]; sumXY += x; - retval -= VP8LFastSLog2(x); + retval += VP8LFastSLog2(x); } xy = X[i + j] + Y[i + j]; sumX += xy; - retval -= VP8LFastSLog2(xy); + retval += VP8LFastSLog2(xy); my &= my - 1; } } - retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY); + retval = VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY) - retval; return retval; } @@ -335,8 +382,9 @@ static int VectorMismatch_SSE2(const uint32_t* const array1, } // 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) { +static void BundleColorMap_SSE2(const uint8_t* WEBP_RESTRICT const row, + int width, int xbits, + uint32_t* WEBP_RESTRICT dst) { int x; assert(xbits >= 0); assert(xbits <= 3); @@ -425,7 +473,7 @@ static WEBP_INLINE void Average2_m128i(const __m128i* const a0, // Predictor0: ARGB_BLACK. static void PredictorSub0_SSE2(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; const __m128i black = _mm_set1_epi32((int)ARGB_BLACK); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -442,7 +490,8 @@ static void PredictorSub0_SSE2(const uint32_t* in, const uint32_t* 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 num_pixels, \ + uint32_t* WEBP_RESTRICT const out) { \ int i; \ for (i = 0; i + 4 <= num_pixels; i += 4) { \ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \ @@ -464,7 +513,7 @@ GENERATE_PREDICTOR_1(4, upper[i - 1]) // Predictor4: TL // 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 num_pixels, uint32_t* WEBP_RESTRICT out) { int i; for (i = 0; i + 4 <= num_pixels; i += 4) { const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]); @@ -484,7 +533,8 @@ static void PredictorSub5_SSE2(const uint32_t* in, const uint32_t* upper, #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 num_pixels, \ + uint32_t* WEBP_RESTRICT out) { \ int i; \ for (i = 0; i + 4 <= num_pixels; i += 4) { \ const __m128i tA = _mm_loadu_si128((const __m128i*)&(A)); \ @@ -508,7 +558,7 @@ GENERATE_PREDICTOR_2(9, upper[i], upper[i + 1]) // Predictor9: average(T, TR) // 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 num_pixels, uint32_t* WEBP_RESTRICT out) { int i; for (i = 0; i + 4 <= num_pixels; i += 4) { const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]); @@ -543,7 +593,7 @@ static void GetSumAbsDiff32_SSE2(const __m128i* const A, const __m128i* const B, } static void PredictorSub11_SSE2(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; for (i = 0; i + 4 <= num_pixels; i += 4) { const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]); @@ -569,7 +619,7 @@ static void PredictorSub11_SSE2(const uint32_t* in, const uint32_t* upper, // Predictor12: ClampedSubSubtractFull. static void PredictorSub12_SSE2(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; const __m128i zero = _mm_setzero_si128(); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -598,28 +648,46 @@ static void PredictorSub12_SSE2(const uint32_t* in, const uint32_t* upper, // Predictors13: ClampedAddSubtractHalf static void PredictorSub13_SSE2(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT 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); + 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 T = _mm_loadu_si128((const __m128i*)&upper[i]); + const __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]); + __m128i A4_lo, A4_hi; + // lo. + { + 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_lo = _mm_add_epi16(T_lo, L_lo); + const __m128i avg_lo = _mm_srli_epi16(sum_lo, 1); + const __m128i A1_lo = _mm_sub_epi16(avg_lo, TL_lo); + const __m128i bit_fix_lo = _mm_cmpgt_epi16(TL_lo, avg_lo); + const __m128i A2_lo = _mm_sub_epi16(A1_lo, bit_fix_lo); + const __m128i A3_lo = _mm_srai_epi16(A2_lo, 1); + A4_lo = _mm_add_epi16(avg_lo, A3_lo); + } + // hi. + { + const __m128i L_hi = _mm_unpackhi_epi8(L, zero); + const __m128i T_hi = _mm_unpackhi_epi8(T, zero); + const __m128i TL_hi = _mm_unpackhi_epi8(TL, zero); + const __m128i sum_hi = _mm_add_epi16(T_hi, L_hi); + const __m128i avg_hi = _mm_srli_epi16(sum_hi, 1); + const __m128i A1_hi = _mm_sub_epi16(avg_hi, TL_hi); + const __m128i bit_fix_hi = _mm_cmpgt_epi16(TL_hi, avg_hi); + const __m128i A2_hi = _mm_sub_epi16(A1_hi, bit_fix_hi); + const __m128i A3_hi = _mm_srai_epi16(A2_hi, 1); + A4_hi = _mm_add_epi16(avg_hi, A3_hi); + } + { + const __m128i pred = _mm_packus_epi16(A4_lo, A4_hi); + const __m128i res = _mm_sub_epi8(src, pred); + _mm_storeu_si128((__m128i*)&out[i], res); + } } if (i != num_pixels) { VP8LPredictorsSub_C[13](in + i, upper + i, num_pixels - i, out + i); @@ -660,6 +728,15 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE2(void) { VP8LPredictorsSub[13] = PredictorSub13_SSE2; VP8LPredictorsSub[14] = PredictorSub0_SSE2; // <- padding security sentinels VP8LPredictorsSub[15] = PredictorSub0_SSE2; + + // SSE exports for AVX and above. + VP8LSubtractGreenFromBlueAndRed_SSE = SubtractGreenFromBlueAndRed_SSE2; + VP8LTransformColor_SSE = TransformColor_SSE2; + VP8LCollectColorBlueTransforms_SSE = CollectColorBlueTransforms_SSE2; + VP8LCollectColorRedTransforms_SSE = CollectColorRedTransforms_SSE2; + VP8LBundleColorMap_SSE = BundleColorMap_SSE2; + + memcpy(VP8LPredictorsSub_SSE, VP8LPredictorsSub, sizeof(VP8LPredictorsSub)); } #else // !WEBP_USE_SSE2 diff --git a/3rdparty/libwebp/src/dsp/lossless_enc_sse41.c b/3rdparty/libwebp/src/dsp/lossless_enc_sse41.c index 7ab83c2604..2c2950a03b 100644 --- a/3rdparty/libwebp/src/dsp/lossless_enc_sse41.c +++ b/3rdparty/libwebp/src/dsp/lossless_enc_sse41.c @@ -14,9 +14,14 @@ #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE41) -#include +#include #include + +#include + +#include "src/dsp/cpu.h" #include "src/dsp/lossless.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Cost operations. @@ -44,28 +49,6 @@ static uint32_t ExtraCost_SSE41(const uint32_t* const a, int length) { return HorizontalSum_SSE41(cost); } -static uint32_t ExtraCostCombined_SSE41(const uint32_t* const a, - const uint32_t* const b, int length) { - int i; - __m128i cost = _mm_add_epi32(_mm_set_epi32(2 * a[7], 2 * a[6], a[5], a[4]), - _mm_set_epi32(2 * b[7], 2 * b[6], b[5], b[4])); - assert(length % 8 == 0); - - for (i = 8; i + 8 <= length; i += 8) { - const int j = (i - 2) >> 1; - const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i]); - const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]); - const __m128i b0 = _mm_loadu_si128((const __m128i*)&b[i]); - const __m128i b1 = _mm_loadu_si128((const __m128i*)&b[i + 4]); - const __m128i w = _mm_set_epi32(j + 3, j + 2, j + 1, j); - const __m128i a2 = _mm_hadd_epi32(a0, a1); - const __m128i b2 = _mm_hadd_epi32(b0, b1); - const __m128i mul = _mm_mullo_epi32(_mm_add_epi32(a2, b2), w); - cost = _mm_add_epi32(mul, cost); - } - return HorizontalSum_SSE41(cost); -} - //------------------------------------------------------------------------------ // Subtract-Green Transform @@ -95,10 +78,11 @@ static void SubtractGreenFromBlueAndRed_SSE41(uint32_t* argb_data, #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, +static void CollectColorBlueTransforms_SSE41(const uint32_t* WEBP_RESTRICT argb, + int stride, int tile_width, int tile_height, int green_to_blue, int red_to_blue, - int histo[]) { + uint32_t histo[]) { const __m128i mult = MK_CST_16(CST_5b(red_to_blue) + 256,CST_5b(green_to_blue)); const __m128i perm = @@ -141,10 +125,11 @@ static void CollectColorBlueTransforms_SSE41(const uint32_t* argb, int stride, } } -static void CollectColorRedTransforms_SSE41(const uint32_t* argb, int stride, +static void CollectColorRedTransforms_SSE41(const uint32_t* WEBP_RESTRICT argb, + int stride, int tile_width, int tile_height, - int green_to_red, int histo[]) { - + int green_to_red, + uint32_t 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) { @@ -192,10 +177,14 @@ extern void VP8LEncDspInitSSE41(void); WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE41(void) { VP8LExtraCost = ExtraCost_SSE41; - VP8LExtraCostCombined = ExtraCostCombined_SSE41; VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_SSE41; VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_SSE41; VP8LCollectColorRedTransforms = CollectColorRedTransforms_SSE41; + + // SSE exports for AVX and above. + VP8LSubtractGreenFromBlueAndRed_SSE = SubtractGreenFromBlueAndRed_SSE41; + VP8LCollectColorBlueTransforms_SSE = CollectColorBlueTransforms_SSE41; + VP8LCollectColorRedTransforms_SSE = CollectColorRedTransforms_SSE41; } #else // !WEBP_USE_SSE41 diff --git a/3rdparty/libwebp/src/dsp/lossless_mips_dsp_r2.c b/3rdparty/libwebp/src/dsp/lossless_mips_dsp_r2.c index bfe5ea6b38..4b4f056417 100644 --- a/3rdparty/libwebp/src/dsp/lossless_mips_dsp_r2.c +++ b/3rdparty/libwebp/src/dsp/lossless_mips_dsp_r2.c @@ -299,9 +299,9 @@ static void TransformColorInverse_MIPSdspR2(const VP8LMultipliers* const m, 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 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" diff --git a/3rdparty/libwebp/src/dsp/lossless_msa.c b/3rdparty/libwebp/src/dsp/lossless_msa.c index 9f5472078d..0f8ef4107e 100644 --- a/3rdparty/libwebp/src/dsp/lossless_msa.c +++ b/3rdparty/libwebp/src/dsp/lossless_msa.c @@ -290,9 +290,9 @@ 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 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, diff --git a/3rdparty/libwebp/src/dsp/lossless_neon.c b/3rdparty/libwebp/src/dsp/lossless_neon.c index e9960db38a..0a85edd4b8 100644 --- a/3rdparty/libwebp/src/dsp/lossless_neon.c +++ b/3rdparty/libwebp/src/dsp/lossless_neon.c @@ -19,6 +19,7 @@ #include "src/dsp/lossless.h" #include "src/dsp/neon.h" +#include "src/webp/format_constants.h" //------------------------------------------------------------------------------ // Colorspace conversion functions @@ -26,8 +27,8 @@ #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) { +static void ConvertBGRAToRGBA_NEON(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const uint32_t* const end = src + (num_pixels & ~15); for (; src < end; src += 16) { uint8x16x4_t pixel = vld4q_u8((uint8_t*)src); @@ -41,8 +42,8 @@ static void ConvertBGRAToRGBA_NEON(const uint32_t* src, VP8LConvertBGRAToRGBA_C(src, num_pixels & 15, dst); // left-overs } -static void ConvertBGRAToBGR_NEON(const uint32_t* src, - int num_pixels, uint8_t* dst) { +static void ConvertBGRAToBGR_NEON(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const uint32_t* const end = src + (num_pixels & ~15); for (; src < end; src += 16) { const uint8x16x4_t pixel = vld4q_u8((uint8_t*)src); @@ -53,8 +54,8 @@ static void ConvertBGRAToBGR_NEON(const uint32_t* src, VP8LConvertBGRAToBGR_C(src, num_pixels & 15, dst); // left-overs } -static void ConvertBGRAToRGB_NEON(const uint32_t* src, - int num_pixels, uint8_t* dst) { +static void ConvertBGRAToRGB_NEON(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const uint32_t* const end = src + (num_pixels & ~15); for (; src < end; src += 16) { const uint8x16x4_t pixel = vld4q_u8((uint8_t*)src); @@ -71,8 +72,8 @@ static void ConvertBGRAToRGB_NEON(const uint32_t* src, 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) { +static void ConvertBGRAToRGBA_NEON(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const uint32_t* const end = src + (num_pixels & ~1); const uint8x8_t shuffle = vld1_u8(kRGBAShuffle); for (; src < end; src += 2) { @@ -89,8 +90,8 @@ static const uint8_t kBGRShuffle[3][8] = { { 21, 22, 24, 25, 26, 28, 29, 30 } }; -static void ConvertBGRAToBGR_NEON(const uint32_t* src, - int num_pixels, uint8_t* dst) { +static void ConvertBGRAToBGR_NEON(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT 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]); @@ -116,8 +117,8 @@ static const uint8_t kRGBShuffle[3][8] = { { 21, 20, 26, 25, 24, 30, 29, 28 } }; -static void ConvertBGRAToRGB_NEON(const uint32_t* src, - int num_pixels, uint8_t* dst) { +static void ConvertBGRAToRGB_NEON(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT 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]); @@ -209,7 +210,7 @@ static uint32_t Predictor13_NEON(const uint32_t* const left, // Predictor0: ARGB_BLACK. static void PredictorAdd0_NEON(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; const uint8x16_t black = vreinterpretq_u8_u32(vdupq_n_u32(ARGB_BLACK)); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -222,7 +223,7 @@ static void PredictorAdd0_NEON(const uint32_t* in, const uint32_t* upper, // Predictor1: left. static void PredictorAdd1_NEON(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; const uint8x16_t zero = LOADQ_U32_AS_U8(0); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -248,7 +249,7 @@ static void PredictorAdd1_NEON(const uint32_t* in, const uint32_t* upper, #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) { \ + uint32_t* WEBP_RESTRICT out) { \ int i; \ for (i = 0; i + 4 <= num_pixels; i += 4) { \ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); \ @@ -276,7 +277,7 @@ GENERATE_PREDICTOR_1(4, upper[i - 1]) } while (0) static void PredictorAdd5_NEON(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -301,7 +302,7 @@ static void PredictorAdd5_NEON(const uint32_t* in, const uint32_t* upper, // Predictor6: average(left, TL) static void PredictorAdd6_NEON(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -317,7 +318,7 @@ static void PredictorAdd6_NEON(const uint32_t* in, const uint32_t* upper, // Predictor7: average(left, T) static void PredictorAdd7_NEON(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -335,7 +336,7 @@ static void PredictorAdd7_NEON(const uint32_t* in, const uint32_t* upper, #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) { \ + uint32_t* WEBP_RESTRICT out) { \ int i; \ for (i = 0; i + 4 <= num_pixels; i += 4) { \ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); \ @@ -363,7 +364,7 @@ GENERATE_PREDICTOR_2(9, upper[i + 1]) } while (0) static void PredictorAdd10_NEON(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -394,7 +395,7 @@ static void PredictorAdd10_NEON(const uint32_t* in, const uint32_t* upper, } while (0) static void PredictorAdd11_NEON(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -427,7 +428,7 @@ static void PredictorAdd11_NEON(const uint32_t* in, const uint32_t* upper, } while (0) static void PredictorAdd12_NEON(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; uint16x8_t L = vmovl_u8(LOAD_U32_AS_U8(out[-1])); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -468,7 +469,7 @@ static void PredictorAdd12_NEON(const uint32_t* in, const uint32_t* upper, } while (0) static void PredictorAdd13_NEON(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; uint8x16_t L = LOADQ_U32_AS_U8(out[-1]); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -551,15 +552,15 @@ static void TransformColorInverse_NEON(const VP8LMultipliers* const m, // 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_) + 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_), + 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 diff --git a/3rdparty/libwebp/src/dsp/lossless_sse2.c b/3rdparty/libwebp/src/dsp/lossless_sse2.c index 4b6a532c23..3c6608be07 100644 --- a/3rdparty/libwebp/src/dsp/lossless_sse2.c +++ b/3rdparty/libwebp/src/dsp/lossless_sse2.c @@ -15,10 +15,15 @@ #if defined(WEBP_USE_SSE2) +#include +#include + #include "src/dsp/common_sse2.h" +#include "src/dsp/cpu.h" #include "src/dsp/lossless.h" #include "src/dsp/lossless_common.h" -#include +#include "src/webp/format_constants.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Predictor Transform @@ -186,7 +191,7 @@ static uint32_t Predictor13_SSE2(const uint32_t* const left, // Predictor0: ARGB_BLACK. static void PredictorAdd0_SSE2(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; const __m128i black = _mm_set1_epi32((int)ARGB_BLACK); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -202,7 +207,7 @@ static void PredictorAdd0_SSE2(const uint32_t* in, const uint32_t* upper, // Predictor1: left. static void PredictorAdd1_SSE2(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; __m128i prev = _mm_set1_epi32((int)out[-1]); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -230,7 +235,8 @@ static void PredictorAdd1_SSE2(const uint32_t* in, const uint32_t* upper, // 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 num_pixels, \ + uint32_t* WEBP_RESTRICT out) { \ int i; \ for (i = 0; i + 4 <= num_pixels; i += 4) { \ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \ @@ -259,7 +265,8 @@ 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 num_pixels, \ + uint32_t* WEBP_RESTRICT out) { \ int i; \ for (i = 0; i + 4 <= num_pixels; i += 4) { \ const __m128i Tother = _mm_loadu_si128((const __m128i*)&(IN)); \ @@ -297,7 +304,7 @@ GENERATE_PREDICTOR_2(9, upper[i + 1]) } while (0) static void PredictorAdd10_SSE2(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; __m128i L = _mm_cvtsi32_si128((int)out[-1]); for (i = 0; i + 4 <= num_pixels; i += 4) { @@ -344,7 +351,7 @@ static void PredictorAdd10_SSE2(const uint32_t* in, const uint32_t* upper, } while (0) static void PredictorAdd11_SSE2(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; __m128i pa; __m128i L = _mm_cvtsi32_si128((int)out[-1]); @@ -395,7 +402,7 @@ static void PredictorAdd11_SSE2(const uint32_t* in, const uint32_t* upper, } while (0) static void PredictorAdd12_SSE2(const uint32_t* in, const uint32_t* upper, - int num_pixels, uint32_t* out) { + int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; const __m128i zero = _mm_setzero_si128(); const __m128i L8 = _mm_cvtsi32_si128((int)out[-1]); @@ -460,8 +467,8 @@ static void TransformColorInverse_SSE2(const VP8LMultipliers* const m, #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); + 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((int)0xff00ff00); // alpha-green masks @@ -490,8 +497,8 @@ static void TransformColorInverse_SSE2(const VP8LMultipliers* const m, //------------------------------------------------------------------------------ // Color-space conversion functions -static void ConvertBGRAToRGB_SSE2(const uint32_t* src, int num_pixels, - uint8_t* dst) { +static void ConvertBGRAToRGB_SSE2(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const __m128i* in = (const __m128i*)src; __m128i* out = (__m128i*)dst; @@ -526,8 +533,8 @@ static void ConvertBGRAToRGB_SSE2(const uint32_t* src, int num_pixels, } } -static void ConvertBGRAToRGBA_SSE2(const uint32_t* src, - int num_pixels, uint8_t* dst) { +static void ConvertBGRAToRGBA_SSE2(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT dst) { const __m128i red_blue_mask = _mm_set1_epi32(0x00ff00ff); const __m128i* in = (const __m128i*)src; __m128i* out = (__m128i*)dst; @@ -554,8 +561,9 @@ static void ConvertBGRAToRGBA_SSE2(const uint32_t* src, } } -static void ConvertBGRAToRGBA4444_SSE2(const uint32_t* src, - int num_pixels, uint8_t* dst) { +static void ConvertBGRAToRGBA4444_SSE2(const uint32_t* WEBP_RESTRICT src, + int num_pixels, + uint8_t* WEBP_RESTRICT dst) { const __m128i mask_0x0f = _mm_set1_epi8(0x0f); const __m128i mask_0xf0 = _mm_set1_epi8((char)0xf0); const __m128i* in = (const __m128i*)src; @@ -590,8 +598,9 @@ static void ConvertBGRAToRGBA4444_SSE2(const uint32_t* src, } } -static void ConvertBGRAToRGB565_SSE2(const uint32_t* src, - int num_pixels, uint8_t* dst) { +static void ConvertBGRAToRGB565_SSE2(const uint32_t* WEBP_RESTRICT src, + int num_pixels, + uint8_t* WEBP_RESTRICT dst) { const __m128i mask_0xe0 = _mm_set1_epi8((char)0xe0); const __m128i mask_0xf8 = _mm_set1_epi8((char)0xf8); const __m128i mask_0x07 = _mm_set1_epi8(0x07); @@ -631,8 +640,8 @@ static void ConvertBGRAToRGB565_SSE2(const uint32_t* src, } } -static void ConvertBGRAToBGR_SSE2(const uint32_t* src, - int num_pixels, uint8_t* dst) { +static void ConvertBGRAToBGR_SSE2(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT 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; @@ -703,6 +712,15 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitSSE2(void) { VP8LConvertBGRAToRGBA4444 = ConvertBGRAToRGBA4444_SSE2; VP8LConvertBGRAToRGB565 = ConvertBGRAToRGB565_SSE2; VP8LConvertBGRAToBGR = ConvertBGRAToBGR_SSE2; + + // SSE exports for AVX and above. + memcpy(VP8LPredictorsAdd_SSE, VP8LPredictorsAdd, sizeof(VP8LPredictorsAdd)); + + VP8LAddGreenToBlueAndRed_SSE = AddGreenToBlueAndRed_SSE2; + VP8LTransformColorInverse_SSE = TransformColorInverse_SSE2; + + VP8LConvertBGRAToRGB_SSE = ConvertBGRAToRGB_SSE2; + VP8LConvertBGRAToRGBA_SSE = ConvertBGRAToRGBA_SSE2; } #else // !WEBP_USE_SSE2 diff --git a/3rdparty/libwebp/src/dsp/lossless_sse41.c b/3rdparty/libwebp/src/dsp/lossless_sse41.c index bb7ce7611f..598e679410 100644 --- a/3rdparty/libwebp/src/dsp/lossless_sse41.c +++ b/3rdparty/libwebp/src/dsp/lossless_sse41.c @@ -12,10 +12,12 @@ #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE41) +#include +#include -#include "src/dsp/common_sse41.h" +#include "src/webp/types.h" +#include "src/dsp/cpu.h" #include "src/dsp/lossless.h" -#include "src/dsp/lossless_common.h" //------------------------------------------------------------------------------ // Color-space conversion functions @@ -26,9 +28,9 @@ static void TransformColorInverse_SSE41(const VP8LMultipliers* const m, // 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((int)((uint32_t)CST(green_to_red_) << 16 | - (CST(green_to_blue_) & 0xffff))); - const __m128i mults_b2 = _mm_set1_epi32(CST(red_to_blue_)); + _mm_set1_epi32((int)((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((int)0xff00ff00); const __m128i perm1 = _mm_setr_epi8(-1, 1, -1, 1, -1, 5, -1, 5, @@ -77,8 +79,8 @@ static void TransformColorInverse_SSE41(const VP8LMultipliers* const m, } \ } while (0) -static void ConvertBGRAToRGB_SSE41(const uint32_t* src, int num_pixels, - uint8_t* dst) { +static void ConvertBGRAToRGB_SSE41(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT 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, @@ -95,8 +97,8 @@ static void ConvertBGRAToRGB_SSE41(const uint32_t* src, int num_pixels, } } -static void ConvertBGRAToBGR_SSE41(const uint32_t* src, - int num_pixels, uint8_t* dst) { +static void ConvertBGRAToBGR_SSE41(const uint32_t* WEBP_RESTRICT src, + int num_pixels, uint8_t* WEBP_RESTRICT 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, @@ -124,6 +126,10 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitSSE41(void) { VP8LTransformColorInverse = TransformColorInverse_SSE41; VP8LConvertBGRAToRGB = ConvertBGRAToRGB_SSE41; VP8LConvertBGRAToBGR = ConvertBGRAToBGR_SSE41; + + // SSE exports for AVX and above. + VP8LTransformColorInverse_SSE = TransformColorInverse_SSE41; + VP8LConvertBGRAToRGB_SSE = ConvertBGRAToRGB_SSE41; } #else // !WEBP_USE_SSE41 diff --git a/3rdparty/libwebp/src/dsp/rescaler.c b/3rdparty/libwebp/src/dsp/rescaler.c index 325d8be180..eafccd442f 100644 --- a/3rdparty/libwebp/src/dsp/rescaler.c +++ b/3rdparty/libwebp/src/dsp/rescaler.c @@ -12,7 +12,10 @@ // Author: Skal (pascal.massimino@gmail.com) #include +#include +#include "src/dsp/cpu.h" +#include "src/webp/types.h" #include "src/dsp/dsp.h" #include "src/utils/rescaler_utils.h" @@ -26,8 +29,8 @@ //------------------------------------------------------------------------------ // Row import -void WebPRescalerImportRowExpand_C(WebPRescaler* const wrk, - const uint8_t* src) { +void WebPRescalerImportRowExpand_C(WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT src) { const int x_stride = wrk->num_channels; const int x_out_max = wrk->dst_width * wrk->num_channels; int channel; @@ -59,8 +62,8 @@ void WebPRescalerImportRowExpand_C(WebPRescaler* const wrk, } } -void WebPRescalerImportRowShrink_C(WebPRescaler* const wrk, - const uint8_t* src) { +void WebPRescalerImportRowShrink_C(WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT src) { const int x_stride = wrk->num_channels; const int x_out_max = wrk->dst_width * wrk->num_channels; int channel; @@ -158,7 +161,8 @@ void WebPRescalerExportRowShrink_C(WebPRescaler* const wrk) { //------------------------------------------------------------------------------ // Main entry calls -void WebPRescalerImportRow(WebPRescaler* const wrk, const uint8_t* src) { +void WebPRescalerImportRow(WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT src) { assert(!WebPRescalerInputDone(wrk)); if (!wrk->x_expand) { WebPRescalerImportRowShrink(wrk, src); diff --git a/3rdparty/libwebp/src/dsp/rescaler_mips32.c b/3rdparty/libwebp/src/dsp/rescaler_mips32.c index 61f63c616c..b5168caa17 100644 --- a/3rdparty/libwebp/src/dsp/rescaler_mips32.c +++ b/3rdparty/libwebp/src/dsp/rescaler_mips32.c @@ -21,8 +21,8 @@ //------------------------------------------------------------------------------ // Row import -static void ImportRowShrink_MIPS32(WebPRescaler* const wrk, - const uint8_t* src) { +static void ImportRowShrink_MIPS32(WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT 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; @@ -81,8 +81,8 @@ static void ImportRowShrink_MIPS32(WebPRescaler* const wrk, } } -static void ImportRowExpand_MIPS32(WebPRescaler* const wrk, - const uint8_t* src) { +static void ImportRowExpand_MIPS32(WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT 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; diff --git a/3rdparty/libwebp/src/dsp/rescaler_msa.c b/3rdparty/libwebp/src/dsp/rescaler_msa.c index 256dbdd437..954d0fdfc3 100644 --- a/3rdparty/libwebp/src/dsp/rescaler_msa.c +++ b/3rdparty/libwebp/src/dsp/rescaler_msa.c @@ -114,9 +114,9 @@ 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) { +static WEBP_INLINE void ExportRowExpand_0( + const uint32_t* WEBP_RESTRICT frow, uint8_t* WEBP_RESTRICT dst, int length, + WebPRescaler* WEBP_RESTRICT 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 }; @@ -171,9 +171,10 @@ static WEBP_INLINE void ExportRowExpand_0(const uint32_t* frow, uint8_t* dst, } } -static WEBP_INLINE void ExportRowExpand_1(const uint32_t* frow, uint32_t* irow, - uint8_t* dst, int length, - WebPRescaler* const wrk) { +static WEBP_INLINE void ExportRowExpand_1( + const uint32_t* WEBP_RESTRICT frow, uint32_t* WEBP_RESTRICT irow, + uint8_t* WEBP_RESTRICT dst, int length, + WebPRescaler* WEBP_RESTRICT 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); @@ -262,10 +263,10 @@ static void RescalerExportRowExpand_MIPSdspR2(WebPRescaler* const 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) { +static WEBP_INLINE void ExportRowShrink_0( + const uint32_t* WEBP_RESTRICT frow, uint32_t* WEBP_RESTRICT irow, + uint8_t* WEBP_RESTRICT dst, int length, const uint32_t yscale, + WebPRescaler* WEBP_RESTRICT 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); @@ -348,9 +349,9 @@ static WEBP_INLINE void ExportRowShrink_0(const uint32_t* frow, uint32_t* irow, } } -static WEBP_INLINE void ExportRowShrink_1(uint32_t* irow, uint8_t* dst, - int length, - WebPRescaler* const wrk) { +static WEBP_INLINE void ExportRowShrink_1( + uint32_t* WEBP_RESTRICT irow, uint8_t* WEBP_RESTRICT dst, int length, + WebPRescaler* WEBP_RESTRICT 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 }; diff --git a/3rdparty/libwebp/src/dsp/rescaler_neon.c b/3rdparty/libwebp/src/dsp/rescaler_neon.c index 957a92dbc9..ab4ddc0090 100644 --- a/3rdparty/libwebp/src/dsp/rescaler_neon.c +++ b/3rdparty/libwebp/src/dsp/rescaler_neon.c @@ -45,8 +45,8 @@ #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, +static uint32x4_t Interpolate_NEON(const rescaler_t* WEBP_RESTRICT const frow, + const rescaler_t* WEBP_RESTRICT const irow, uint32_t A, uint32_t B) { LOAD_32x4(frow, A0); LOAD_32x4(irow, B0); diff --git a/3rdparty/libwebp/src/dsp/rescaler_sse2.c b/3rdparty/libwebp/src/dsp/rescaler_sse2.c index 3f18e94e93..be5508ca1f 100644 --- a/3rdparty/libwebp/src/dsp/rescaler_sse2.c +++ b/3rdparty/libwebp/src/dsp/rescaler_sse2.c @@ -17,8 +17,12 @@ #include #include +#include + +#include "src/dsp/cpu.h" #include "src/utils/rescaler_utils.h" #include "src/utils/utils.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Implementations of critical functions ImportRow / ExportRow @@ -43,8 +47,8 @@ static void LoadEightPixels_SSE2(const uint8_t* const src, __m128i* out) { *out = _mm_unpacklo_epi8(A, zero); } -static void RescalerImportRowExpand_SSE2(WebPRescaler* const wrk, - const uint8_t* src) { +static void RescalerImportRowExpand_SSE2(WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT 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; @@ -109,8 +113,8 @@ static void RescalerImportRowExpand_SSE2(WebPRescaler* const wrk, assert(accum == 0); } -static void RescalerImportRowShrink_SSE2(WebPRescaler* const wrk, - const uint8_t* src) { +static void RescalerImportRowShrink_SSE2(WebPRescaler* WEBP_RESTRICT const wrk, + const uint8_t* WEBP_RESTRICT src) { const int x_sub = wrk->x_sub; int accum = 0; const __m128i zero = _mm_setzero_si128(); @@ -168,12 +172,10 @@ static void RescalerImportRowShrink_SSE2(WebPRescaler* const wrk, // 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) { +static WEBP_INLINE void LoadDispatchAndMult_SSE2( + const rescaler_t* WEBP_RESTRICT 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); diff --git a/3rdparty/libwebp/src/dsp/ssim.c b/3rdparty/libwebp/src/dsp/ssim.c index 9a1341ed95..3002d62697 100644 --- a/3rdparty/libwebp/src/dsp/ssim.c +++ b/3rdparty/libwebp/src/dsp/ssim.c @@ -14,7 +14,9 @@ #include #include // for abs() +#include "src/dsp/cpu.h" #include "src/dsp/dsp.h" +#include "src/webp/types.h" #if !defined(WEBP_REDUCE_SIZE) diff --git a/3rdparty/libwebp/src/dsp/ssim_sse2.c b/3rdparty/libwebp/src/dsp/ssim_sse2.c index 1dcb0eb0ec..3ca3de4e05 100644 --- a/3rdparty/libwebp/src/dsp/ssim_sse2.c +++ b/3rdparty/libwebp/src/dsp/ssim_sse2.c @@ -14,11 +14,13 @@ #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE2) - -#include #include +#include + #include "src/dsp/common_sse2.h" +#include "src/dsp/cpu.h" +#include "src/webp/types.h" #if !defined(WEBP_DISABLE_STATS) diff --git a/3rdparty/libwebp/src/dsp/upsampling.c b/3rdparty/libwebp/src/dsp/upsampling.c index 983b9c42d3..c57f66c355 100644 --- a/3rdparty/libwebp/src/dsp/upsampling.c +++ b/3rdparty/libwebp/src/dsp/upsampling.c @@ -11,10 +11,14 @@ // // Author: somnath@google.com (Somnath Banerjee) +#include +#include + +#include "src/dsp/cpu.h" +#include "src/webp/types.h" #include "src/dsp/dsp.h" #include "src/dsp/yuv.h" - -#include +#include "src/webp/decode.h" //------------------------------------------------------------------------------ // Fancy upsampler @@ -35,10 +39,14 @@ WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST]; #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) { \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT top_y, \ + const uint8_t* WEBP_RESTRICT bottom_y, \ + const uint8_t* WEBP_RESTRICT top_u, \ + const uint8_t* WEBP_RESTRICT top_v, \ + const uint8_t* WEBP_RESTRICT cur_u, \ + const uint8_t* WEBP_RESTRICT cur_v, \ + uint8_t* WEBP_RESTRICT top_dst, \ + uint8_t* WEBP_RESTRICT 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 */ \ @@ -136,10 +144,14 @@ static void EmptyUpsampleFunc(const uint8_t* top_y, const uint8_t* bottom_y, #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) { \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT top_y, \ + const uint8_t* WEBP_RESTRICT bot_y, \ + const uint8_t* WEBP_RESTRICT top_u, \ + const uint8_t* WEBP_RESTRICT top_v, \ + const uint8_t* WEBP_RESTRICT bot_u, \ + const uint8_t* WEBP_RESTRICT bot_v, \ + uint8_t* WEBP_RESTRICT top_dst, \ + uint8_t* WEBP_RESTRICT bot_dst, int len) { \ const int half_len = len >> 1; \ int x; \ assert(top_dst != NULL); \ @@ -178,10 +190,14 @@ WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last) { // 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) { \ +extern void FUNC_NAME(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len); \ +void FUNC_NAME(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len) { \ int i; \ for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * (XSTEP)]); \ } diff --git a/3rdparty/libwebp/src/dsp/upsampling_mips_dsp_r2.c b/3rdparty/libwebp/src/dsp/upsampling_mips_dsp_r2.c index 10d499d771..cbe8e71d40 100644 --- a/3rdparty/libwebp/src/dsp/upsampling_mips_dsp_r2.c +++ b/3rdparty/libwebp/src/dsp/upsampling_mips_dsp_r2.c @@ -143,10 +143,14 @@ static WEBP_INLINE void YuvToRgba(uint8_t y, uint8_t u, uint8_t v, #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) { \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT top_y, \ + const uint8_t* WEBP_RESTRICT bottom_y, \ + const uint8_t* WEBP_RESTRICT top_u, \ + const uint8_t* WEBP_RESTRICT top_v, \ + const uint8_t* WEBP_RESTRICT cur_u, \ + const uint8_t* WEBP_RESTRICT cur_v, \ + uint8_t* WEBP_RESTRICT top_dst, \ + uint8_t* WEBP_RESTRICT 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 */ \ @@ -241,8 +245,10 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersMIPSdspR2(void) { // 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) { \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len) { \ int i; \ for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * XSTEP]); \ } diff --git a/3rdparty/libwebp/src/dsp/upsampling_msa.c b/3rdparty/libwebp/src/dsp/upsampling_msa.c index f2e03e85e9..72a526bc17 100644 --- a/3rdparty/libwebp/src/dsp/upsampling_msa.c +++ b/3rdparty/libwebp/src/dsp/upsampling_msa.c @@ -320,8 +320,10 @@ static void YuvToRgba(uint8_t y, uint8_t u, uint8_t v, uint8_t* const rgba) { } #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) { +static void YuvToRgbLine(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int length) { v16u8 R, G, B; while (length >= 16) { CALC_RGB16(y, u, v, R, G, B); @@ -347,8 +349,10 @@ static void YuvToRgbLine(const uint8_t* y, const uint8_t* u, } } -static void YuvToBgrLine(const uint8_t* y, const uint8_t* u, - const uint8_t* v, uint8_t* dst, int length) { +static void YuvToBgrLine(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int length) { v16u8 R, G, B; while (length >= 16) { CALC_RGB16(y, u, v, R, G, B); @@ -375,8 +379,10 @@ static void YuvToBgrLine(const uint8_t* y, const uint8_t* u, } #endif // WEBP_REDUCE_CSP -static void YuvToRgbaLine(const uint8_t* y, const uint8_t* u, - const uint8_t* v, uint8_t* dst, int length) { +static void YuvToRgbaLine(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int length) { v16u8 R, G, B; const v16u8 A = (v16u8)__msa_ldi_b(ALPHAVAL); while (length >= 16) { @@ -403,8 +409,10 @@ static void YuvToRgbaLine(const uint8_t* y, const uint8_t* u, } } -static void YuvToBgraLine(const uint8_t* y, const uint8_t* u, - const uint8_t* v, uint8_t* dst, int length) { +static void YuvToBgraLine(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int length) { v16u8 R, G, B; const v16u8 A = (v16u8)__msa_ldi_b(ALPHAVAL); while (length >= 16) { @@ -432,8 +440,10 @@ static void YuvToBgraLine(const uint8_t* y, const uint8_t* u, } #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) { +static void YuvToArgbLine(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int length) { v16u8 R, G, B; const v16u8 A = (v16u8)__msa_ldi_b(ALPHAVAL); while (length >= 16) { @@ -460,8 +470,10 @@ static void YuvToArgbLine(const uint8_t* y, const uint8_t* u, } } -static void YuvToRgba4444Line(const uint8_t* y, const uint8_t* u, - const uint8_t* v, uint8_t* dst, int length) { +static void YuvToRgba4444Line(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int length) { v16u8 R, G, B, RG, BA, tmp0, tmp1; while (length >= 16) { #if (WEBP_SWAP_16BIT_CSP == 1) @@ -496,8 +508,10 @@ static void YuvToRgba4444Line(const uint8_t* y, const uint8_t* u, } } -static void YuvToRgb565Line(const uint8_t* y, const uint8_t* u, - const uint8_t* v, uint8_t* dst, int length) { +static void YuvToRgb565Line(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int length) { v16u8 R, G, B, RG, GB, tmp0, tmp1; while (length >= 16) { #if (WEBP_SWAP_16BIT_CSP == 1) @@ -564,11 +578,14 @@ static void YuvToRgb565Line(const uint8_t* y, const uint8_t* u, } 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) \ -{ \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT top_y, \ + const uint8_t* WEBP_RESTRICT bot_y, \ + const uint8_t* WEBP_RESTRICT top_u, \ + const uint8_t* WEBP_RESTRICT top_v, \ + const uint8_t* WEBP_RESTRICT cur_u, \ + const uint8_t* WEBP_RESTRICT cur_v, \ + uint8_t* WEBP_RESTRICT top_dst, \ + uint8_t* WEBP_RESTRICT bot_dst, int len) { \ int size = (len - 1) >> 1; \ uint8_t temp_u[64]; \ uint8_t temp_v[64]; \ diff --git a/3rdparty/libwebp/src/dsp/upsampling_neon.c b/3rdparty/libwebp/src/dsp/upsampling_neon.c index bbc000ca2d..2bd3e931e8 100644 --- a/3rdparty/libwebp/src/dsp/upsampling_neon.c +++ b/3rdparty/libwebp/src/dsp/upsampling_neon.c @@ -58,8 +58,9 @@ } 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) { +static void Upsample16Pixels_NEON(const uint8_t* WEBP_RESTRICT const r1, + const uint8_t* WEBP_RESTRICT const r2, + uint8_t* WEBP_RESTRICT const out) { UPSAMPLE_16PIXELS(r1, r2, out); } @@ -189,57 +190,61 @@ static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 }; } \ } -#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); \ +#define NEON_UPSAMPLE_FUNC(FUNC_NAME, FMT, XSTEP) \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT top_y, \ + const uint8_t* WEBP_RESTRICT bottom_y, \ + const uint8_t* WEBP_RESTRICT top_u, \ + const uint8_t* WEBP_RESTRICT top_v, \ + const uint8_t* WEBP_RESTRICT cur_u, \ + const uint8_t* WEBP_RESTRICT cur_v, \ + uint8_t* WEBP_RESTRICT top_dst, \ + uint8_t* WEBP_RESTRICT 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) & ~(uintptr_t)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. diff --git a/3rdparty/libwebp/src/dsp/upsampling_sse2.c b/3rdparty/libwebp/src/dsp/upsampling_sse2.c index 77b4f7221e..0eecb11c5a 100644 --- a/3rdparty/libwebp/src/dsp/upsampling_sse2.c +++ b/3rdparty/libwebp/src/dsp/upsampling_sse2.c @@ -14,11 +14,15 @@ #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE2) +#include #include -#include #include + +#include "src/webp/types.h" +#include "src/dsp/cpu.h" #include "src/dsp/yuv.h" +#include "src/webp/decode.h" #ifdef FANCY_UPSAMPLING @@ -88,8 +92,9 @@ } while (0) // 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) { +static void Upsample32Pixels_SSE2(const uint8_t* WEBP_RESTRICT const r1, + const uint8_t* WEBP_RESTRICT const r2, + uint8_t* WEBP_RESTRICT const out) { UPSAMPLE_32PIXELS(r1, r2, out); } @@ -114,10 +119,14 @@ static void Upsample32Pixels_SSE2(const uint8_t r1[], const uint8_t r2[], } 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) { \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT top_y, \ + const uint8_t* WEBP_RESTRICT bottom_y, \ + const uint8_t* WEBP_RESTRICT top_u, \ + const uint8_t* WEBP_RESTRICT top_v, \ + const uint8_t* WEBP_RESTRICT cur_u, \ + const uint8_t* WEBP_RESTRICT cur_v, \ + uint8_t* WEBP_RESTRICT top_dst, \ + uint8_t* WEBP_RESTRICT 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 }; \ @@ -215,10 +224,14 @@ 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) { \ +extern void CALL_C(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len); \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len) { \ int i; \ const int max_len = len & ~31; \ for (i = 0; i < max_len; i += 32) { \ diff --git a/3rdparty/libwebp/src/dsp/upsampling_sse41.c b/3rdparty/libwebp/src/dsp/upsampling_sse41.c index e38c88d5e6..cac9567aac 100644 --- a/3rdparty/libwebp/src/dsp/upsampling_sse41.c +++ b/3rdparty/libwebp/src/dsp/upsampling_sse41.c @@ -14,11 +14,15 @@ #include "src/dsp/dsp.h" #if defined(WEBP_USE_SSE41) +#include #include -#include #include + +#include "src/webp/types.h" +#include "src/dsp/cpu.h" #include "src/dsp/yuv.h" +#include "src/webp/decode.h" #ifdef FANCY_UPSAMPLING @@ -90,8 +94,9 @@ } while (0) // 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) { +static void Upsample32Pixels_SSE41(const uint8_t* WEBP_RESTRICT const r1, + const uint8_t* WEBP_RESTRICT const r2, + uint8_t* WEBP_RESTRICT const out) { UPSAMPLE_32PIXELS(r1, r2, out); } @@ -116,14 +121,18 @@ static void Upsample32Pixels_SSE41(const uint8_t r1[], const uint8_t r2[], } 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) { \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT top_y, \ + const uint8_t* WEBP_RESTRICT bottom_y, \ + const uint8_t* WEBP_RESTRICT top_u, \ + const uint8_t* WEBP_RESTRICT top_v, \ + const uint8_t* WEBP_RESTRICT cur_u, \ + const uint8_t* WEBP_RESTRICT cur_v, \ + uint8_t* WEBP_RESTRICT top_dst, \ + uint8_t* WEBP_RESTRICT 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_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~(uintptr_t)15); \ uint8_t* const r_v = r_u + 32; \ \ assert(top_y != NULL); \ @@ -202,10 +211,14 @@ 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) { \ +extern void CALL_C(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len); \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len) { \ int i; \ const int max_len = len & ~31; \ for (i = 0; i < max_len; i += 32) { \ diff --git a/3rdparty/libwebp/src/dsp/yuv.c b/3rdparty/libwebp/src/dsp/yuv.c index 8a04b85d82..62f1ecc156 100644 --- a/3rdparty/libwebp/src/dsp/yuv.c +++ b/3rdparty/libwebp/src/dsp/yuv.c @@ -11,18 +11,23 @@ // // Author: Skal (pascal.massimino@gmail.com) -#include "src/dsp/yuv.h" - #include #include +#include "src/dsp/cpu.h" +#include "src/webp/types.h" +#include "src/dsp/dsp.h" +#include "src/dsp/yuv.h" +#include "src/webp/decode.h" + //----------------------------------------------------------------------------- // 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) { \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len) { \ const uint8_t* const end = dst + (len & ~1) * (XSTEP); \ while (dst != end) { \ FUNC(y[0], u[0], v[0], dst); \ @@ -49,9 +54,10 @@ 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, +void WebPSamplerProcessPlane(const uint8_t* WEBP_RESTRICT y, int y_stride, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, int uv_stride, + uint8_t* WEBP_RESTRICT dst, int dst_stride, int width, int height, WebPSamplerRowFunc func) { int j; for (j = 0; j < height; ++j) { @@ -117,7 +123,8 @@ WEBP_DSP_INIT_FUNC(WebPInitSamplers) { //----------------------------------------------------------------------------- // ARGB -> YUV converters -static void ConvertARGBToY_C(const uint32_t* argb, uint8_t* y, int width) { +static void ConvertARGBToY_C(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT y, int width) { int i; for (i = 0; i < width; ++i) { const uint32_t p = argb[i]; @@ -126,7 +133,8 @@ static void ConvertARGBToY_C(const uint32_t* argb, uint8_t* y, int width) { } } -void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v, +void WebPConvertARGBToUV_C(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT v, int src_width, int do_store) { // No rounding. Last pixel is dealt with separately. const int uv_width = src_width >> 1; @@ -169,22 +177,25 @@ void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v, //----------------------------------------------------------------------------- -static void ConvertRGB24ToY_C(const uint8_t* rgb, uint8_t* y, int width) { +static void ConvertRGB24ToY_C(const uint8_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT 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) { +static void ConvertBGR24ToY_C(const uint8_t* WEBP_RESTRICT bgr, + uint8_t* WEBP_RESTRICT 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) { +void WebPConvertRGBA32ToUV_C(const uint16_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT 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]; @@ -195,13 +206,18 @@ void WebPConvertRGBA32ToUV_C(const uint16_t* rgb, //----------------------------------------------------------------------------- -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 (*WebPConvertRGB24ToY)(const uint8_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT y, int width); +void (*WebPConvertBGR24ToY)(const uint8_t* WEBP_RESTRICT bgr, + uint8_t* WEBP_RESTRICT y, int width); +void (*WebPConvertRGBA32ToUV)(const uint16_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT 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, +void (*WebPConvertARGBToY)(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT y, int width); +void (*WebPConvertARGBToUV)(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT u, uint8_t* WEBP_RESTRICT v, int src_width, int do_store); extern void WebPInitConvertARGBToYUVSSE2(void); diff --git a/3rdparty/libwebp/src/dsp/yuv.h b/3rdparty/libwebp/src/dsp/yuv.h index 66a397d117..6f218cf7e0 100644 --- a/3rdparty/libwebp/src/dsp/yuv.h +++ b/3rdparty/libwebp/src/dsp/yuv.h @@ -11,15 +11,15 @@ // // The exact naming is Y'CbCr, following the ITU-R BT.601 standard. // More information at: https://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 +// Y = 0.2568 * R + 0.5041 * G + 0.0979 * B + 16 +// U = -0.1482 * R - 0.2910 * G + 0.4392 * B + 128 +// V = 0.4392 * R - 0.3678 * G - 0.0714 * 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) +// G = 1.164 * (Y-16) - 0.813 * (V-128) - 0.392 * (U-128) +// B = 1.164 * (Y-16) + 2.017 * (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: @@ -35,8 +35,10 @@ #ifndef WEBP_DSP_YUV_H_ #define WEBP_DSP_YUV_H_ -#include "src/dsp/dsp.h" #include "src/dec/vp8_dec.h" +#include "src/dsp/cpu.h" +#include "src/dsp/dsp.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // YUV -> RGB conversion @@ -149,20 +151,34 @@ static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v, #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); +void VP8YuvToRgba32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst); +void VP8YuvToRgb32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst); +void VP8YuvToBgra32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst); +void VP8YuvToBgr32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst); +void VP8YuvToArgb32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst); +void VP8YuvToRgba444432_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst); +void VP8YuvToRgb56532_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst); #endif // WEBP_USE_SSE2 @@ -172,10 +188,14 @@ void VP8YuvToRgb56532_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, #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); +void VP8YuvToRgb32_SSE41(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst); +void VP8YuvToBgr32_SSE41(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst); #endif // WEBP_USE_SSE41 diff --git a/3rdparty/libwebp/src/dsp/yuv_mips32.c b/3rdparty/libwebp/src/dsp/yuv_mips32.c index 9d0a887824..1f6348586a 100644 --- a/3rdparty/libwebp/src/dsp/yuv_mips32.c +++ b/3rdparty/libwebp/src/dsp/yuv_mips32.c @@ -22,9 +22,10 @@ // 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) { \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len) { \ int i, r, g, b; \ int temp0, temp1, temp2, temp3, temp4; \ for (i = 0; i < (len >> 1); i++) { \ diff --git a/3rdparty/libwebp/src/dsp/yuv_mips_dsp_r2.c b/3rdparty/libwebp/src/dsp/yuv_mips_dsp_r2.c index cc8afcc756..816340fe0b 100644 --- a/3rdparty/libwebp/src/dsp/yuv_mips_dsp_r2.c +++ b/3rdparty/libwebp/src/dsp/yuv_mips_dsp_r2.c @@ -69,9 +69,10 @@ : "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) { \ +static void FUNC_NAME(const uint8_t* WEBP_RESTRICT y, \ + const uint8_t* WEBP_RESTRICT u, \ + const uint8_t* WEBP_RESTRICT v, \ + uint8_t* WEBP_RESTRICT dst, int len) { \ int i; \ uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; \ const int t_con_1 = 26149; \ diff --git a/3rdparty/libwebp/src/dsp/yuv_neon.c b/3rdparty/libwebp/src/dsp/yuv_neon.c index ff77b00980..44745cf786 100644 --- a/3rdparty/libwebp/src/dsp/yuv_neon.c +++ b/3rdparty/libwebp/src/dsp/yuv_neon.c @@ -18,6 +18,7 @@ #include #include +#include "src/dsp/dsp.h" #include "src/dsp/neon.h" //----------------------------------------------------------------------------- @@ -46,7 +47,8 @@ static uint8x8_t ConvertRGBToY_NEON(const uint8x8_t R, return vqmovn_u16(Y2); } -static void ConvertRGB24ToY_NEON(const uint8_t* rgb, uint8_t* y, int width) { +static void ConvertRGB24ToY_NEON(const uint8_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT y, int width) { int i; for (i = 0; i + 8 <= width; i += 8, rgb += 3 * 8) { const uint8x8x3_t RGB = vld3_u8(rgb); @@ -58,7 +60,8 @@ static void ConvertRGB24ToY_NEON(const uint8_t* rgb, uint8_t* y, int width) { } } -static void ConvertBGR24ToY_NEON(const uint8_t* bgr, uint8_t* y, int width) { +static void ConvertBGR24ToY_NEON(const uint8_t* WEBP_RESTRICT bgr, + uint8_t* WEBP_RESTRICT y, int width) { int i; for (i = 0; i + 8 <= width; i += 8, bgr += 3 * 8) { const uint8x8x3_t BGR = vld3_u8(bgr); @@ -70,7 +73,8 @@ static void ConvertBGR24ToY_NEON(const uint8_t* bgr, uint8_t* y, int width) { } } -static void ConvertARGBToY_NEON(const uint32_t* argb, uint8_t* y, int width) { +static void ConvertARGBToY_NEON(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT y, int width) { int i; for (i = 0; i + 8 <= width; i += 8) { const uint8x8x4_t RGB = vld4_u8((const uint8_t*)&argb[i]); @@ -114,8 +118,9 @@ static void ConvertARGBToY_NEON(const uint32_t* argb, uint8_t* y, int width) { 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) { +static void ConvertRGBA32ToUV_NEON(const uint16_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT 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); @@ -131,7 +136,9 @@ static void ConvertRGBA32ToUV_NEON(const uint16_t* rgb, } } -static void ConvertARGBToUV_NEON(const uint32_t* argb, uint8_t* u, uint8_t* v, +static void ConvertARGBToUV_NEON(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT v, int src_width, int do_store) { int i; for (i = 0; i + 16 <= src_width; i += 16, u += 8, v += 8) { diff --git a/3rdparty/libwebp/src/dsp/yuv_sse2.c b/3rdparty/libwebp/src/dsp/yuv_sse2.c index 01a48f9af2..f1abf217af 100644 --- a/3rdparty/libwebp/src/dsp/yuv_sse2.c +++ b/3rdparty/libwebp/src/dsp/yuv_sse2.c @@ -14,12 +14,16 @@ #include "src/dsp/yuv.h" #if defined(WEBP_USE_SSE2) - -#include #include +#include + #include "src/dsp/common_sse2.h" +#include "src/dsp/cpu.h" +#include "src/dsp/dsp.h" #include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/types.h" //----------------------------------------------------------------------------- // Convert spans of 32 pixels to various RGB formats for the fancy upsampler. @@ -82,9 +86,9 @@ static WEBP_INLINE __m128i Load_UV_HI_8_SSE2(const uint8_t* src) { } // 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, +static void YUV444ToRGB_SSE2(const uint8_t* WEBP_RESTRICT const y, + const uint8_t* WEBP_RESTRICT const u, + const uint8_t* WEBP_RESTRICT 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), @@ -93,9 +97,9 @@ static void YUV444ToRGB_SSE2(const uint8_t* const y, } // 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, +static void YUV420ToRGB_SSE2(const uint8_t* WEBP_RESTRICT const y, + const uint8_t* WEBP_RESTRICT const u, + const uint8_t* WEBP_RESTRICT 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), @@ -108,7 +112,7 @@ 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) { + uint8_t* WEBP_RESTRICT 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); @@ -120,11 +124,9 @@ static WEBP_INLINE void PackAndStore4_SSE2(const __m128i* const R, } // 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) { +static WEBP_INLINE void PackAndStore4444_SSE2( + const __m128i* const R, const __m128i* const G, const __m128i* const B, + const __m128i* const A, uint8_t* WEBP_RESTRICT const dst) { #if (WEBP_SWAP_16BIT_CSP == 0) const __m128i rg0 = _mm_packus_epi16(*R, *G); const __m128i ba0 = _mm_packus_epi16(*B, *A); @@ -145,7 +147,7 @@ static WEBP_INLINE void PackAndStore4444_SSE2(const __m128i* const R, static WEBP_INLINE void PackAndStore565_SSE2(const __m128i* const R, const __m128i* const G, const __m128i* const B, - uint8_t* const dst) { + uint8_t* WEBP_RESTRICT 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); @@ -170,7 +172,7 @@ static WEBP_INLINE void PackAndStore565_SSE2(const __m128i* const R, 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) { + uint8_t* WEBP_RESTRICT 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 @@ -193,8 +195,10 @@ static WEBP_INLINE void PlanarTo24b_SSE2(__m128i* const in0, __m128i* const in1, _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) { +void VP8YuvToRgba32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst) { const __m128i kAlpha = _mm_set1_epi16(255); int n; for (n = 0; n < 32; n += 8, dst += 32) { @@ -204,8 +208,10 @@ void VP8YuvToRgba32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, } } -void VP8YuvToBgra32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, - uint8_t* dst) { +void VP8YuvToBgra32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst) { const __m128i kAlpha = _mm_set1_epi16(255); int n; for (n = 0; n < 32; n += 8, dst += 32) { @@ -215,8 +221,10 @@ void VP8YuvToBgra32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, } } -void VP8YuvToArgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, - uint8_t* dst) { +void VP8YuvToArgb32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst) { const __m128i kAlpha = _mm_set1_epi16(255); int n; for (n = 0; n < 32; n += 8, dst += 32) { @@ -226,8 +234,10 @@ void VP8YuvToArgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, } } -void VP8YuvToRgba444432_SSE2(const uint8_t* y, const uint8_t* u, - const uint8_t* v, uint8_t* dst) { +void VP8YuvToRgba444432_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst) { const __m128i kAlpha = _mm_set1_epi16(255); int n; for (n = 0; n < 32; n += 8, dst += 16) { @@ -237,8 +247,10 @@ void VP8YuvToRgba444432_SSE2(const uint8_t* y, const uint8_t* u, } } -void VP8YuvToRgb56532_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, - uint8_t* dst) { +void VP8YuvToRgb56532_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst) { int n; for (n = 0; n < 32; n += 8, dst += 16) { __m128i R, G, B; @@ -247,8 +259,10 @@ void VP8YuvToRgb56532_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, } } -void VP8YuvToRgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, - uint8_t* dst) { +void VP8YuvToRgb32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst) { __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5; @@ -269,8 +283,10 @@ void VP8YuvToRgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, 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) { +void VP8YuvToBgr32_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst) { __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5; @@ -294,9 +310,10 @@ void VP8YuvToBgr32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v, //----------------------------------------------------------------------------- // 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) { +static void YuvToRgbaRow_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int len) { const __m128i kAlpha = _mm_set1_epi16(255); int n; for (n = 0; n + 8 <= len; n += 8, dst += 32) { @@ -316,9 +333,10 @@ static void YuvToRgbaRow_SSE2(const uint8_t* y, } } -static void YuvToBgraRow_SSE2(const uint8_t* y, - const uint8_t* u, const uint8_t* v, - uint8_t* dst, int len) { +static void YuvToBgraRow_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int len) { const __m128i kAlpha = _mm_set1_epi16(255); int n; for (n = 0; n + 8 <= len; n += 8, dst += 32) { @@ -338,9 +356,10 @@ static void YuvToBgraRow_SSE2(const uint8_t* y, } } -static void YuvToArgbRow_SSE2(const uint8_t* y, - const uint8_t* u, const uint8_t* v, - uint8_t* dst, int len) { +static void YuvToArgbRow_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst, int len) { const __m128i kAlpha = _mm_set1_epi16(255); int n; for (n = 0; n + 8 <= len; n += 8, dst += 32) { @@ -360,9 +379,10 @@ static void YuvToArgbRow_SSE2(const uint8_t* y, } } -static void YuvToRgbRow_SSE2(const uint8_t* y, - const uint8_t* u, const uint8_t* v, - uint8_t* dst, int len) { +static void YuvToRgbRow_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT 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; @@ -397,9 +417,10 @@ static void YuvToRgbRow_SSE2(const uint8_t* y, } } -static void YuvToBgrRow_SSE2(const uint8_t* y, - const uint8_t* u, const uint8_t* v, - uint8_t* dst, int len) { +static void YuvToBgrRow_SSE2(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT 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; @@ -471,7 +492,7 @@ static WEBP_INLINE void RGB24PackedToPlanarHelper_SSE2( // 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]*/) { + const uint8_t* WEBP_RESTRICT 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)); @@ -488,8 +509,8 @@ static WEBP_INLINE void RGB24PackedToPlanar_SSE2( } // Convert 8 packed ARGB to r[], g[], b[] -static WEBP_INLINE void RGB32PackedToPlanar_SSE2(const uint32_t* const argb, - __m128i* const rgb /*in[6]*/) { +static WEBP_INLINE void RGB32PackedToPlanar_SSE2( + const uint32_t* WEBP_RESTRICT 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); @@ -562,7 +583,8 @@ static WEBP_INLINE void ConvertRGBToUV_SSE2(const __m128i* const R, #undef MK_CST_16 #undef TRANSFORM -static void ConvertRGB24ToY_SSE2(const uint8_t* rgb, uint8_t* y, int width) { +static void ConvertRGB24ToY_SSE2(const uint8_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT y, int width) { const int max_width = width & ~31; int i; for (i = 0; i < max_width; rgb += 3 * 16 * 2) { @@ -596,7 +618,8 @@ static void ConvertRGB24ToY_SSE2(const uint8_t* rgb, uint8_t* y, int width) { } } -static void ConvertBGR24ToY_SSE2(const uint8_t* bgr, uint8_t* y, int width) { +static void ConvertBGR24ToY_SSE2(const uint8_t* WEBP_RESTRICT bgr, + uint8_t* WEBP_RESTRICT y, int width) { const int max_width = width & ~31; int i; for (i = 0; i < max_width; bgr += 3 * 16 * 2) { @@ -630,7 +653,8 @@ static void ConvertBGR24ToY_SSE2(const uint8_t* bgr, uint8_t* y, int width) { } } -static void ConvertARGBToY_SSE2(const uint32_t* argb, uint8_t* y, int width) { +static void ConvertARGBToY_SSE2(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT y, int width) { const int max_width = width & ~15; int i; for (i = 0; i < max_width; i += 16) { @@ -658,8 +682,9 @@ static void HorizontalAddPack_SSE2(const __m128i* const A, *out = _mm_packs_epi32(C, D); } -static void ConvertARGBToUV_SSE2(const uint32_t* argb, - uint8_t* u, uint8_t* v, +static void ConvertARGBToUV_SSE2(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT v, int src_width, int do_store) { const int max_width = src_width & ~31; int i; @@ -695,7 +720,7 @@ static void ConvertARGBToUV_SSE2(const uint32_t* argb, // Convert 16 packed ARGB 16b-values to r[], g[], b[] static WEBP_INLINE void RGBA32PackedToPlanar_16b_SSE2( - const uint16_t* const rgbx, + const uint16_t* WEBP_RESTRICT 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 @@ -715,8 +740,9 @@ static WEBP_INLINE void RGBA32PackedToPlanar_16b_SSE2( *b = _mm_unpacklo_epi64(B1, B3); } -static void ConvertRGBA32ToUV_SSE2(const uint16_t* rgb, - uint8_t* u, uint8_t* v, int width) { +static void ConvertRGBA32ToUV_SSE2(const uint16_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT v, int width) { const int max_width = width & ~15; const uint16_t* const last_rgb = rgb + 4 * max_width; while (rgb < last_rgb) { diff --git a/3rdparty/libwebp/src/dsp/yuv_sse41.c b/3rdparty/libwebp/src/dsp/yuv_sse41.c index f79b802e47..e1b8084648 100644 --- a/3rdparty/libwebp/src/dsp/yuv_sse41.c +++ b/3rdparty/libwebp/src/dsp/yuv_sse41.c @@ -14,12 +14,17 @@ #include "src/dsp/yuv.h" #if defined(WEBP_USE_SSE41) - -#include +#include #include +#include + #include "src/dsp/common_sse41.h" +#include "src/dsp/cpu.h" +#include "src/dsp/dsp.h" #include "src/utils/utils.h" +#include "src/webp/decode.h" +#include "src/webp/types.h" //----------------------------------------------------------------------------- // Convert spans of 32 pixels to various RGB formats for the fancy upsampler. @@ -82,9 +87,9 @@ static WEBP_INLINE __m128i Load_UV_HI_8_SSE41(const uint8_t* src) { } // 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, +static void YUV444ToRGB_SSE41(const uint8_t* WEBP_RESTRICT const y, + const uint8_t* WEBP_RESTRICT const u, + const uint8_t* WEBP_RESTRICT 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), @@ -93,9 +98,9 @@ static void YUV444ToRGB_SSE41(const uint8_t* const y, } // 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, +static void YUV420ToRGB_SSE41(const uint8_t* WEBP_RESTRICT const y, + const uint8_t* WEBP_RESTRICT const u, + const uint8_t* WEBP_RESTRICT 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), @@ -109,7 +114,7 @@ static void YUV420ToRGB_SSE41(const uint8_t* const y, 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) { + uint8_t* WEBP_RESTRICT 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 @@ -132,8 +137,10 @@ static WEBP_INLINE void PlanarTo24b_SSE41( _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) { +void VP8YuvToRgb32_SSE41(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst) { __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5; @@ -154,8 +161,10 @@ void VP8YuvToRgb32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v, 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) { +void VP8YuvToBgr32_SSE41(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT dst) { __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5; @@ -179,9 +188,10 @@ void VP8YuvToBgr32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v, //----------------------------------------------------------------------------- // 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) { +static void YuvToRgbRow_SSE41(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT 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; @@ -216,9 +226,10 @@ static void YuvToRgbRow_SSE41(const uint8_t* y, } } -static void YuvToBgrRow_SSE41(const uint8_t* y, - const uint8_t* u, const uint8_t* v, - uint8_t* dst, int len) { +static void YuvToBgrRow_SSE41(const uint8_t* WEBP_RESTRICT y, + const uint8_t* WEBP_RESTRICT u, + const uint8_t* WEBP_RESTRICT v, + uint8_t* WEBP_RESTRICT 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; @@ -290,7 +301,7 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersSSE41(void) { // 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 uint8_t* WEBP_RESTRICT 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)); @@ -334,7 +345,7 @@ static WEBP_INLINE void RGB24PackedToPlanar_SSE41( // 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 uint32_t* WEBP_RESTRICT 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); @@ -407,7 +418,8 @@ static WEBP_INLINE void ConvertRGBToUV_SSE41(const __m128i* const R, #undef MK_CST_16 #undef TRANSFORM -static void ConvertRGB24ToY_SSE41(const uint8_t* rgb, uint8_t* y, int width) { +static void ConvertRGB24ToY_SSE41(const uint8_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT y, int width) { const int max_width = width & ~31; int i; for (i = 0; i < max_width; rgb += 3 * 16 * 2) { @@ -441,7 +453,8 @@ static void ConvertRGB24ToY_SSE41(const uint8_t* rgb, uint8_t* y, int width) { } } -static void ConvertBGR24ToY_SSE41(const uint8_t* bgr, uint8_t* y, int width) { +static void ConvertBGR24ToY_SSE41(const uint8_t* WEBP_RESTRICT bgr, + uint8_t* WEBP_RESTRICT y, int width) { const int max_width = width & ~31; int i; for (i = 0; i < max_width; bgr += 3 * 16 * 2) { @@ -475,7 +488,8 @@ static void ConvertBGR24ToY_SSE41(const uint8_t* bgr, uint8_t* y, int width) { } } -static void ConvertARGBToY_SSE41(const uint32_t* argb, uint8_t* y, int width) { +static void ConvertARGBToY_SSE41(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT y, int width) { const int max_width = width & ~15; int i; for (i = 0; i < max_width; i += 16) { @@ -503,8 +517,9 @@ static void HorizontalAddPack_SSE41(const __m128i* const A, *out = _mm_packs_epi32(C, D); } -static void ConvertARGBToUV_SSE41(const uint32_t* argb, - uint8_t* u, uint8_t* v, +static void ConvertARGBToUV_SSE41(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT v, int src_width, int do_store) { const int max_width = src_width & ~31; int i; @@ -540,7 +555,7 @@ static void ConvertARGBToUV_SSE41(const uint32_t* argb, // Convert 16 packed ARGB 16b-values to r[], g[], b[] static WEBP_INLINE void RGBA32PackedToPlanar_16b_SSE41( - const uint16_t* const rgbx, + const uint16_t* WEBP_RESTRICT 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 @@ -570,8 +585,9 @@ static WEBP_INLINE void RGBA32PackedToPlanar_16b_SSE41( *b = _mm_unpackhi_epi64(B1, B3); } -static void ConvertRGBA32ToUV_SSE41(const uint16_t* rgb, - uint8_t* u, uint8_t* v, int width) { +static void ConvertRGBA32ToUV_SSE41(const uint16_t* WEBP_RESTRICT rgb, + uint8_t* WEBP_RESTRICT u, + uint8_t* WEBP_RESTRICT v, int width) { const int max_width = width & ~15; const uint16_t* const last_rgb = rgb + 4 * max_width; while (rgb < last_rgb) { diff --git a/3rdparty/libwebp/src/enc/alpha_enc.c b/3rdparty/libwebp/src/enc/alpha_enc.c index c11a261c8a..7153fc24ea 100644 --- a/3rdparty/libwebp/src/enc/alpha_enc.c +++ b/3rdparty/libwebp/src/enc/alpha_enc.c @@ -15,10 +15,13 @@ #include #include -#include "src/enc/vp8i_enc.h" #include "src/dsp/dsp.h" +#include "src/webp/types.h" +#include "src/enc/vp8i_enc.h" +#include "src/utils/bit_writer_utils.h" #include "src/utils/filters_utils.h" #include "src/utils/quant_levels_utils.h" +#include "src/utils/thread_utils.h" #include "src/utils/utils.h" #include "src/webp/encode.h" #include "src/webp/format_constants.h" @@ -86,7 +89,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height, ok = VP8LEncodeStream(&config, &picture, bw); WebPPictureFree(&picture); - ok = ok && !bw->error_; + ok = ok && !bw->error; if (!ok) { VP8LBitWriterWipeOut(bw); return 0; @@ -138,7 +141,7 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, !reduce_levels, &tmp_bw, &result->stats); if (ok) { output = VP8LBitWriterFinish(&tmp_bw); - if (tmp_bw.error_) { + if (tmp_bw.error) { VP8LBitWriterWipeOut(&tmp_bw); memset(&result->bw, 0, sizeof(result->bw)); return 0; @@ -173,7 +176,7 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, if (method != ALPHA_NO_COMPRESSION) { VP8LBitWriterWipeOut(&tmp_bw); } - ok = ok && !result->bw.error_; + ok = ok && !result->bw.error; result->score = VP8BitWriterSize(&result->bw); return ok; } @@ -276,6 +279,7 @@ static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height, stats->lossless_features = best.stats.lossless_features; stats->histogram_bits = best.stats.histogram_bits; stats->transform_bits = best.stats.transform_bits; + stats->cross_color_transform_bits = best.stats.cross_color_transform_bits; stats->cache_bits = best.stats.cache_bits; stats->palette_size = best.stats.palette_size; stats->lossless_size = best.stats.lossless_size; @@ -297,7 +301,7 @@ 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 WebPPicture* const pic = enc->pic; const int width = pic->width; const int height = pic->height; @@ -356,7 +360,7 @@ static int EncodeAlpha(VP8Encoder* const enc, #if !defined(WEBP_DISABLE_STATS) if (pic->stats != NULL) { // need stats? pic->stats->coded_size += (int)(*output_size); - enc->sse_[3] = sse; + enc->sse[3] = sse; } #endif } @@ -370,7 +374,7 @@ static int EncodeAlpha(VP8Encoder* const enc, static int CompressAlphaJob(void* arg1, void* unused) { VP8Encoder* const enc = (VP8Encoder*)arg1; - const WebPConfig* config = enc->config_; + 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] @@ -386,19 +390,19 @@ static int CompressAlphaJob(void* arg1, void* unused) { WebPSafeFree(alpha_data); return 0; } - enc->alpha_data_size_ = (uint32_t)alpha_size; - enc->alpha_data_ = alpha_data; + 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_; + 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; @@ -407,12 +411,12 @@ void VP8EncInitAlpha(VP8Encoder* const enc) { } int VP8EncStartAlpha(VP8Encoder* const enc) { - if (enc->has_alpha_) { - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; + 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 WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } WebPGetWorkerInterface()->Launch(worker); return 1; @@ -424,27 +428,27 @@ int VP8EncStartAlpha(VP8Encoder* const enc) { } int VP8EncFinishAlpha(VP8Encoder* const enc) { - if (enc->has_alpha_) { - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; + 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_); + 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_; + 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; + WebPSafeFree(enc->alpha_data); + enc->alpha_data = NULL; + enc->alpha_data_size = 0; + enc->has_alpha = 0; return ok; } diff --git a/3rdparty/libwebp/src/enc/analysis_enc.c b/3rdparty/libwebp/src/enc/analysis_enc.c index 962eaa998f..f80f61e28a 100644 --- a/3rdparty/libwebp/src/enc/analysis_enc.c +++ b/3rdparty/libwebp/src/enc/analysis_enc.c @@ -11,13 +11,17 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include #include #include -#include +#include "src/dec/common_dec.h" +#include "src/dsp/dsp.h" #include "src/enc/vp8i_enc.h" -#include "src/enc/cost_enc.h" +#include "src/utils/thread_utils.h" #include "src/utils/utils.h" +#include "src/webp/encode.h" +#include "src/webp/types.h" #define MAX_ITERS_K_MEANS 6 @@ -27,8 +31,8 @@ static void SmoothSegmentMap(VP8Encoder* const enc) { int n, x, y; - const int w = enc->mb_w_; - const int h = enc->mb_h_; + 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 @@ -37,17 +41,17 @@ static void SmoothSegmentMap(VP8Encoder* const enc) { 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_; + 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 + 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; @@ -59,15 +63,15 @@ static void SmoothSegmentMap(VP8Encoder* const enc) { } 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]; + VP8MBInfo* const mb = &enc->mb_info[x + w * y]; + mb->segment = tmp[x + y * w]; } } WebPSafeFree(tmp); } //------------------------------------------------------------------------------ -// set segment susceptibility alpha_ / beta_ +// set segment susceptibility 'alpha' / 'beta' static WEBP_INLINE int clip(int v, int m, int M) { return (v < m) ? m : (v > M) ? M : v; @@ -76,7 +80,7 @@ static WEBP_INLINE int clip(int v, int m, int M) { static void SetSegmentAlphas(VP8Encoder* const enc, const int centers[NUM_MB_SEGMENTS], int mid) { - const int nb = enc->segment_hdr_.num_segments_; + const int nb = enc->segment_hdr.num_segments; int min = centers[0], max = centers[0]; int n; @@ -91,8 +95,8 @@ static void SetSegmentAlphas(VP8Encoder* const enc, 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); + enc->dqm[n].alpha = clip(alpha, -127, 127); + enc->dqm[n].beta = clip(beta, 0, 255); } } @@ -131,11 +135,11 @@ static void InitHistogram(VP8Histogram* const histo) { static void AssignSegments(VP8Encoder* const enc, const int alphas[MAX_ALPHA + 1]) { - // 'num_segments_' is previously validated and <= NUM_MB_SEGMENTS, but an + // '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; + 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]; @@ -200,15 +204,15 @@ static void AssignSegments(VP8Encoder* const enc, } // 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. + 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); + const int smooth = (enc->config->preprocessing & 1); if (smooth) SmoothSegmentMap(enc); } @@ -220,7 +224,7 @@ static void AssignSegments(VP8Encoder* const enc, // 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 +// 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 @@ -239,8 +243,8 @@ static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) { int alpha; InitHistogram(&histo); - VP8CollectHistogram(it->yuv_in_ + Y_OFF_ENC, - it->yuv_p_ + VP8I16ModeOffsets[mode], + 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)) { @@ -255,12 +259,12 @@ static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) { 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 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]); + VP8Mean16x4(it->yuv_in + Y_OFF_ENC + k * BPS, &dc[k]); } for (m = 0, m2 = 0, k = 0; k < 16; ++k) { m += dc[k]; @@ -287,8 +291,8 @@ static int MBAnalyzeBestUVMode(VP8EncIterator* const it) { VP8Histogram histo; int alpha; InitHistogram(&histo); - VP8CollectHistogram(it->yuv_in_ + U_OFF_ENC, - it->yuv_p_ + VP8UVModeOffsets[mode], + 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)) { @@ -307,14 +311,14 @@ static int MBAnalyzeBestUVMode(VP8EncIterator* const it) { static void MBAnalyze(VP8EncIterator* const it, int alphas[MAX_ALPHA + 1], int* const alpha, int* const uv_alpha) { - const VP8Encoder* const enc = it->enc_; + 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) { + if (enc->method <= 1) { best_alpha = FastMBAnalyze(it); } else { best_alpha = MBAnalyzeBestIntra16Mode(it); @@ -325,7 +329,7 @@ static void MBAnalyze(VP8EncIterator* const it, 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. + it->mb->alpha = best_alpha; // for later remapping. // Accumulate for later complexity analysis. *alpha += best_alpha; // mixed susceptibility (not just luma) @@ -333,11 +337,11 @@ static void MBAnalyze(VP8EncIterator* const it, } 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; + mb->type = 1; // I16x16 + mb->uv_mode = 0; + mb->skip = 0; // not skipped + mb->segment = 0; // default segment + mb->alpha = 0; } //------------------------------------------------------------------------------ @@ -352,16 +356,16 @@ static void DefaultMBInfo(VP8MBInfo* const mb) { static void ResetAllMBInfo(VP8Encoder* const enc) { int n; - for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { - DefaultMBInfo(&enc->mb_info_[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_); + 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 @@ -409,7 +413,7 @@ static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job, job->worker.hook = DoSegmentsJob; VP8IteratorInit(enc, &job->it); VP8IteratorSetRow(&job->it, start_row); - VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_); + VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w); memset(job->alphas, 0, sizeof(job->alphas)); job->alpha = 0; job->uv_alpha = 0; @@ -422,17 +426,17 @@ static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job, 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. + 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_; - const int total_mb = last_row * enc->mb_w_; + const int last_row = enc->mb_h; + const int total_mb = last_row * enc->mb_w; #ifdef WEBP_USE_THREAD // We give a little more than a half work to the main thread. const int split_row = (9 * last_row + 15) >> 4; const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it - const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow); + const int do_mt = (enc->thread_level > 0) && (split_row >= kMinSplitRow); #else const int do_mt = 0; #endif @@ -467,17 +471,16 @@ int VP8EncAnalyze(VP8Encoder* const enc) { } worker_interface->End(&main_job.worker); if (ok) { - enc->alpha_ = main_job.alpha / total_mb; - enc->uv_alpha_ = main_job.uv_alpha / total_mb; + 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); } if (!ok) { - return WebPEncodingSetError(enc->pic_, + return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY); // imprecise } return ok; } - diff --git a/3rdparty/libwebp/src/enc/backward_references_cost_enc.c b/3rdparty/libwebp/src/enc/backward_references_cost_enc.c index 6968ef3c9f..6474dd6703 100644 --- a/3rdparty/libwebp/src/enc/backward_references_cost_enc.c +++ b/3rdparty/libwebp/src/enc/backward_references_cost_enc.c @@ -15,13 +15,15 @@ // #include -#include +#include #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" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" #define VALUES_IN_BYTE 256 @@ -31,15 +33,15 @@ extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs, const PixOrCopy v); typedef struct { - float alpha_[VALUES_IN_BYTE]; - float red_[VALUES_IN_BYTE]; - float blue_[VALUES_IN_BYTE]; - float distance_[NUM_DISTANCE_CODES]; - float* literal_; + uint32_t alpha[VALUES_IN_BYTE]; + uint32_t red[VALUES_IN_BYTE]; + uint32_t blue[VALUES_IN_BYTE]; + uint32_t distance[NUM_DISTANCE_CODES]; + uint32_t* literal; } CostModel; static void ConvertPopulationCountTableToBitEstimates( - int num_symbols, const uint32_t population_counts[], float output[]) { + int num_symbols, const uint32_t population_counts[], uint32_t output[]) { uint32_t sum = 0; int nonzeros = 0; int i; @@ -52,7 +54,7 @@ static void ConvertPopulationCountTableToBitEstimates( if (nonzeros <= 1) { memset(output, 0, num_symbols * sizeof(*output)); } else { - const float logsum = VP8LFastLog2(sum); + const uint32_t logsum = VP8LFastLog2(sum); for (i = 0; i < num_symbols; ++i) { output[i] = logsum - VP8LFastLog2(population_counts[i]); } @@ -62,30 +64,25 @@ static void ConvertPopulationCountTableToBitEstimates( 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); - } + VP8LHistogramStoreRefs(refs, VP8LDistanceToPlaneCode, xsize, histo); ConvertPopulationCountTableToBitEstimates( - VP8LHistogramNumCodes(histo->palette_code_bits_), histo->literal_, - m->literal_); + VP8LHistogramNumCodes(histo->palette_code_bits), histo->literal, + m->literal); ConvertPopulationCountTableToBitEstimates( - VALUES_IN_BYTE, histo->red_, m->red_); + VALUES_IN_BYTE, histo->red, m->red); ConvertPopulationCountTableToBitEstimates( - VALUES_IN_BYTE, histo->blue_, m->blue_); + VALUES_IN_BYTE, histo->blue, m->blue); ConvertPopulationCountTableToBitEstimates( - VALUES_IN_BYTE, histo->alpha_, m->alpha_); + VALUES_IN_BYTE, histo->alpha, m->alpha); ConvertPopulationCountTableToBitEstimates( - NUM_DISTANCE_CODES, histo->distance_, m->distance_); + NUM_DISTANCE_CODES, histo->distance, m->distance); ok = 1; Error: @@ -93,47 +90,47 @@ static int CostModelBuild(CostModel* const m, int xsize, int cache_bits, return ok; } -static WEBP_INLINE float 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 int64_t GetLiteralCost(const CostModel* const m, + uint32_t v) { + return (int64_t)m->alpha[v >> 24] + m->red[(v >> 16) & 0xff] + + m->literal[(v >> 8) & 0xff] + m->blue[v & 0xff]; } -static WEBP_INLINE float GetCacheCost(const CostModel* const m, uint32_t idx) { +static WEBP_INLINE int64_t GetCacheCost(const CostModel* const m, + uint32_t idx) { const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx; - return m->literal_[literal_idx]; + return (int64_t)m->literal[literal_idx]; } -static WEBP_INLINE float GetLengthCost(const CostModel* const m, - uint32_t length) { +static WEBP_INLINE int64_t 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; + return (int64_t)m->literal[VALUES_IN_BYTE + code] + + ((int64_t)extra_bits << LOG_2_PRECISION_BITS); } -static WEBP_INLINE float GetDistanceCost(const CostModel* const m, - uint32_t distance) { +static WEBP_INLINE int64_t GetDistanceCost(const CostModel* const m, + uint32_t distance) { int code, extra_bits; VP8LPrefixEncodeBits(distance, &code, &extra_bits); - return m->distance_[code] + extra_bits; + return (int64_t)m->distance[code] + + ((int64_t)extra_bits << LOG_2_PRECISION_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) { - float cost_val = prev_cost; + int64_t prev_cost, int64_t* const cost, uint16_t* const dist_array) { + int64_t 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 float mul0 = 0.68f; - cost_val += GetCacheCost(cost_model, ix) * mul0; + cost_val += DivRound(GetCacheCost(cost_model, ix) * 68, 100); } else { - const float mul1 = 0.82f; if (use_color_cache) VP8LColorCacheInsert(hashers, color); - cost_val += GetLiteralCost(cost_model, color) * mul1; + cost_val += DivRound(GetLiteralCost(cost_model, color) * 82, 100); } if (cost[idx] > cost_val) { cost[idx] = cost_val; @@ -147,83 +144,84 @@ static WEBP_INLINE void AddSingleLiteralWithCostModel( // 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 +// 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) +// 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 +// 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_; + int64_t cost; + int start; + int end; + int index; + CostInterval* previous; + CostInterval* next; }; // The GetLengthCost(cost_model, k) are cached in a CostCacheInterval. typedef struct { - float cost_; - int start_; - int end_; // Exclusive. + int64_t 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). +// 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_; - float cost_cache_[MAX_LENGTH]; // Contains the GetLengthCost(cost_model, k). - float* costs_; - uint16_t* dist_array_; + CostInterval* head; + int count; // The number of stored intervals. + CostCacheInterval* cache_intervals; + size_t cache_intervals_size; + // Contains the GetLengthCost(cost_model, k). + int64_t cost_cache[MAX_LENGTH]; + int64_t* 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_; + 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_; + CostInterval* recycled_intervals; } CostManager; static void CostIntervalAddToFreeList(CostManager* const manager, CostInterval* const interval) { - interval->next_ = manager->free_intervals_; - manager->free_intervals_ = 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]); + 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; + manager->free_intervals = NULL; for (i = 0; i < COST_MANAGER_MAX_FREE_LIST; ++i) { - CostIntervalAddToFreeList(manager, &manager->intervals_[i]); + CostIntervalAddToFreeList(manager, &manager->intervals[i]); } } static void DeleteIntervalList(CostManager* const manager, const CostInterval* interval) { while (interval != NULL) { - const CostInterval* const next = interval->next_; + const CostInterval* const next = interval->next; if (!CostIntervalIsInFreeList(manager, interval)) { WebPSafeFree((void*)interval); } // else: do nothing @@ -234,16 +232,16 @@ static void DeleteIntervalList(CostManager* const manager, static void CostManagerClear(CostManager* const manager) { if (manager == NULL) return; - WebPSafeFree(manager->costs_); - WebPSafeFree(manager->cache_intervals_); + 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; + DeleteIntervalList(manager, manager->head); + manager->head = NULL; + DeleteIntervalList(manager, manager->recycled_intervals); + manager->recycled_intervals = NULL; - // Reset pointers, count_ and cache_intervals_size_. + // Reset pointers, 'count' and 'cache_intervals_size'. memset(manager, 0, sizeof(*manager)); CostManagerInitFreeList(manager); } @@ -254,25 +252,25 @@ static int CostManagerInit(CostManager* const manager, 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; + 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_. + // Fill in the 'cost_cache'. // Has to be done in two passes due to a GCC bug on i686 // related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 for (i = 0; i < cost_cache_size; ++i) { - manager->cost_cache_[i] = GetLengthCost(cost_model, i); + manager->cost_cache[i] = GetLengthCost(cost_model, i); } - manager->cache_intervals_size_ = 1; + manager->cache_intervals_size = 1; for (i = 1; i < cost_cache_size; ++i) { // Get the number of bound intervals. - if (manager->cost_cache_[i] != manager->cost_cache_[i - 1]) { - ++manager->cache_intervals_size_; + if (manager->cost_cache[i] != manager->cost_cache[i - 1]) { + ++manager->cache_intervals_size; } } @@ -280,44 +278,46 @@ static int CostManagerInit(CostManager* const manager, // 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) { + 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_. + // Fill in the 'cache_intervals'. { - CostCacheInterval* cur = manager->cache_intervals_; + CostCacheInterval* cur = manager->cache_intervals; - // Consecutive values in cost_cache_ are compared and if a big enough + // 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]; + cur->start = 0; + cur->end = 1; + cur->cost = manager->cost_cache[0]; for (i = 1; i < cost_cache_size; ++i) { - const float cost_val = manager->cost_cache_[i]; - if (cost_val != cur->cost_) { + const int64_t cost_val = manager->cost_cache[i]; + if (cost_val != cur->cost) { ++cur; // Initialize an interval. - cur->start_ = i; - cur->cost_ = cost_val; + cur->start = i; + cur->cost = cost_val; } - cur->end_ = i + 1; + cur->end = i + 1; } - assert((size_t)(cur - manager->cache_intervals_) + 1 == - manager->cache_intervals_size_); + assert((size_t)(cur - manager->cache_intervals) + 1 == + manager->cache_intervals_size); } - manager->costs_ = (float*)WebPSafeMalloc(pix_count, sizeof(*manager->costs_)); - if (manager->costs_ == NULL) { + manager->costs = + (int64_t*)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] = FLT_MAX; + // Set the initial 'costs' to INT64_MAX for every pixel as we will keep the + // minimum. + for (i = 0; i < pix_count; ++i) manager->costs[i] = WEBP_INT64_MAX; return 1; } @@ -325,13 +325,13 @@ static int CostManagerInit(CostManager* const manager, // 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) { + int position, int64_t 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; + if (manager->costs[i] > cost) { + manager->costs[i] = cost; + manager->dist_array[i] = k + 1; } } @@ -339,7 +339,7 @@ static WEBP_INLINE void UpdateCost(CostManager* const manager, int i, // all the pixels between 'start' and 'end' excluded. static WEBP_INLINE void UpdateCostPerInterval(CostManager* const manager, int start, int end, int position, - float cost) { + int64_t cost) { int i; for (i = start; i < end; ++i) UpdateCost(manager, i, position, cost); } @@ -349,12 +349,12 @@ static WEBP_INLINE void ConnectIntervals(CostManager* const manager, CostInterval* const prev, CostInterval* const next) { if (prev != NULL) { - prev->next_ = next; + prev->next = next; } else { - manager->head_ = next; + manager->head = next; } - if (next != NULL) next->previous_ = prev; + if (next != NULL) next->previous = prev; } // Pop an interval in the manager. @@ -362,15 +362,15 @@ static WEBP_INLINE void PopInterval(CostManager* const manager, CostInterval* const interval) { if (interval == NULL) return; - ConnectIntervals(manager, interval->previous_, interval->next_); + 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; + interval->next = manager->recycled_intervals; + manager->recycled_intervals = interval; } - --manager->count_; - assert(manager->count_ >= 0); + --manager->count; + assert(manager->count >= 0); } // Update the cost at index i by going over all the stored intervals that @@ -379,17 +379,17 @@ static WEBP_INLINE void PopInterval(CostManager* const manager, // end before 'i' will be popped. static WEBP_INLINE void UpdateCostAtIndex(CostManager* const manager, int i, int do_clean_intervals) { - CostInterval* current = manager->head_; + CostInterval* current = manager->head; - while (current != NULL && current->start_ <= i) { - CostInterval* const next = current->next_; - if (current->end_ <= i) { + 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_); + UpdateCost(manager, i, current->index, current->cost); } current = next; } @@ -397,49 +397,49 @@ static WEBP_INLINE void UpdateCostAtIndex(CostManager* const manager, int i, // 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. +// 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_; + 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_; + while (previous != NULL && previous->next != NULL && + previous->next->start < current->start) { + previous = previous->next; } if (previous != NULL) { - ConnectIntervals(manager, current, previous->next_); + ConnectIntervals(manager, current, previous->next); } else { - ConnectIntervals(manager, current, manager->head_); + 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. +// '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, + int64_t cost, int position, int start, int end) { CostInterval* interval_new; if (start >= end) return; - if (manager->count_ >= COST_CACHE_INTERVAL_SIZE_MAX) { + 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_; + 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) { @@ -449,13 +449,13 @@ static WEBP_INLINE void InsertInterval(CostManager* const manager, } } - interval_new->cost_ = cost; - interval_new->index_ = position; - interval_new->start_ = start; - interval_new->end_ = end; + interval_new->cost = cost; + interval_new->index = position; + interval_new->start = start; + interval_new->end = end; PositionOrphanInterval(manager, interval_new, interval_in); - ++manager->count_; + ++manager->count; } // Given a new cost interval defined by its start at position, its length value @@ -463,13 +463,13 @@ static WEBP_INLINE void InsertInterval(CostManager* const manager, // 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, - float distance_cost, int position, + int64_t distance_cost, int position, int len) { size_t i; - CostInterval* interval = manager->head_; + CostInterval* interval = manager->head; CostInterval* interval_next; const CostCacheInterval* const cost_cache_intervals = - manager->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; @@ -478,86 +478,86 @@ static WEBP_INLINE void PushInterval(CostManager* const manager, int j; for (j = position; j < position + len; ++j) { const int k = j - position; - float cost_tmp; + int64_t cost_tmp; assert(k >= 0 && k < MAX_LENGTH); - cost_tmp = distance_cost + manager->cost_cache_[k]; + cost_tmp = distance_cost + manager->cost_cache[k]; - if (manager->costs_[j] > cost_tmp) { - manager->costs_[j] = cost_tmp; - manager->dist_array_[j] = k + 1; + 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; + 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 + 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 = distance_cost + cost_cache_intervals[i].cost_; + : cost_cache_intervals[i].end); + const int64_t cost = distance_cost + cost_cache_intervals[i].cost; - for (; interval != NULL && interval->start_ < end; + for (; interval != NULL && interval->start < end; interval = interval_next) { - interval_next = interval->next_; + interval_next = interval->next; // Make sure we have some overlap - if (start >= interval->end_) continue; + if (start >= interval->end) continue; - if (cost >= interval->cost_) { + if (cost >= interval->cost) { // When intervals are represented, the lower, the better. // [**********************************************************[ // start end // [----------------------------------[ - // interval->start_ interval->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_; + const int start_new = interval->end; InsertInterval(manager, interval, cost, position, start, - interval->start_); + interval->start); start = start_new; if (start >= end) break; continue; } - if (start <= interval->start_) { - if (interval->end_ <= end) { + if (start <= interval->start) { + if (interval->end <= end) { // [----------------------------------[ - // interval->start_ interval->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_ + // interval->start interval->end // [*****************************[ // start end - interval->start_ = end; + interval->start = end; break; } } else { - if (end < interval->end_) { + if (end < interval->end) { // [--------------------------------------------------------------[ - // interval->start_ 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_, + const int end_original = interval->end; + interval->end = start; + InsertInterval(manager, interval, interval->cost, interval->index, end, end_original); - interval = interval->next_; + interval = interval->next; break; } else { // [------------------------------------[ - // interval->start_ interval->end_ + // interval->start interval->end // [*****************************[ // start end - interval->end_ = start; + interval->end = start; } } } @@ -576,7 +576,7 @@ static int BackwardReferencesHashChainDistanceOnly( const int pix_count = xsize * ysize; const int use_color_cache = (cache_bits > 0); const size_t literal_array_size = - sizeof(float) * (VP8LHistogramNumCodes(cache_bits)); + sizeof(*((CostModel*)NULL)->literal) * VP8LHistogramNumCodes(cache_bits); const size_t cost_model_size = sizeof(CostModel) + literal_array_size; CostModel* const cost_model = (CostModel*)WebPSafeCalloc(1ULL, cost_model_size); @@ -584,13 +584,13 @@ static int BackwardReferencesHashChainDistanceOnly( CostManager* cost_manager = (CostManager*)WebPSafeCalloc(1ULL, sizeof(*cost_manager)); int offset_prev = -1, len_prev = -1; - float offset_cost = -1.f; + int64_t 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_ = (float*)(cost_model + 1); + cost_model->literal = (uint32_t*)(cost_model + 1); if (use_color_cache) { cc_init = VP8LColorCacheInit(&hashers, cache_bits); if (!cc_init) goto Error; @@ -608,18 +608,19 @@ static int BackwardReferencesHashChainDistanceOnly( // 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); + AddSingleLiteralWithCostModel(argb, &hashers, cost_model, /*idx=*/0, + use_color_cache, /*prev_cost=*/0, + cost_manager->costs, dist_array); for (i = 1; i < pix_count; ++i) { - const float prev_cost = cost_manager->costs_[i - 1]; + const int64_t 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); + cost_manager->costs, dist_array); // If we are dealing with a non-literal. if (len >= 2) { @@ -667,7 +668,7 @@ static int BackwardReferencesHashChainDistanceOnly( UpdateCostAtIndex(cost_manager, j - 1, 0); UpdateCostAtIndex(cost_manager, j, 0); - PushInterval(cost_manager, cost_manager->costs_[j - 1] + offset_cost, + PushInterval(cost_manager, cost_manager->costs[j - 1] + offset_cost, j, len_j); reach = j + len_j - 1; } @@ -679,7 +680,7 @@ static int BackwardReferencesHashChainDistanceOnly( len_prev = len; } - ok = !refs->error_; + ok = !refs->error; Error: if (cc_init) VP8LColorCacheClear(&hashers); CostManagerClear(cost_manager); @@ -752,7 +753,7 @@ static int BackwardReferencesHashChainFollowChosenPath( ++i; } } - ok = !refs->error_; + ok = !refs->error; Error: if (cc_init) VP8LColorCacheClear(&hashers); return ok; diff --git a/3rdparty/libwebp/src/enc/backward_references_enc.c b/3rdparty/libwebp/src/enc/backward_references_enc.c index dc98bf1719..911ce52488 100644 --- a/3rdparty/libwebp/src/enc/backward_references_enc.c +++ b/3rdparty/libwebp/src/enc/backward_references_enc.c @@ -13,10 +13,9 @@ #include "src/enc/backward_references_enc.h" #include -#include -#include +#include -#include "src/dsp/dsp.h" +#include "src/dsp/cpu.h" #include "src/dsp/lossless.h" #include "src/dsp/lossless_common.h" #include "src/enc/histogram_enc.h" @@ -24,11 +23,11 @@ #include "src/utils/color_cache_utils.h" #include "src/utils/utils.h" #include "src/webp/encode.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.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) @@ -80,30 +79,30 @@ static WEBP_INLINE int FindMatchLength(const uint32_t* const array1, // VP8LBackwardRefs struct PixOrCopyBlock { - PixOrCopyBlock* next_; // next block (or NULL) - PixOrCopy* start_; // data start - int size_; // currently used size + 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 + 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; + 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; + while (refs->free_blocks != NULL) { + PixOrCopyBlock* const next = refs->free_blocks->next; + WebPSafeFree(refs->free_blocks); + refs->free_blocks = next; } } @@ -111,79 +110,79 @@ void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) { static void BackwardRefsSwap(VP8LBackwardRefs* const refs1, VP8LBackwardRefs* const refs2) { const int point_to_refs1 = - (refs1->tail_ != NULL && refs1->tail_ == &refs1->refs_); + (refs1->tail != NULL && refs1->tail == &refs1->refs); const int point_to_refs2 = - (refs2->tail_ != NULL && refs2->tail_ == &refs2->refs_); + (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_; + 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_ = + 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_; + 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; + 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; + 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_; + 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_); + sizeof(*b) + refs->block_size * sizeof(*b->start); b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size); if (b == NULL) { - refs->error_ |= 1; + refs->error |= 1; return NULL; } - b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned + b->start = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned } else { // recycle from free-list - refs->free_blocks_ = b->next_; + refs->free_blocks = b->next; } - *refs->tail_ = b; - refs->tail_ = &b->next_; - refs->last_block_ = b; - b->next_ = NULL; - b->size_ = 0; + *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_; + 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_; + 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; } @@ -192,35 +191,35 @@ 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_) { + PixOrCopyBlock* b = refs->last_block; + if (b == NULL || b->size == refs->block_size) { b = BackwardRefsNewBlock(refs); - if (b == NULL) return; // refs->error_ is set + if (b == NULL) return; // refs->error is set } - b->start_[b->size_++] = v; + b->start[b->size++] = v; } // ----------------------------------------------------------------------------- // Hash chains int VP8LHashChainInit(VP8LHashChain* const p, int size) { - assert(p->size_ == 0); - assert(p->offset_length_ == NULL); + 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; + 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_); + WebPSafeFree(p->offset_length); - p->size_ = 0; - p->offset_length_ = NULL; + p->size = 0; + p->offset_length = NULL; } // ----------------------------------------------------------------------------- @@ -269,14 +268,14 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality, 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_; + // 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); + assert(p->size != 0); + assert(p->offset_length != NULL); if (size <= 2) { - p->offset_length_[0] = p->offset_length_[size - 1] = 0; + p->offset_length[0] = p->offset_length[size - 1] = 0; return 1; } @@ -355,7 +354,7 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality, // (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; + 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; @@ -415,7 +414,7 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality, while (1) { assert(best_length <= MAX_LENGTH); assert(best_distance <= WINDOW_SIZE); - p->offset_length_[base_position] = + 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. @@ -509,7 +508,7 @@ static int BackwardReferencesRle(int xsize, int ysize, } } if (use_color_cache) VP8LColorCacheClear(&hashers); - return !refs->error_; + return !refs->error; } static int BackwardReferencesLz77(int xsize, int ysize, @@ -574,7 +573,7 @@ static int BackwardReferencesLz77(int xsize, int ysize, i += len; } - ok = !refs->error_; + ok = !refs->error; Error: if (cc_init) VP8LColorCacheClear(&hashers); return ok; @@ -649,7 +648,7 @@ static int BackwardReferencesLz77Box(int xsize, int ysize, } } - hash_chain->offset_length_[0] = 0; + hash_chain->offset_length[0] = 0; for (i = 1; i < pix_count; ++i) { int ind; int best_length = VP8LHashChainFindLength(hash_chain_best, i); @@ -716,17 +715,17 @@ static int BackwardReferencesLz77Box(int xsize, int ysize, assert(i + best_length <= pix_count); assert(best_length <= MAX_LENGTH); if (best_length <= MIN_LENGTH) { - hash_chain->offset_length_[i] = 0; + hash_chain->offset_length[i] = 0; best_offset_prev = 0; best_length_prev = 0; } else { - hash_chain->offset_length_[i] = + 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; + hash_chain->offset_length[0] = 0; WebPSafeFree(counts_ini); return BackwardReferencesLz77(xsize, ysize, argb, cache_bits, hash_chain, @@ -758,7 +757,7 @@ static int CalculateBestCacheSize(const uint32_t* argb, int quality, int* const best_cache_bits) { int i; const int cache_bits_max = (quality <= 25) ? 0 : *best_cache_bits; - float entropy_min = MAX_ENTROPY; + uint64_t entropy_min = WEBP_UINT64_MAX; int cc_init[MAX_COLOR_CACHE_BITS + 1] = { 0 }; VP8LColorCache hashers[MAX_COLOR_CACHE_BITS + 1]; VP8LRefsCursor c = VP8LRefsCursorInit(refs); @@ -797,20 +796,20 @@ static int CalculateBestCacheSize(const uint32_t* argb, int quality, // 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]; + ++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]; + ++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]; + ++histos[i]->blue[b]; + ++histos[i]->literal[g]; + ++histos[i]->red[r]; + ++histos[i]->alpha[a]; } } } else { @@ -819,12 +818,12 @@ static int CalculateBestCacheSize(const uint32_t* argb, int quality, // 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. + // 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]; + ++histos[i]->literal[NUM_LITERAL_CODES + code]; } // Update the color caches. do { @@ -832,7 +831,7 @@ static int CalculateBestCacheSize(const uint32_t* argb, int quality, // 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; + hashers[i].colors[key] = *argb; } argb_prev = *argb; } @@ -843,7 +842,7 @@ static int CalculateBestCacheSize(const uint32_t* argb, int quality, } for (i = 0; i <= cache_bits_max; ++i) { - const float entropy = VP8LHistogramEstimateBits(histos[i]); + const uint64_t entropy = VP8LHistogramEstimateBits(histos[i]); if (i == 0 || entropy < entropy_min) { entropy_min = entropy; *best_cache_bits = i; @@ -920,7 +919,7 @@ static int GetBackwardReferences(int width, int height, 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}; - float bit_costs_best[2] = {FLT_MAX, FLT_MAX}; + uint64_t bit_costs_best[2] = {WEBP_UINT64_MAX, WEBP_UINT64_MAX}; VP8LHashChain hash_chain_box; VP8LBackwardRefs* const refs_tmp = &refs[do_no_cache ? 2 : 1]; int status = 0; @@ -932,7 +931,7 @@ static int GetBackwardReferences(int width, int height, for (lz77_type = 1; lz77_types_to_try; lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) { int res = 0; - float bit_cost = 0.f; + uint64_t bit_cost = 0u; if ((lz77_types_to_try & lz77_type) == 0) continue; switch (lz77_type) { case kLZ77RLE: @@ -1006,7 +1005,7 @@ static int GetBackwardReferences(int width, int height, 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; - float bit_cost_trace; + uint64_t bit_cost_trace; if (!VP8LBackwardReferencesTraceBackwards(width, height, argb, cache_bits, hash_chain_tmp, &refs[i], refs_tmp)) { diff --git a/3rdparty/libwebp/src/enc/backward_references_enc.h b/3rdparty/libwebp/src/enc/backward_references_enc.h index 4dff1c27b5..b9738d0678 100644 --- a/3rdparty/libwebp/src/enc/backward_references_enc.h +++ b/3rdparty/libwebp/src/enc/backward_references_enc.h @@ -15,9 +15,10 @@ #include #include -#include "src/webp/types.h" + #include "src/webp/encode.h" #include "src/webp/format_constants.h" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { @@ -126,10 +127,10 @@ struct VP8LHashChain { // (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_; + 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_; + int size; }; // Must be called first, to set size. @@ -143,12 +144,12 @@ 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; + 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); + return p->offset_length[base_position] & ((1U << MAX_LENGTH_BITS) - 1); } static WEBP_INLINE void VP8LHashChainFindCopy(const VP8LHashChain* const p, @@ -170,12 +171,12 @@ 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) + 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 @@ -189,8 +190,8 @@ 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 + 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. @@ -205,7 +206,7 @@ void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c); static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) { assert(c != NULL); assert(VP8LRefsCursorOk(c)); - if (++c->cur_pos == c->last_pos_) VP8LRefsCursorNextBlock(c); + if (++c->cur_pos == c->last_pos) VP8LRefsCursorNextBlock(c); } // ----------------------------------------------------------------------------- diff --git a/3rdparty/libwebp/src/enc/config_enc.c b/3rdparty/libwebp/src/enc/config_enc.c index 3518b41403..6d3d442445 100644 --- a/3rdparty/libwebp/src/enc/config_enc.c +++ b/3rdparty/libwebp/src/enc/config_enc.c @@ -15,7 +15,10 @@ #include "src/webp/config.h" #endif +#include + #include "src/webp/encode.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // WebPConfig @@ -55,7 +58,6 @@ int WebPConfigInitInternal(WebPConfig* config, 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. @@ -125,9 +127,6 @@ int WebPValidateConfig(const WebPConfig* config) { 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; @@ -139,8 +138,8 @@ int WebPValidateConfig(const WebPConfig* config) { // Mapping between -z level and -m / -q parameter settings. static const struct { - uint8_t method_; - uint8_t quality_; + 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 } @@ -149,8 +148,8 @@ static const struct { 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_; + config->method = kLosslessPresets[level].method; + config->quality = kLosslessPresets[level].quality; return 1; } diff --git a/3rdparty/libwebp/src/enc/cost_enc.c b/3rdparty/libwebp/src/enc/cost_enc.c index 48fd9bc347..3ed4eea48e 100644 --- a/3rdparty/libwebp/src/enc/cost_enc.c +++ b/3rdparty/libwebp/src/enc/cost_enc.c @@ -11,7 +11,13 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include + +#include "src/dec/common_dec.h" +#include "src/webp/types.h" +#include "src/dsp/dsp.h" #include "src/enc/cost_enc.h" +#include "src/enc/vp8i_enc.h" //------------------------------------------------------------------------------ // Level cost tables @@ -19,7 +25,7 @@ // 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] = { +static 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}, @@ -60,14 +66,14 @@ static int VariableLevelCost(int level, const uint8_t probas[NUM_PROBAS]) { void VP8CalculateLevelCosts(VP8EncProba* const proba) { int ctype, band, ctx; - if (!proba->dirty_) return; // nothing to do. + 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 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; @@ -81,12 +87,12 @@ void VP8CalculateLevelCosts(VP8EncProba* const proba) { } 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->remapped_costs[ctype][n][ctx] = + proba->level_cost[ctype][VP8EncBands[n]][ctx]; } } } - proba->dirty_ = 0; + proba->dirty = 0; } //------------------------------------------------------------------------------ @@ -206,9 +212,9 @@ const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES] = { 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->prob = enc->proba.coeffs[coeff_type]; + res->stats = enc->proba.stats[coeff_type]; + res->costs = enc->proba.remapped_costs[coeff_type]; res->first = first; } @@ -216,14 +222,14 @@ void VP8InitResidual(int first, int coeff_type, // Mode costs int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) { - const int x = (it->i4_ & 3), y = (it->i4_ >> 2); + const int x = (it->i4 & 3), y = (it->i4 >> 2); VP8Residual res; - VP8Encoder* const enc = it->enc_; + VP8Encoder* const enc = it->enc; int R = 0; int ctx; VP8InitResidual(0, 3, enc, &res); - ctx = it->top_nz_[x] + it->left_nz_[y]; + ctx = it->top_nz[x] + it->left_nz[y]; VP8SetResidualCoeffs(levels, &res); R += VP8GetResidualCost(ctx, &res); return R; @@ -231,7 +237,7 @@ int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) { int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) { VP8Residual res; - VP8Encoder* const enc = it->enc_; + VP8Encoder* const enc = it->enc; int x, y; int R = 0; @@ -240,16 +246,16 @@ int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) { // DC VP8InitResidual(0, 1, enc, &res); VP8SetResidualCoeffs(rd->y_dc_levels, &res); - R += VP8GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &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]; + 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); + it->top_nz[x] = it->left_nz[y] = (res.last >= 0); } } return R; @@ -257,7 +263,7 @@ int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) { int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) { VP8Residual res; - VP8Encoder* const enc = it->enc_; + VP8Encoder* const enc = it->enc; int ch, x, y; int R = 0; @@ -267,10 +273,10 @@ int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) { 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]; + 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); + it->top_nz[4 + ch + x] = it->left_nz[4 + ch + y] = (res.last >= 0); } } } diff --git a/3rdparty/libwebp/src/enc/cost_enc.h b/3rdparty/libwebp/src/enc/cost_enc.h index a4b177b342..d2ffcc124e 100644 --- a/3rdparty/libwebp/src/enc/cost_enc.h +++ b/3rdparty/libwebp/src/enc/cost_enc.h @@ -16,7 +16,11 @@ #include #include + +#include "src/dec/common_dec.h" +#include "src/dsp/dsp.h" #include "src/enc/vp8i_enc.h" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { @@ -61,7 +65,6 @@ static WEBP_INLINE int VP8BitCost(int bit, uint8_t 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] diff --git a/3rdparty/libwebp/src/enc/filter_enc.c b/3rdparty/libwebp/src/enc/filter_enc.c index 580800bfb8..8790ac814a 100644 --- a/3rdparty/libwebp/src/enc/filter_enc.c +++ b/3rdparty/libwebp/src/enc/filter_enc.c @@ -12,8 +12,13 @@ // Author: somnath@google.com (Somnath Banerjee) #include -#include "src/enc/vp8i_enc.h" +#include +#include + +#include "src/dec/common_dec.h" +#include "src/webp/types.h" #include "src/dsp/dsp.h" +#include "src/enc/vp8i_enc.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. @@ -83,18 +88,18 @@ static int GetILevel(int sharpness, int 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 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; + 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)); + // 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 + if (enc->filter_hdr.simple == 1) { // simple VP8SimpleHFilter16i(y_dst, BPS, limit); VP8SimpleVFilter16i(y_dst, BPS, limit); } else { // complex @@ -139,11 +144,11 @@ static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) { void VP8InitFilter(VP8EncIterator* const it) { #if !defined(WEBP_REDUCE_SIZE) - if (it->lf_stats_ != NULL) { + 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; + (*it->lf_stats)[s][i] = 0; } } VP8SSIMDspInit(); @@ -156,16 +161,16 @@ void VP8InitFilter(VP8EncIterator* const it) { 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_; + 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 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; + if (it->lf_stats == NULL) return; // NOTE: Currently we are applying filter only across the sublock edges // There are two reasons for that. @@ -173,10 +178,10 @@ void VP8StoreFilterStats(VP8EncIterator* const it) { // 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; + 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_); + (*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; @@ -184,7 +189,7 @@ void VP8StoreFilterStats(VP8EncIterator* const it) { continue; } DoFilter(it, level); - (*it->lf_stats_)[s][level] += GetMBSSIM(it->yuv_in_, it->yuv_out2_); + (*it->lf_stats)[s][level] += GetMBSSIM(it->yuv_in, it->yuv_out2); } #else // defined(WEBP_REDUCE_SIZE) (void)it; @@ -192,43 +197,43 @@ void VP8StoreFilterStats(VP8EncIterator* const it) { } void VP8AdjustFilterStrength(VP8EncIterator* const it) { - VP8Encoder* const enc = it->enc_; + VP8Encoder* const enc = it->enc; #if !defined(WEBP_REDUCE_SIZE) - if (it->lf_stats_ != NULL) { + 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]; + 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]; + const double v = (*it->lf_stats)[s][i]; if (v > best_v) { best_v = v; best_level = i; } } - enc->dqm_[s].fstrength_ = best_level; + enc->dqm[s].fstrength = best_level; } return; } #endif // !defined(WEBP_REDUCE_SIZE) - if (enc->config_->filter_strength > 0) { + 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]; + 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 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; + VP8FilterStrengthFromDelta(enc->filter_hdr.sharpness, delta); + if (level > dqm->fstrength) { + dqm->fstrength = level; } - if (max_level < dqm->fstrength_) { - max_level = dqm->fstrength_; + if (max_level < dqm->fstrength) { + max_level = dqm->fstrength; } } - enc->filter_hdr_.level_ = max_level; + enc->filter_hdr.level = max_level; } } diff --git a/3rdparty/libwebp/src/enc/frame_enc.c b/3rdparty/libwebp/src/enc/frame_enc.c index 01860ca757..5a50085c72 100644 --- a/3rdparty/libwebp/src/enc/frame_enc.c +++ b/3rdparty/libwebp/src/enc/frame_enc.c @@ -11,12 +11,17 @@ // // Author: Skal (pascal.massimino@gmail.com) -#include +#include #include +#include +#include "src/dec/common_dec.h" +#include "src/webp/types.h" +#include "src/dsp/dsp.h" #include "src/enc/cost_enc.h" #include "src/enc/vp8i_enc.h" -#include "src/dsp/dsp.h" +#include "src/utils/bit_writer_utils.h" +#include "src/webp/encode.h" #include "src/webp/format_constants.h" // RIFF constants #define SEGMENT_VISU 0 @@ -46,15 +51,15 @@ typedef struct { // struct for organizing convergence in either size or PSNR } PassStats; static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) { - const uint64_t target_size = (uint64_t)enc->config_->target_size; + 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; + 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->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 @@ -95,9 +100,9 @@ const uint8_t VP8Cat6[] = // Reset the statistics about: number of skips, token proba, level cost,... static void ResetStats(VP8Encoder* const enc) { - VP8EncProba* const proba = &enc->proba_; + VP8EncProba* const proba = &enc->proba; VP8CalculateLevelCosts(proba); - proba->nb_skip_ = 0; + proba->nb_skip = 0; } //------------------------------------------------------------------------------ @@ -111,17 +116,17 @@ static int CalcSkipProba(uint64_t nb, uint64_t total) { // 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_; + 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); + 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. + 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; } @@ -139,8 +144,8 @@ static int BranchCost(int nb, int total, int proba) { } static void ResetTokenStats(VP8Encoder* const enc) { - VP8EncProba* const proba = &enc->proba_; - memset(proba->stats_, 0, sizeof(proba->stats_)); + VP8EncProba* const proba = &enc->proba; + memset(proba->stats, 0, sizeof(proba->stats)); } static int FinalizeTokenProbas(VP8EncProba* const proba) { @@ -151,7 +156,7 @@ static int FinalizeTokenProbas(VP8EncProba* const proba) { 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 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]; @@ -165,17 +170,17 @@ static int FinalizeTokenProbas(VP8EncProba* const proba) { 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; + 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->coeffs[t][b][c][p] = old_p; } } } } } - proba->dirty_ = has_changed; + proba->dirty = has_changed; return size; } @@ -190,8 +195,8 @@ static int GetProba(int a, int b) { 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; + for (n = 0; n < enc->mb_w * enc->mb_h; ++n) { + enc->mb_info[n].segment = 0; } } @@ -199,34 +204,34 @@ 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_]; + 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) { + if (enc->pic->stats != NULL) { for (n = 0; n < NUM_MB_SEGMENTS; ++n) { - enc->pic_->stats->segment_size[n] = p[n]; + enc->pic->stats->segment_size[n] = p[n]; } } #endif - if (enc->segment_hdr_.num_segments_ > 1) { - uint8_t* const probas = enc->proba_.segments_; + 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_ = + 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_ = + 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; + enc->segment_hdr.update_map = 0; + enc->segment_hdr.size = 0; } } @@ -311,9 +316,9 @@ static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it, 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_; + const int i16 = (it->mb->type == 1); + const int segment = it->mb->segment; + VP8Encoder* const enc = it->enc; VP8IteratorNzToBytes(it); @@ -321,8 +326,8 @@ static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it, 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); + 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); @@ -331,9 +336,9 @@ static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it, // 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]; + 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); + it->top_nz[x] = it->left_nz[y] = PutCoeffs(bw, ctx, &res); } } pos2 = VP8BitWriterPos(bw); @@ -343,18 +348,18 @@ static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it, 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]; + 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] = + 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_; + 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); } @@ -364,15 +369,15 @@ static void RecordResiduals(VP8EncIterator* const it, const VP8ModeScore* const rd) { int x, y, ch; VP8Residual res; - VP8Encoder* const enc = it->enc_; + VP8Encoder* const enc = it->enc; VP8IteratorNzToBytes(it); - if (it->mb_->type_ == 1) { // i16x16 + 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); + 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); @@ -381,9 +386,9 @@ static void RecordResiduals(VP8EncIterator* const it, // 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]; + 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); + it->top_nz[x] = it->left_nz[y] = VP8RecordCoeffs(ctx, &res); } } @@ -392,9 +397,9 @@ static void RecordResiduals(VP8EncIterator* const it, 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]; + 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] = + it->top_nz[4 + ch + x] = it->left_nz[4 + ch + y] = VP8RecordCoeffs(ctx, &res); } } @@ -412,14 +417,14 @@ static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd, VP8TBuffer* const tokens) { int x, y, ch; VP8Residual res; - VP8Encoder* const enc = it->enc_; + VP8Encoder* const enc = it->enc; VP8IteratorNzToBytes(it); - if (it->mb_->type_ == 1) { // i16x16 - const int ctx = it->top_nz_[8] + it->left_nz_[8]; + 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] = + it->top_nz[8] = it->left_nz[8] = VP8RecordCoeffTokens(ctx, &res, tokens); VP8InitResidual(1, 0, enc, &res); } else { @@ -429,9 +434,9 @@ static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd, // 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]; + 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] = + it->top_nz[x] = it->left_nz[y] = VP8RecordCoeffTokens(ctx, &res, tokens); } } @@ -441,15 +446,15 @@ static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd, 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]; + 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] = + it->top_nz[4 + ch + x] = it->left_nz[4 + ch + y] = VP8RecordCoeffTokens(ctx, &res, tokens); } } } VP8IteratorBytesToNz(it); - return !tokens->error_; + return !tokens->error; } #endif // !DISABLE_TOKEN_BUFFER @@ -470,64 +475,64 @@ static void SetBlock(uint8_t* p, int value, int size) { #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; + 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_; + 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; + 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_; + 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); + 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_]; + 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 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); + const int b = (int)((it->luma_bits + it->uv_bits + 7) >> 3); *info = (b > 255) ? 255 : b; break; } - case 7: *info = mb->alpha_; 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); + 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_; + VP8Encoder* const enc = it->enc; + WebPPicture* const pic = enc->pic; if (pic->stats != NULL) { - memset(enc->block_count_, 0, sizeof(enc->block_count_)); + memset(enc->block_count, 0, sizeof(enc->block_count)); } ResetSSE(enc); } @@ -536,12 +541,12 @@ 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_; + 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 + 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)); + enc->mb_w * enc->mb_h * sizeof(*pic->extra_info)); } } } @@ -587,7 +592,7 @@ static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt, 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_; + ++enc->proba.nb_skip; } RecordResiduals(&it, &info); size += info.R + info.H; @@ -599,10 +604,10 @@ static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt, VP8IteratorSaveBoundary(&it); } while (VP8IteratorNext(&it) && --nb_mbs > 0); - size_p0 += enc->segment_hdr_.size_; + size_p0 += enc->segment_hdr.size; if (s->do_size_search) { size += FinalizeSkipProba(enc); - size += FinalizeTokenProbas(&enc->proba_); + size += FinalizeTokenProbas(&enc->proba); size = ((size + size_p0 + 1024) >> 11) + HEADER_SIZE_ESTIMATE; s->value = (double)size; } else { @@ -612,17 +617,17 @@ static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt, } static int StatLoop(VP8Encoder* const enc) { - const int method = enc->method_; - const int do_search = enc->do_search_; + 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; + 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 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_; + int nb_mbs = enc->mb_w * enc->mb_h; PassStats stats; InitPassStats(enc, &stats); @@ -640,7 +645,7 @@ static int StatLoop(VP8Encoder* const enc) { 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); + (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; @@ -648,9 +653,9 @@ static int StatLoop(VP8Encoder* const enc) { 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) { + 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... + enc->max_i4_header_bits >>= 1; // strengthen header bit limitation... continue; // ...and start over } if (is_last_pass) { @@ -665,10 +670,10 @@ static int StatLoop(VP8Encoder* const enc) { 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_); + FinalizeTokenProbas(&enc->proba); } - VP8CalculateLevelCosts(&enc->proba_); // finalize costs - return WebPReportProgress(enc->pic_, final_percent, &enc->percent_); + VP8CalculateLevelCosts(&enc->proba); // finalize costs + return WebPReportProgress(enc->pic, final_percent, &enc->percent); } //------------------------------------------------------------------------------ @@ -680,37 +685,37 @@ 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 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_; + 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); + for (p = 0; ok && p < enc->num_parts; ++p) { + ok = VP8BitWriterInit(enc->parts + p, bytes_per_parts); } if (!ok) { VP8EncFreeBitWriters(enc); // malloc error occurred - return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } return ok; } static int PostLoopFinalize(VP8EncIterator* const it, int ok) { - VP8Encoder* const enc = it->enc_; + 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_; + 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... + 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); + enc->residual_bytes[i][s] = (int)((it->bit_count[s][i] + 7) >> 3); } } } @@ -719,7 +724,7 @@ static int PostLoopFinalize(VP8EncIterator* const it, int ok) { } else { // Something bad happened -> need to do some memory cleanup. VP8EncFreeBitWriters(enc); - return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } return ok; } @@ -728,11 +733,11 @@ static int PostLoopFinalize(VP8EncIterator* const it, int 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; + 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 + *it->nz &= (1 << 24); // preserve the dc_nz bit } } @@ -747,16 +752,16 @@ int VP8EncLoop(VP8Encoder* const enc) { VP8InitFilter(&it); do { VP8ModeScore info; - const int dont_use_skip = !enc->proba_.use_skip_proba_; - const VP8RDLevel rd_opt = enc->rd_opt_level_; + 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); - if (it.bw_->error_) { - // enc->pic_->error_code is set in PostLoopFinalize(). + CodeResiduals(it.bw, &it, &info); + if (it.bw->error) { + // enc->pic->error_code is set in PostLoopFinalize(). ok = 0; break; } @@ -782,14 +787,14 @@ int VP8EncLoop(VP8Encoder* const enc) { 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; + int max_count = (enc->mb_w * enc->mb_h) >> 3; + int num_pass_left = enc->config->pass; int remaining_progress = 40; // percents - const int do_search = enc->do_search_; + 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 = (uint64_t)enc->mb_w_ * enc->mb_h_ * 384; + VP8EncProba* const proba = &enc->proba; + const VP8RDLevel rd_opt = enc->rd_opt_level; + const uint64_t pixel_count = (uint64_t)enc->mb_w * enc->mb_h * 384; PassStats stats; int ok; @@ -799,16 +804,16 @@ int VP8EncTokenLoop(VP8Encoder* const enc) { 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(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); + (enc->max_i4_header_bits == 0); uint64_t size_p0 = 0; uint64_t distortion = 0; int cnt = max_count; @@ -821,7 +826,7 @@ int VP8EncTokenLoop(VP8Encoder* const enc) { ResetTokenStats(enc); VP8InitFilter(&it); // don't collect stats until last pass (too costly) } - VP8TBufferClear(&enc->tokens_); + VP8TBufferClear(&enc->tokens); do { VP8ModeScore info; VP8IteratorImport(&it, NULL); @@ -831,9 +836,9 @@ int VP8EncTokenLoop(VP8Encoder* const enc) { cnt = max_count; } VP8Decimate(&it, &info, rd_opt); - ok = RecordTokens(&it, &info, &enc->tokens_); + ok = RecordTokens(&it, &info, &enc->tokens); if (!ok) { - WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY); break; } size_p0 += info.H; @@ -848,11 +853,11 @@ int VP8EncTokenLoop(VP8Encoder* const enc) { } while (ok && VP8IteratorNext(&it)); if (!ok) break; - size_p0 += enc->segment_hdr_.size_; + 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_); + 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; @@ -866,9 +871,9 @@ int VP8EncTokenLoop(VP8Encoder* const enc) { 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) { + 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... + enc->max_i4_header_bits >>= 1; // strengthen header bit limitation... if (is_last_pass) { ResetSideInfo(&it); } @@ -883,13 +888,13 @@ int VP8EncTokenLoop(VP8Encoder* const enc) { } if (ok) { if (!stats.do_size_search) { - FinalizeTokenProbas(&enc->proba_); + FinalizeTokenProbas(&enc->proba); } - ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0, - (const uint8_t*)proba->coeffs_, 1); + ok = VP8EmitTokens(&enc->tokens, enc->parts + 0, + (const uint8_t*)proba->coeffs, 1); } - ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + remaining_progress, - &enc->percent_); + ok = ok && WebPReportProgress(enc->pic, enc->percent + remaining_progress, + &enc->percent); return PostLoopFinalize(&it, ok); } diff --git a/3rdparty/libwebp/src/enc/histogram_enc.c b/3rdparty/libwebp/src/enc/histogram_enc.c index 3ca67b3ad0..b7f265c0cb 100644 --- a/3rdparty/libwebp/src/enc/histogram_enc.c +++ b/3rdparty/libwebp/src/enc/histogram_enc.c @@ -13,8 +13,9 @@ #include "src/webp/config.h" #endif -#include -#include +#include +#include +#include #include "src/dsp/lossless.h" #include "src/dsp/lossless_common.h" @@ -22,8 +23,9 @@ #include "src/enc/histogram_enc.h" #include "src/enc/vp8i_enc.h" #include "src/utils/utils.h" - -#define MAX_BIT_COST FLT_MAX +#include "src/webp/encode.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" // Number of partitions for the three dominant (literal, red and blue) symbol // costs. @@ -33,91 +35,98 @@ // 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; -} +// Enum to meaningfully access the elements of the Histogram arrays. +typedef enum { + LITERAL = 0, + RED, + BLUE, + ALPHA, + DISTANCE +} HistogramIndex; -// 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) { +// Return the size of the histogram for a given cache_bits. +static int GetHistogramSize(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); +static void HistogramStatsClear(VP8LHistogram* const h) { + int i; + for (i = 0; i < 5; ++i) { + h->trivial_symbol[i] = VP8L_NON_TRIVIAL_SYM; + // By default, the histogram is assumed to be used. + h->is_used[i] = 1; } + h->bit_cost = 0; + memset(h->costs, 0, sizeof(h->costs)); } -void VP8LHistogramCreate(VP8LHistogram* const p, +static void HistogramClear(VP8LHistogram* const h) { + uint32_t* const literal = h->literal; + const int cache_bits = h->palette_code_bits; + const int histo_size = GetHistogramSize(cache_bits); + memset(h, 0, histo_size); + h->palette_code_bits = cache_bits; + h->literal = literal; + HistogramStatsClear(h); +} + +// Swap two histogram pointers. +static void HistogramSwap(VP8LHistogram** const h1, VP8LHistogram** const h2) { + VP8LHistogram* const tmp = *h1; + *h1 = *h2; + *h2 = 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 = GetHistogramSize(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)); +} + +void VP8LFreeHistogram(VP8LHistogram* const h) { WebPSafeFree(h); } + +void VP8LFreeHistogramSet(VP8LHistogramSet* const histograms) { + WebPSafeFree(histograms); +} + +void VP8LHistogramCreate(VP8LHistogram* const h, const VP8LBackwardRefs* const refs, int palette_code_bits) { if (palette_code_bits >= 0) { - p->palette_code_bits_ = palette_code_bits; + h->palette_code_bits = palette_code_bits; } - HistogramClear(p); - VP8LHistogramStoreRefs(refs, p); + HistogramClear(h); + VP8LHistogramStoreRefs(refs, /*distance_modifier=*/NULL, + /*distance_modifier_arg0=*/0, h); } -void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits, +void VP8LHistogramInit(VP8LHistogram* const h, int palette_code_bits, int init_arrays) { - p->palette_code_bits_ = palette_code_bits; + h->palette_code_bits = palette_code_bits; if (init_arrays) { - HistogramClear(p); + HistogramClear(h); } 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_)); + HistogramStatsClear(h); } } VP8LHistogram* VP8LAllocateHistogram(int cache_bits) { VP8LHistogram* histo = NULL; - const int total_size = VP8LGetHistogramSize(cache_bits); + const int total_size = GetHistogramSize(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)); + // 'literal' won't necessary be aligned. + histo->literal = (uint32_t*)(memory + sizeof(VP8LHistogram)); VP8LHistogramInit(histo, cache_bits, /*init_arrays=*/ 0); return histo; } @@ -126,21 +135,21 @@ VP8LHistogram* VP8LAllocateHistogram(int cache_bits) { static void HistogramSetResetPointers(VP8LHistogramSet* const set, int cache_bits) { int i; - const int histo_size = VP8LGetHistogramSize(cache_bits); + const int histo_size = GetHistogramSize(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)); + // '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); + const int histo_size = GetHistogramSize(cache_bits); return (sizeof(VP8LHistogramSet) + size * (sizeof(VP8LHistogram*) + histo_size + WEBP_ALIGN_CST)); } @@ -166,7 +175,7 @@ VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) { void VP8LHistogramSetClear(VP8LHistogramSet* const set) { int i; - const int cache_bits = set->histograms[0]->palette_code_bits_; + 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; @@ -178,44 +187,36 @@ void VP8LHistogramSetClear(VP8LHistogramSet* const set) { set->size = size; HistogramSetResetPointers(set, cache_bits); for (i = 0; i < size; ++i) { - set->histograms[i]->palette_code_bits_ = cache_bits; + 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; - } - } +// Removes the histogram 'i' from 'set'. +static void HistogramSetRemoveHistogram(VP8LHistogramSet* const set, int i) { + set->histograms[i] = set->histograms[set->size - 1]; + --set->size; + assert(set->size > 0); } // ----------------------------------------------------------------------------- -void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, - const PixOrCopy* const v, - int (*const distance_modifier)(int, int), - int distance_modifier_arg0) { +static void HistogramAddSinglePixOrCopy( + 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)]; + ++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]; + 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]; + ++histo->literal[NUM_LITERAL_CODES + code]; if (distance_modifier == NULL) { VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits); } else { @@ -223,15 +224,27 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, distance_modifier(distance_modifier_arg0, PixOrCopyDistance(v)), &code, &extra_bits); } - ++histo->distance_[code]; + ++histo->distance[code]; + } +} + +void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs, + int (*const distance_modifier)(int, int), + int distance_modifier_arg0, + VP8LHistogram* const histo) { + VP8LRefsCursor c = VP8LRefsCursorInit(refs); + while (VP8LRefsCursorOk(&c)) { + HistogramAddSinglePixOrCopy(histo, c.cur_pos, distance_modifier, + distance_modifier_arg0); + VP8LRefsCursorNext(&c); } } // ----------------------------------------------------------------------------- // Entropy-related functions. -static WEBP_INLINE float BitsEntropyRefine(const VP8LBitEntropy* entropy) { - float mix; +static WEBP_INLINE uint64_t BitsEntropyRefine(const VP8LBitEntropy* entropy) { + uint64_t mix; if (entropy->nonzeros < 5) { if (entropy->nonzeros <= 1) { return 0; @@ -240,67 +253,72 @@ static WEBP_INLINE float BitsEntropyRefine(const VP8LBitEntropy* entropy) { // Let's mix in a bit of entropy to favor good clustering when // distributions of these are combined. if (entropy->nonzeros == 2) { - return 0.99f * entropy->sum + 0.01f * entropy->entropy; + return DivRound(99 * ((uint64_t)entropy->sum << LOG_2_PRECISION_BITS) + + entropy->entropy, + 100); } // 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.95f; + mix = 950; } else { - mix = 0.7f; // nonzeros == 4. + mix = 700; // nonzeros == 4. } } else { - mix = 0.627f; + mix = 627; } { - float min_limit = 2.f * entropy->sum - entropy->max_val; - min_limit = mix * min_limit + (1.f - mix) * entropy->entropy; + uint64_t min_limit = (uint64_t)(2 * entropy->sum - entropy->max_val) + << LOG_2_PRECISION_BITS; + min_limit = + DivRound(mix * min_limit + (1000 - mix) * entropy->entropy, 1000); return (entropy->entropy < min_limit) ? min_limit : entropy->entropy; } } -float VP8LBitsEntropy(const uint32_t* const array, int n) { +uint64_t VP8LBitsEntropy(const uint32_t* const array, int n) { VP8LBitEntropy entropy; VP8LBitsEntropyUnrefined(array, n, &entropy); return BitsEntropyRefine(&entropy); } -static float InitialHuffmanCost(void) { +static uint64_t 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 float kSmallBias = 9.1f; - return kHuffmanCodeOfHuffmanCodeSize - kSmallBias; + static const uint64_t kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3; + // Subtract a bias of 9.1. + return (kHuffmanCodeOfHuffmanCodeSize << LOG_2_PRECISION_BITS) - + DivRound(91ll << LOG_2_PRECISION_BITS, 10); } // Finalize the Huffman cost based on streak numbers and length type (<3 or >=3) -static float FinalHuffmanCost(const VP8LStreaks* const stats) { - // The constants in this function are experimental and got rounded from +static uint64_t FinalHuffmanCost(const VP8LStreaks* const stats) { + // The constants in this function are empirical and got rounded from // their original values in 1/8 when switched to 1/1024. - float retval = InitialHuffmanCost(); + uint64_t 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.5625f + 0.234375f * stats->streaks[0][1]; + uint32_t retval_extra = stats->counts[0] * 1600 + 240 * stats->streaks[0][1]; // Second coefficient: Constant values are encoded less efficiently, but still // RLE'ed. Originally 6/8. - retval += stats->counts[1] * 2.578125f + 0.703125f * stats->streaks[1][1]; + retval_extra += stats->counts[1] * 2640 + 720 * stats->streaks[1][1]; // 0s are usually encoded more efficiently than non-0s. // Originally 15/8. - retval += 1.796875f * stats->streaks[0][0]; + retval_extra += 1840 * stats->streaks[0][0]; // Originally 26/8. - retval += 3.28125f * stats->streaks[1][0]; - return retval; + retval_extra += 3360 * stats->streaks[1][0]; + return retval + ((uint64_t)retval_extra << (LOG_2_PRECISION_BITS - 10)); } // Get the symbol entropy for the distribution 'population'. // Set 'trivial_sym', if there's only one symbol present in the distribution. -static float PopulationCost(const uint32_t* const population, int length, - uint32_t* const trivial_sym, - uint8_t* const is_used) { +static uint64_t PopulationCost(const uint32_t* const population, int length, + uint16_t* const trivial_sym, + uint8_t* const is_used) { VP8LBitEntropy bit_entropy; VP8LStreaks stats; VP8LGetEntropyUnrefined(population, length, &bit_entropy, &stats); @@ -308,131 +326,175 @@ static float PopulationCost(const uint32_t* const population, int length, *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); + if (is_used != NULL) { + // 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 float 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); +static WEBP_INLINE void GetPopulationInfo(const VP8LHistogram* const histo, + HistogramIndex index, + const uint32_t** population, + int* length) { + switch (index) { + case LITERAL: + *population = histo->literal; + *length = VP8LHistogramNumCodes(histo->palette_code_bits); + break; + case RED: + *population = histo->red; + *length = NUM_LITERAL_CODES; + break; + case BLUE: + *population = histo->blue; + *length = NUM_LITERAL_CODES; + break; + case ALPHA: + *population = histo->alpha; + *length = NUM_LITERAL_CODES; + break; + case DISTANCE: + *population = histo->distance; + *length = NUM_DISTANCE_CODES; + break; } } +// 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. +// 'index' is the index of the symbol in the histogram (literal, red, blue, +// alpha, distance). +static WEBP_INLINE uint64_t GetCombinedEntropy(const VP8LHistogram* const h1, + const VP8LHistogram* const h2, + HistogramIndex index) { + const uint32_t* X; + const uint32_t* Y; + int length; + VP8LStreaks stats; + VP8LBitEntropy bit_entropy; + const int is_h1_used = h1->is_used[index]; + const int is_h2_used = h2->is_used[index]; + const int is_trivial = h1->trivial_symbol[index] != VP8L_NON_TRIVIAL_SYM && + h1->trivial_symbol[index] == h2->trivial_symbol[index]; + + if (is_trivial || !is_h1_used || !is_h2_used) { + if (is_h1_used) return h1->costs[index]; + return h2->costs[index]; + } + assert(is_h1_used && is_h2_used); + + GetPopulationInfo(h1, index, &X, &length); + GetPopulationInfo(h2, index, &Y, &length); + VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats); + return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats); +} + // Estimates the Entropy + Huffman + other block overhead size cost. -float 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]) + - (float)VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, - NUM_LENGTH_CODES) + - (float)VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); +uint64_t VP8LHistogramEstimateBits(const VP8LHistogram* const h) { + int i; + uint64_t cost = 0; + for (i = 0; i < 5; ++i) { + int length; + const uint32_t* population; + GetPopulationInfo(h, (HistogramIndex)i, &population, &length); + cost += PopulationCost(population, length, /*trivial_sym=*/NULL, + /*is_used=*/NULL); + } + cost += ((uint64_t)(VP8LExtraCost(h->literal + NUM_LITERAL_CODES, + NUM_LENGTH_CODES) + + VP8LExtraCost(h->distance, NUM_DISTANCE_CODES)) + << LOG_2_PRECISION_BITS); + return cost; } // ----------------------------------------------------------------------------- // Various histogram combine/cost-eval functions -static int GetCombinedHistogramEntropy(const VP8LHistogram* const a, - const VP8LHistogram* const b, - float cost_threshold, float* 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 += (float)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; - } +// Set a + b in b, saturating at WEBP_INT64_MAX. +static WEBP_INLINE void SaturateAdd(uint64_t a, int64_t* b) { + if (*b < 0 || (int64_t)a <= WEBP_INT64_MAX - *b) { + *b += (int64_t)a; + } else { + *b = WEBP_INT64_MAX; } +} - *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; +// Returns 1 if the cost of the combined histogram is less than the threshold. +// Otherwise returns 0 and the cost is invalid due to early bail-out. +WEBP_NODISCARD static int GetCombinedHistogramEntropy( + const VP8LHistogram* const a, const VP8LHistogram* const b, + int64_t cost_threshold_in, uint64_t* cost, uint64_t costs[5]) { + int i; + const uint64_t cost_threshold = (uint64_t)cost_threshold_in; + assert(a->palette_code_bits == b->palette_code_bits); + if (cost_threshold_in <= 0) return 0; + *cost = 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 += (float)VP8LExtraCostCombined(a->distance_, b->distance_, - NUM_DISTANCE_CODES); - if (*cost > cost_threshold) return 0; + // No need to add the extra cost for length and distance as it is a constant + // that does not influence the histograms. + for (i = 0; i < 5; ++i) { + costs[i] = GetCombinedEntropy(a, b, (HistogramIndex)i); + *cost += costs[i]; + 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; +static WEBP_INLINE void HistogramAdd(const VP8LHistogram* const h1, + const VP8LHistogram* const h2, + VP8LHistogram* const hout) { + int i; + assert(h1->palette_code_bits == h2->palette_code_bits); + + for (i = 0; i < 5; ++i) { + int length; + const uint32_t *p1, *p2, *pout_const; + uint32_t* pout; + GetPopulationInfo(h1, (HistogramIndex)i, &p1, &length); + GetPopulationInfo(h2, (HistogramIndex)i, &p2, &length); + GetPopulationInfo(hout, (HistogramIndex)i, &pout_const, &length); + pout = (uint32_t*)pout_const; + if (h2 == hout) { + if (h1->is_used[i]) { + if (hout->is_used[i]) { + VP8LAddVectorEq(p1, pout, length); + } else { + memcpy(pout, p1, length * sizeof(pout[0])); + } + } + } else { + if (h1->is_used[i]) { + if (h2->is_used[i]) { + VP8LAddVector(p1, p2, pout, length); + } else { + memcpy(pout, p1, length * sizeof(pout[0])); + } + } else if (h2->is_used[i]) { + memcpy(pout, p2, length * sizeof(pout[0])); + } else { + memset(pout, 0, length * sizeof(pout[0])); + } + } + } + + for (i = 0; i < 5; ++i) { + hout->trivial_symbol[i] = h1->trivial_symbol[i] == h2->trivial_symbol[i] + ? h1->trivial_symbol[i] + : VP8L_NON_TRIVIAL_SYM; + hout->is_used[i] = h1->is_used[i] || h2->is_used[i]; + } +} + +static void UpdateHistogramCost(uint64_t bit_cost, uint64_t costs[5], + VP8LHistogram* const h) { + int i; + h->bit_cost = bit_cost; + for (i = 0; i < 5; ++i) { + h->costs[i] = costs[i]; + } } // Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing @@ -441,33 +503,42 @@ static WEBP_INLINE void HistogramAdd(const VP8LHistogram* const a, // 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 float HistogramAddEval(const VP8LHistogram* const a, - const VP8LHistogram* const b, - VP8LHistogram* const out, float cost_threshold) { - float cost = 0; - const float 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_; +// Returns 1 if the cost is less than the threshold. +// Otherwise returns 0 and the cost is invalid due to early bail-out. +WEBP_NODISCARD static int HistogramAddEval(const VP8LHistogram* const a, + const VP8LHistogram* const b, + VP8LHistogram* const out, + int64_t cost_threshold) { + const uint64_t sum_cost = a->bit_cost + b->bit_cost; + uint64_t bit_cost, costs[5]; + SaturateAdd(sum_cost, &cost_threshold); + if (!GetCombinedHistogramEntropy(a, b, cost_threshold, &bit_cost, costs)) { + return 0; } - return cost - sum_cost; + HistogramAdd(a, b, out); + UpdateHistogramCost(bit_cost, costs, out); + return 1; } // 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 float HistogramAddThresh(const VP8LHistogram* const a, - const VP8LHistogram* const b, - float cost_threshold) { - float cost; +// Returns 1 if the cost is less than the threshold. +// Otherwise returns 0 and the cost is invalid due to early bail-out. +WEBP_NODISCARD static int HistogramAddThresh(const VP8LHistogram* const a, + const VP8LHistogram* const b, + int64_t cost_threshold, + int64_t* cost_out) { + uint64_t cost, costs[5]; assert(a != NULL && b != NULL); - cost = -a->bit_cost_; - GetCombinedHistogramEntropy(a, b, cost_threshold, &cost); - return cost; + SaturateAdd(a->bit_cost, &cost_threshold); + if (!GetCombinedHistogramEntropy(a, b, cost_threshold, &cost, costs)) { + return 0; + } + + *cost_out = (int64_t)cost - (int64_t)a->bit_cost; + return 1; } // ----------------------------------------------------------------------------- @@ -475,62 +546,52 @@ static float HistogramAddThresh(const VP8LHistogram* const a, // The structure to keep track of cost range for the three dominant entropy // symbols. typedef struct { - float literal_max_; - float literal_min_; - float red_max_; - float red_min_; - float blue_max_; - float blue_min_; + uint64_t literal_max; + uint64_t literal_min; + uint64_t red_max; + uint64_t red_min; + uint64_t blue_max; + uint64_t blue_min; } DominantCostRange; static void DominantCostRangeInit(DominantCostRange* const c) { - c->literal_max_ = 0.; - c->literal_min_ = MAX_BIT_COST; - c->red_max_ = 0.; - c->red_min_ = MAX_BIT_COST; - c->blue_max_ = 0.; - c->blue_min_ = MAX_BIT_COST; + c->literal_max = 0; + c->literal_min = WEBP_UINT64_MAX; + c->red_max = 0; + c->red_min = WEBP_UINT64_MAX; + c->blue_max = 0; + c->blue_min = WEBP_UINT64_MAX; } 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_; + if (c->literal_max < h->costs[LITERAL]) c->literal_max = h->costs[LITERAL]; + if (c->literal_min > h->costs[LITERAL]) c->literal_min = h->costs[LITERAL]; + if (c->red_max < h->costs[RED]) c->red_max = h->costs[RED]; + if (c->red_min > h->costs[RED]) c->red_min = h->costs[RED]; + if (c->blue_max < h->costs[BLUE]) c->blue_max = h->costs[BLUE]; + if (c->blue_min > h->costs[BLUE]) c->blue_min = h->costs[BLUE]; } -static void UpdateHistogramCost(VP8LHistogram* const h) { - uint32_t alpha_sym, red_sym, blue_sym; - const float alpha_cost = - PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym, &h->is_used_[3]); - const float distance_cost = - PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL, &h->is_used_[4]) + - (float)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]) + - (float)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 void ComputeHistogramCost(VP8LHistogram* const h) { + int i; + // No need to add the extra cost for length and distance as it is a constant + // that does not influence the histograms. + for (i = 0; i < 5; ++i) { + const uint32_t* population; + int length; + GetPopulationInfo(h, i, &population, &length); + h->costs[i] = PopulationCost(population, length, &h->trivial_symbol[i], + &h->is_used[i]); } + h->bit_cost = h->costs[LITERAL] + h->costs[RED] + h->costs[BLUE] + + h->costs[ALPHA] + h->costs[DISTANCE]; } -static int GetBinIdForEntropy(float min, float max, float val) { - const float range = max - min; - if (range > 0.) { - const float delta = val - min; +static int GetBinIdForEntropy(uint64_t min, uint64_t max, uint64_t val) { + const uint64_t range = max - min; + if (range > 0) { + const uint64_t delta = val - min; return (int)((NUM_PARTITIONS - 1e-6) * delta / range); } else { return 0; @@ -539,14 +600,14 @@ static int GetBinIdForEntropy(float min, float max, float val) { 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_); + int bin_id = + GetBinIdForEntropy(c->literal_min, c->literal_max, h->costs[LITERAL]); 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_); + bin_id = bin_id * NUM_PARTITIONS + + GetBinIdForEntropy(c->red_min, c->red_max, h->costs[RED]); + bin_id = bin_id * NUM_PARTITIONS + + GetBinIdForEntropy(c->blue_min, c->blue_max, h->costs[BLUE]); assert(bin_id < BIN_SIZE); } return bin_id; @@ -565,7 +626,7 @@ static void HistogramBuild( 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); + HistogramAddSinglePixOrCopy(histograms[ix], v, NULL, 0); x += PixOrCopyLength(v); while (x >= xsize) { x -= xsize; @@ -576,36 +637,29 @@ static void HistogramBuild( } // 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; + VP8LHistogramSet* const image_histo) { + int i; 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) { + image_histo->size = 0; + for (i = 0; i < orig_histo->max_size; ++i) { VP8LHistogram* const histo = orig_histograms[i]; - UpdateHistogramCost(histo); + ComputeHistogramCost(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. + if (!histo->is_used[LITERAL] && !histo->is_used[RED] && + !histo->is_used[BLUE] && !histo->is_used[ALPHA] && + !histo->is_used[DISTANCE]) { + // The first histogram is always used. assert(i > 0); - HistogramSetRemoveHistogram(image_histo, i, num_used); - HistogramSetRemoveHistogram(orig_histo, i, &num_used_orig); - histogram_symbols[i] = kInvalidHistogramSymbol; + orig_histograms[i] = NULL; } 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); + HistogramCopy(histo, histograms[image_histo->size]); + ++image_histo->size; } } } @@ -613,7 +667,6 @@ static void HistogramCopyAndAnalyze(VP8LHistogramSet* const orig_histo, // 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; @@ -623,27 +676,24 @@ static void HistogramAnalyzeEntropyBin(VP8LHistogramSet* const image_histo, // 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); + histograms[i]->bin_id = + 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, - float combine_cost_factor, int low_effort) { +// 'combine_cost_factor' has to be divided by 100. +static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo, + VP8LHistogram* cur_combo, int num_bins, + int32_t combine_cost_factor, + int low_effort) { VP8LHistogram** const histograms = image_histo->histograms; int idx; struct { @@ -658,53 +708,60 @@ static void HistogramCombineEntropyBin( 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; + for (idx = 0; idx < image_histo->size;) { + const int bin_id = histograms[idx]->bin_id; + const int first = bin_info[bin_id].first; if (first == -1) { bin_info[bin_id].first = idx; + ++idx; } else if (low_effort) { HistogramAdd(histograms[idx], histograms[first], histograms[first]); - HistogramSetRemoveHistogram(image_histo, idx, num_used); - cluster_mappings[clusters[idx]] = clusters[first]; + HistogramSetRemoveHistogram(image_histo, idx); } else { // try to merge #idx into #first (both share the same bin_id) - const float bit_cost = histograms[idx]->bit_cost_; - const float bit_cost_thresh = -bit_cost * combine_cost_factor; - const float curr_cost_diff = HistogramAddEval( - histograms[first], histograms[idx], cur_combo, bit_cost_thresh); - if (curr_cost_diff < bit_cost_thresh) { + const uint64_t bit_cost = histograms[idx]->bit_cost; + const int64_t bit_cost_thresh = + -DivRound((int64_t)bit_cost * combine_cost_factor, 100); + if (HistogramAddEval(histograms[first], histograms[idx], cur_combo, + bit_cost_thresh)) { + const int max_combine_failures = 32; // 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; + int try_combine = + cur_combo->trivial_symbol[RED] != VP8L_NON_TRIVIAL_SYM && + cur_combo->trivial_symbol[BLUE] != VP8L_NON_TRIVIAL_SYM && + cur_combo->trivial_symbol[ALPHA] != VP8L_NON_TRIVIAL_SYM; + if (!try_combine) { + try_combine = + histograms[idx]->trivial_symbol[RED] == VP8L_NON_TRIVIAL_SYM || + histograms[idx]->trivial_symbol[BLUE] == VP8L_NON_TRIVIAL_SYM || + histograms[idx]->trivial_symbol[ALPHA] == VP8L_NON_TRIVIAL_SYM; + try_combine &= + histograms[first]->trivial_symbol[RED] == VP8L_NON_TRIVIAL_SYM || + histograms[first]->trivial_symbol[BLUE] == VP8L_NON_TRIVIAL_SYM || + histograms[first]->trivial_symbol[ALPHA] == VP8L_NON_TRIVIAL_SYM; + } 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]; + HistogramSetRemoveHistogram(image_histo, idx); } else { ++bin_info[bin_id].num_combine_failures; + ++idx; } + } else { + ++idx; } } } 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]); + ComputeHistogramCost(histograms[idx]); } } } @@ -724,8 +781,9 @@ static uint32_t MyRand(uint32_t* const seed) { typedef struct { int idx1; int idx2; - float cost_diff; - float cost_combo; + int64_t cost_diff; + uint64_t cost_combo; + uint64_t costs[5]; } HistogramPair; typedef struct { @@ -765,7 +823,7 @@ static void HistoQueuePopPair(HistoQueue* const histo_queue, // 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->cost_diff < 0); assert(pair >= histo_queue->queue && pair < (histo_queue->queue + histo_queue->size)); assert(histo_queue->size > 0); @@ -777,30 +835,49 @@ static void HistoQueueUpdateHead(HistoQueue* const histo_queue, } } +// Replaces the bad_id with good_id in the pair. +static void HistoQueueFixPair(int bad_id, int good_id, + HistogramPair* const pair) { + if (pair->idx1 == bad_id) pair->idx1 = good_id; + if (pair->idx2 == bad_id) pair->idx2 = good_id; + if (pair->idx1 > pair->idx2) { + const int tmp = pair->idx1; + pair->idx1 = pair->idx2; + pair->idx2 = 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, float threshold, - HistogramPair* const pair) { - const float 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; +// called when the histograms have been merged with a third one. +// Returns 1 if the cost diff is less than the threshold. +// Otherwise returns 0 and the cost is invalid due to early bail-out. +WEBP_NODISCARD static int HistoQueueUpdatePair(const VP8LHistogram* const h1, + const VP8LHistogram* const h2, + int64_t cost_threshold, + HistogramPair* const pair) { + const int64_t sum_cost = h1->bit_cost + h2->bit_cost; + SaturateAdd(sum_cost, &cost_threshold); + if (!GetCombinedHistogramEntropy(h1, h2, cost_threshold, &pair->cost_combo, + pair->costs)) { + return 0; + } + pair->cost_diff = (int64_t)pair->cost_combo - sum_cost; + return 1; } // 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 float HistoQueuePush(HistoQueue* const histo_queue, - VP8LHistogram** const histograms, int idx1, - int idx2, float threshold) { +// It returns the cost of the pair, or 0 if it superior to threshold. +static int64_t HistoQueuePush(HistoQueue* const histo_queue, + VP8LHistogram** const histograms, int idx1, + int idx2, int64_t 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 (histo_queue->size == histo_queue->max_size) return 0; + assert(threshold <= 0); if (idx1 > idx2) { const int tmp = idx2; idx2 = idx1; @@ -811,10 +888,8 @@ static float HistoQueuePush(HistoQueue* const histo_queue, 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.; + if (!HistoQueueUpdatePair(h1, h2, threshold, &pair)) return 0; histo_queue->queue[histo_queue->size++] = pair; HistoQueueUpdateHead(histo_queue, &histo_queue->queue[histo_queue->size - 1]); @@ -826,8 +901,7 @@ static float HistoQueuePush(HistoQueue* const histo_queue, // Combines histograms by continuously choosing the one with the highest cost // reduction. -static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, - int* const num_used) { +static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) { int ok = 0; const int image_histo_size = image_histo->size; int i, j; @@ -846,12 +920,10 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, goto End; } + // Initialize the queue. 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.); + HistoQueuePush(&histo_queue, histograms, i, j, 0); } } @@ -859,10 +931,11 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, 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; + UpdateHistogramCost(histo_queue.queue[0].cost_combo, + histo_queue.queue[0].costs, histograms[idx1]); // Remove merged histogram. - HistogramSetRemoveHistogram(image_histo, idx2, num_used); + HistogramSetRemoveHistogram(image_histo, idx2); // Remove pairs intersecting the just combined best pair. for (i = 0; i < histo_queue.size;) { @@ -871,6 +944,7 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, p->idx1 == idx2 || p->idx2 == idx2) { HistoQueuePopPair(&histo_queue, p); } else { + HistoQueueFixPair(image_histo->size, idx2, p); HistoQueueUpdateHead(&histo_queue, p); ++i; } @@ -878,8 +952,8 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, // 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.); + if (i == idx1) continue; + HistoQueuePush(&histo_queue, image_histo->histograms, idx1, i, 0); } } @@ -893,17 +967,13 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, // 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 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 outer_iters = image_histo->size; const int num_tries_no_success = outer_iters / 2; VP8LHistogram** const histograms = image_histo->histograms; // Priority queue of histogram pairs. Its size of 'kHistoQueueSize' @@ -912,49 +982,34 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo, 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) { + if (image_histo->size < 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; - float best_cost = - (histo_queue.size == 0) ? 0.f : histo_queue.queue[0].cost_diff; + for (iter = 0; iter < outer_iters && image_histo->size >= min_cluster_size && + ++tries_with_no_success < num_tries_no_success; + ++iter) { + int64_t 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; + const uint32_t rand_range = (image_histo->size - 1) * (image_histo->size); + // (image_histo->size) / 2 was chosen empirically. Less means faster but + // worse compression. + const int num_tries = (image_histo->size) / 2; // Pick random samples. - for (j = 0; *num_used >= 2 && j < num_tries; ++j) { - float curr_cost; + for (j = 0; image_histo->size >= 2 && j < num_tries; ++j) { + int64_t 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); + uint32_t idx1 = tmp / (image_histo->size - 1); + uint32_t idx2 = tmp % (image_histo->size - 1); if (idx2 >= idx1) ++idx2; - idx1 = mappings[idx1]; - idx2 = mappings[idx2]; // Calculate cost reduction on combination. curr_cost = @@ -971,24 +1026,18 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo, 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); + UpdateHistogramCost(histo_queue.queue[0].cost_combo, + histo_queue.queue[0].costs, histograms[best_idx1]); + HistogramSetRemoveHistogram(image_histo, best_idx2); // 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) { @@ -997,38 +1046,26 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo, } // 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) { + if (is_idx1_best || is_idx2_best) { + HistoQueueFixPair(best_idx2, best_idx1, p); // Re-evaluate the cost of an updated pair. - HistoQueueUpdatePair(histograms[p->idx1], histograms[p->idx2], 0., p); - if (p->cost_diff >= 0.) { + if (!HistoQueueUpdatePair(histograms[p->idx1], histograms[p->idx2], 0, + p)) { HistoQueuePopPair(&histo_queue, p); continue; } } + HistoQueueFixPair(image_histo->size, best_idx2, p); HistoQueueUpdateHead(&histo_queue, p); ++j; } tries_with_no_success = 0; } - *do_greedy = (*num_used <= min_cluster_size); + *do_greedy = (image_histo->size <= min_cluster_size); ok = 1; End: HistoQueueClear(&histo_queue); - WebPSafeFree(mappings); return ok; } @@ -1037,10 +1074,10 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo, // 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. +// 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) { + uint32_t* const symbols) { int i; VP8LHistogram** const in_histo = in->histograms; VP8LHistogram** const out_histo = out->histograms; @@ -1049,7 +1086,7 @@ static void HistogramRemap(const VP8LHistogramSet* const in, if (out_size > 1) { for (i = 0; i < in_size; ++i) { int best_out = 0; - float best_bits = MAX_BIT_COST; + int64_t best_bits = WEBP_INT64_MAX; int k; if (in_histo[i] == NULL) { // Arbitrarily set to the previous value if unused to help future LZ77. @@ -1057,9 +1094,9 @@ static void HistogramRemap(const VP8LHistogramSet* const in, continue; } for (k = 0; k < out_size; ++k) { - float cur_bits; - cur_bits = HistogramAddThresh(out_histo[k], in_histo[i], best_bits); - if (k == 0 || cur_bits < best_bits) { + int64_t cur_bits; + if (HistogramAddThresh(out_histo[k], in_histo[i], best_bits, + &cur_bits)) { best_bits = cur_bits; best_out = k; } @@ -1085,87 +1122,23 @@ static void HistogramRemap(const VP8LHistogramSet* const in, } } -static float GetCombineCostFactor(int histo_size, int quality) { - float combine_cost_factor = 0.16f; +static int32_t GetCombineCostFactor(int histo_size, int quality) { + int32_t combine_cost_factor = 16; if (quality < 90) { - if (histo_size > 256) combine_cost_factor /= 2.f; - if (histo_size > 512) combine_cost_factor /= 2.f; - if (histo_size > 1024) combine_cost_factor /= 2.f; - if (quality <= 50) combine_cost_factor /= 2.f; + 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, + uint32_t* const histogram_symbols, const WebPPicture* const pic, int percent_range, int* const percent) { const int histo_xsize = @@ -1180,55 +1153,41 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, // 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) { + if (orig_histo == NULL) { WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); 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); - + HistogramCopyAndAnalyze(orig_histo, image_histo); entropy_combine = - (num_used > entropy_combine_num_bins * 2) && (quality < 100); + (image_histo->size > entropy_combine_num_bins * 2) && (quality < 100); if (entropy_combine) { - uint16_t* const bin_map = map_tmp; - const float combine_cost_factor = + const int32_t combine_cost_factor = GetCombineCostFactor(image_histo_raw_size, quality); - const uint32_t num_clusters = num_used; - HistogramAnalyzeEntropyBin(image_histo, bin_map, low_effort); + HistogramAnalyzeEntropyBin(image_histo, 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); + HistogramCombineEntropyBin(image_histo, tmp_histo, entropy_combine_num_bins, + combine_cost_factor, low_effort); } // Don't combine the histograms using stochastic and greedy heuristics for // low-effort compression mode. if (!low_effort || !entropy_combine) { - const float x = quality / 100.f; // cubic ramp between 1 and MAX_HISTO_GREEDY: - const int threshold_size = (int)(1 + (x * x * x) * (MAX_HISTO_GREEDY - 1)); + const int threshold_size = + (int)(1 + DivRound(quality * quality * quality * (MAX_HISTO_GREEDY - 1), + 100 * 100 * 100)); int do_greedy; - if (!HistogramCombineStochastic(image_histo, &num_used, threshold_size, - &do_greedy)) { + if (!HistogramCombineStochastic(image_histo, threshold_size, &do_greedy)) { WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); goto Error; } if (do_greedy) { - RemoveEmptyHistograms(image_histo); - if (!HistogramCombineGreedy(image_histo, &num_used)) { + if (!HistogramCombineGreedy(image_histo)) { WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); goto Error; } @@ -1236,7 +1195,6 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, } // Find the optimal map from original histograms to the final ones. - RemoveEmptyHistograms(image_histo); HistogramRemap(orig_histo, image_histo, histogram_symbols); if (!WebPReportProgress(pic, *percent + percent_range, percent)) { @@ -1245,6 +1203,5 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, Error: VP8LFreeHistogramSet(orig_histo); - WebPSafeFree(map_tmp); return (pic->error_code == VP8_ENC_OK); } diff --git a/3rdparty/libwebp/src/enc/histogram_enc.h b/3rdparty/libwebp/src/enc/histogram_enc.h index 4c0bb97464..303c1e2ecf 100644 --- a/3rdparty/libwebp/src/enc/histogram_enc.h +++ b/3rdparty/libwebp/src/enc/histogram_enc.h @@ -14,9 +14,8 @@ #ifndef WEBP_ENC_HISTOGRAM_ENC_H_ #define WEBP_ENC_HISTOGRAM_ENC_H_ -#include - #include "src/enc/backward_references_enc.h" +#include "src/webp/encode.h" #include "src/webp/format_constants.h" #include "src/webp/types.h" @@ -25,26 +24,29 @@ extern "C" { #endif // Not a trivial literal symbol. -#define VP8L_NON_TRIVIAL_SYM (0xffffffff) +#define VP8L_NON_TRIVIAL_SYM ((uint16_t)(0xffff)) // A simple container for histograms of data. typedef struct { - // literal_ contains green literal, palette-code and + // '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]; + 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. - float bit_cost_; // cached value of bit cost. - float literal_cost_; // Cached values of dominant entropy costs: - float red_cost_; // literal, red & blue. - float blue_cost_; - uint8_t is_used_[5]; // 5 for literal, red, blue, alpha, distance + uint32_t distance[NUM_DISTANCE_CODES]; + int palette_code_bits; + // The following members are only used within VP8LGetHistoImageSymbols. + + // Index of the unique value of a histogram if any, VP8L_NON_TRIVIAL_SYM + // otherwise. + uint16_t trivial_symbol[5]; + uint64_t bit_cost; // Cached value of total bit cost. + // Cached values of entropy costs: literal, red, blue, alpha, distance + uint64_t costs[5]; + uint8_t is_used[5]; // 5 for literal, red, blue, alpha, distance + uint16_t bin_id; // entropy bin index. } VP8LHistogram; // Collection of histograms with fixed capacity, allocated as one @@ -60,20 +62,21 @@ typedef struct { // 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, +void VP8LHistogramCreate(VP8LHistogram* const h, 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, +void VP8LHistogramInit(VP8LHistogram* const h, int palette_code_bits, int init_arrays); // Collect all the references into a histogram (without reset) +// The distance modifier function is applied to the distance before +// the histogram is updated. It can be NULL. void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs, + int (*const distance_modifier)(int, int), + int distance_modifier_arg0, VP8LHistogram* const histo); // Free the memory allocated for the histogram. @@ -94,12 +97,6 @@ void VP8LHistogramSetClear(VP8LHistogramSet* const set); // 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); @@ -112,16 +109,16 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, int low_effort, int histogram_bits, int cache_bits, VP8LHistogramSet* const image_histo, VP8LHistogram* const tmp_histo, - uint16_t* const histogram_symbols, + uint32_t* const histogram_symbols, const WebPPicture* const pic, int percent_range, int* const percent); // Returns the entropy for the symbols in the input array. -float VP8LBitsEntropy(const uint32_t* const array, int n); +uint64_t VP8LBitsEntropy(const uint32_t* const array, int n); // Estimate how many bits the combined entropy of literals and distance // approximately maps to. -float VP8LHistogramEstimateBits(VP8LHistogram* const p); +uint64_t VP8LHistogramEstimateBits(const VP8LHistogram* const h); #ifdef __cplusplus } diff --git a/3rdparty/libwebp/src/enc/iterator_enc.c b/3rdparty/libwebp/src/enc/iterator_enc.c index 29f91d8315..bc807d5e41 100644 --- a/3rdparty/libwebp/src/enc/iterator_enc.c +++ b/3rdparty/libwebp/src/enc/iterator_enc.c @@ -13,87 +13,92 @@ #include +#include "src/dsp/cpu.h" +#include "src/dsp/dsp.h" #include "src/enc/vp8i_enc.h" +#include "src/utils/utils.h" +#include "src/webp/types.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_)); + 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_)); + 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_; + 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_; +// restart a scan +static void VP8IteratorReset(VP8EncIterator* const it) { + VP8Encoder* const enc = it->enc; VP8IteratorSetRow(it, 0); - VP8IteratorSetCountDown(it, enc->mb_w_ * enc->mb_h_); // default + VP8IteratorSetCountDown(it, enc->mb_w * enc->mb_h); // default InitTop(it); - memset(it->bit_count_, 0, sizeof(it->bit_count_)); - it->do_trellis_ = 0; + 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; + it->count_down = it->count_down0 = count_down; } int VP8IteratorIsDone(const VP8EncIterator* const it) { - return (it->count_down_ <= 0); + 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_; + 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_); + 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; } @@ -129,9 +134,9 @@ static void ImportLine(const uint8_t* src, int src_stride, } 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 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; @@ -140,9 +145,9 @@ void VP8IteratorImport(VP8EncIterator* const it, uint8_t* const tmp_32) { 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); + 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; @@ -151,19 +156,19 @@ void VP8IteratorImport(VP8EncIterator* const it, uint8_t* const tmp_32) { InitLeft(it); } else { if (y == 0) { - it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = 127; + 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]; + 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); + 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; + it->y_top = tmp_32 + 0; + it->uv_top = tmp_32 + 16; if (y == 0) { memset(tmp_32, 127, 32 * sizeof(*tmp_32)); } else { @@ -186,13 +191,13 @@ static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride, } 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_; + 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; @@ -232,9 +237,9 @@ void VP8IteratorExport(const VP8EncIterator* const it) { #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_; + 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); @@ -266,8 +271,8 @@ void VP8IteratorNzToBytes(VP8EncIterator* const it) { 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_; + 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); @@ -279,7 +284,7 @@ void VP8IteratorBytesToNz(VP8EncIterator* const it) { nz |= (left_nz[2] << 11); nz |= (left_nz[4] << 17) | (left_nz[6] << 21); - *it->nz_ = nz; + *it->nz = nz; } #undef BIT @@ -288,77 +293,77 @@ void VP8IteratorBytesToNz(VP8EncIterator* const it) { // 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 + 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]; + 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]; + 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]; + 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); + 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_); + 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; + it->preds += 4; + it->mb += 1; + it->nz += 1; + it->y_top += 16; + it->uv_top += 16; } - return (0 < --it->count_down_); + return (0 < --it->count_down); } //------------------------------------------------------------------------------ // Helper function to set mode properties void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) { - uint8_t* preds = it->preds_; + uint8_t* preds = it->preds; int y; for (y = 0; y < 4; ++y) { memset(preds, mode, 4); - preds += it->enc_->preds_w_; + preds += it->enc->preds_w; } - it->mb_->type_ = 1; + it->mb->type = 1; } void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes) { - uint8_t* preds = it->preds_; + uint8_t* preds = it->preds; int y; for (y = 4; y > 0; --y) { memcpy(preds, modes, 4 * sizeof(*modes)); - preds += it->enc_->preds_w_; + preds += it->enc->preds_w; modes += 4; } - it->mb_->type_ = 0; + it->mb->type = 0; } void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode) { - it->mb_->uv_mode_ = mode; + it->mb->uv_mode = mode; } void VP8SetSkip(const VP8EncIterator* const it, int skip) { - it->mb_->skip_ = skip; + it->mb->skip = skip; } void VP8SetSegment(const VP8EncIterator* const it, int segment) { - it->mb_->segment_ = segment; + it->mb->segment = segment; } //------------------------------------------------------------------------------ @@ -401,43 +406,52 @@ static const uint8_t VP8TopLeftI4[16] = { }; void VP8IteratorStartI4(VP8EncIterator* const it) { - const VP8Encoder* const enc = it->enc_; + const VP8Encoder* const enc = it->enc; int i; - it->i4_ = 0; // first 4x4 sub-block - it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0]; + 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]; + 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]; + 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) { + if (it->x < enc->mb_w - 1) { for (i = 16; i < 16 + 4; ++i) { - it->i4_boundary_[17 + i] = it->y_top_[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]; + it->i4_boundary[17 + i] = it->i4_boundary[17 + 15]; } } +#if WEBP_AARCH64 && BPS == 32 && defined(WEBP_MSAN) + // Intra4Preds_NEON() reads 3 uninitialized bytes from 'i4_boundary' when top + // is positioned at offset 29 (VP8TopLeftI4[3]). The values are not used + // meaningfully, but due to limitations in MemorySanitizer related to + // modeling of tbl instructions, a warning will be issued. This can be + // removed if MSan is updated to support the instructions. See + // https://issues.webmproject.org/372109644. + memset(it->i4_boundary + sizeof(it->i4_boundary) - 3, 0xaa, 3); +#endif 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_; + 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 + 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]; } @@ -447,12 +461,12 @@ int VP8IteratorRotateI4(VP8EncIterator* const it, } } // move pointers to next sub-block - ++it->i4_; - if (it->i4_ == 16) { // we're done + ++it->i4; + if (it->i4 == 16) { // we're done return 0; } - it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_]; + it->i4_top = it->i4_boundary + VP8TopLeftI4[it->i4]; return 1; } diff --git a/3rdparty/libwebp/src/enc/near_lossless_enc.c b/3rdparty/libwebp/src/enc/near_lossless_enc.c index 5517a7e271..4bb0e891b8 100644 --- a/3rdparty/libwebp/src/enc/near_lossless_enc.c +++ b/3rdparty/libwebp/src/enc/near_lossless_enc.c @@ -16,10 +16,13 @@ #include #include +#include #include "src/dsp/lossless_common.h" -#include "src/utils/utils.h" +#include "src/webp/types.h" #include "src/enc/vp8li_enc.h" +#include "src/utils/utils.h" +#include "src/webp/encode.h" #if (WEBP_NEAR_LOSSLESS == 1) diff --git a/3rdparty/libwebp/src/enc/picture_csp_enc.c b/3rdparty/libwebp/src/enc/picture_csp_enc.c index a9280e6c30..bf3c230443 100644 --- a/3rdparty/libwebp/src/enc/picture_csp_enc.c +++ b/3rdparty/libwebp/src/enc/picture_csp_enc.c @@ -12,18 +12,21 @@ // Author: Skal (pascal.massimino@gmail.com) #include -#include #include +#include +#include #include "sharpyuv/sharpyuv.h" #include "sharpyuv/sharpyuv_csp.h" -#include "src/enc/vp8i_enc.h" -#include "src/utils/random_utils.h" -#include "src/utils/utils.h" +#include "src/dsp/cpu.h" #include "src/dsp/dsp.h" #include "src/dsp/lossless.h" #include "src/dsp/yuv.h" -#include "src/dsp/cpu.h" +#include "src/enc/vp8i_enc.h" +#include "src/utils/random_utils.h" +#include "src/utils/utils.h" +#include "src/webp/encode.h" +#include "src/webp/types.h" #if defined(WEBP_USE_THREAD) && !defined(_WIN32) #include diff --git a/3rdparty/libwebp/src/enc/picture_enc.c b/3rdparty/libwebp/src/enc/picture_enc.c index 5a2703541f..3638e8499a 100644 --- a/3rdparty/libwebp/src/enc/picture_enc.c +++ b/3rdparty/libwebp/src/enc/picture_enc.c @@ -14,9 +14,12 @@ #include #include #include +#include #include "src/enc/vp8i_enc.h" #include "src/utils/utils.h" +#include "src/webp/encode.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // WebPPicture @@ -226,9 +229,7 @@ int WebPMemoryWrite(const uint8_t* data, size_t data_size, void WebPMemoryWriterClear(WebPMemoryWriter* writer) { if (writer != NULL) { WebPSafeFree(writer->mem); - writer->mem = NULL; - writer->size = 0; - writer->max_size = 0; + WebPMemoryWriterInit(writer); } } diff --git a/3rdparty/libwebp/src/enc/picture_psnr_enc.c b/3rdparty/libwebp/src/enc/picture_psnr_enc.c index 1a2f0bef3e..c6bb9b94b7 100644 --- a/3rdparty/libwebp/src/enc/picture_psnr_enc.c +++ b/3rdparty/libwebp/src/enc/picture_psnr_enc.c @@ -18,6 +18,7 @@ #include #include +#include "src/webp/types.h" #include "src/dsp/dsp.h" #include "src/enc/vp8i_enc.h" #include "src/utils/utils.h" diff --git a/3rdparty/libwebp/src/enc/picture_rescale_enc.c b/3rdparty/libwebp/src/enc/picture_rescale_enc.c index ea90d82548..097172bf0f 100644 --- a/3rdparty/libwebp/src/enc/picture_rescale_enc.c +++ b/3rdparty/libwebp/src/enc/picture_rescale_enc.c @@ -16,6 +16,8 @@ #include #include +#include "src/webp/types.h" +#include "src/dsp/dsp.h" #include "src/enc/vp8i_enc.h" #if !defined(WEBP_REDUCE_SIZE) diff --git a/3rdparty/libwebp/src/enc/picture_tools_enc.c b/3rdparty/libwebp/src/enc/picture_tools_enc.c index 147cc18608..a2ea36300f 100644 --- a/3rdparty/libwebp/src/enc/picture_tools_enc.c +++ b/3rdparty/libwebp/src/enc/picture_tools_enc.c @@ -12,9 +12,14 @@ // Author: Skal (pascal.massimino@gmail.com) #include +#include +#include -#include "src/enc/vp8i_enc.h" +#include "src/dsp/dsp.h" #include "src/dsp/yuv.h" +#include "src/enc/vp8i_enc.h" +#include "src/webp/encode.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Helper: clean up fully transparent area to help compressibility. diff --git a/3rdparty/libwebp/src/enc/predictor_enc.c b/3rdparty/libwebp/src/enc/predictor_enc.c index b3d44b59d5..3ead56c18b 100644 --- a/3rdparty/libwebp/src/enc/predictor_enc.c +++ b/3rdparty/libwebp/src/enc/predictor_enc.c @@ -14,53 +14,75 @@ // Urvang Joshi (urvang@google.com) // Vincent Rabaud (vrabaud@google.com) +#include +#include +#include + #include "src/dsp/lossless.h" #include "src/dsp/lossless_common.h" #include "src/enc/vp8i_enc.h" #include "src/enc/vp8li_enc.h" +#include "src/utils/utils.h" +#include "src/webp/encode.h" +#include "src/webp/format_constants.h" +#include "src/webp/types.h" -#define MAX_DIFF_COST (1e30f) - -static const float kSpatialPredictorBias = 15.f; +#define HISTO_SIZE (4 * 256) +static const int64_t kSpatialPredictorBias = 15ll << LOG_2_PRECISION_BITS; static const int kPredLowEffort = 11; static const uint32_t kMaskAlpha = 0xff000000; +static const int kNumPredModes = 14; // Mostly used to reduce code size + readability static WEBP_INLINE int GetMin(int a, int b) { return (a > b) ? b : a; } +static WEBP_INLINE int GetMax(int a, int b) { return (a < b) ? b : a; } //------------------------------------------------------------------------------ // Methods to calculate Entropy (Shannon). -static float PredictionCostSpatial(const int counts[256], int weight_0, - float exp_val) { +// Compute a bias for prediction entropy using a global heuristic to favor +// values closer to 0. Hence the final negative sign. +// 'exp_val' has a scaling factor of 1/100. +static int64_t PredictionCostBias(const uint32_t counts[256], uint64_t weight_0, + uint64_t exp_val) { const int significant_symbols = 256 >> 4; - const float exp_decay_factor = 0.6f; - float bits = (float)weight_0 * counts[0]; + const uint64_t exp_decay_factor = 6; // has a scaling factor of 1/10 + uint64_t bits = (weight_0 * counts[0]) << LOG_2_PRECISION_BITS; int i; + exp_val <<= LOG_2_PRECISION_BITS; for (i = 1; i < significant_symbols; ++i) { - bits += exp_val * (counts[i] + counts[256 - i]); - exp_val *= exp_decay_factor; + bits += DivRound(exp_val * (counts[i] + counts[256 - i]), 100); + exp_val = DivRound(exp_decay_factor * exp_val, 10); } - return (float)(-0.1 * bits); + return -DivRound((int64_t)bits, 10); } -static float PredictionCostSpatialHistogram(const int accumulated[4][256], - const int tile[4][256]) { +static int64_t PredictionCostSpatialHistogram( + const uint32_t accumulated[HISTO_SIZE], const uint32_t tile[HISTO_SIZE], + int mode, int left_mode, int above_mode) { int i; - float retval = 0.f; + int64_t retval = 0; for (i = 0; i < 4; ++i) { - const float kExpValue = 0.94f; - retval += PredictionCostSpatial(tile[i], 1, kExpValue); - retval += VP8LCombinedShannonEntropy(tile[i], accumulated[i]); + const uint64_t kExpValue = 94; + retval += PredictionCostBias(&tile[i * 256], 1, kExpValue); + // Compute the new cost if 'tile' is added to 'accumulate' but also add the + // cost of the current histogram to guide the spatial predictor selection. + // Basically, favor low entropy, locally and globally. + retval += (int64_t)VP8LCombinedShannonEntropy(&tile[i * 256], + &accumulated[i * 256]); } - return (float)retval; + // Favor keeping the areas locally similar. + if (mode == left_mode) retval -= kSpatialPredictorBias; + if (mode == above_mode) retval -= kSpatialPredictorBias; + return 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]; +static WEBP_INLINE void UpdateHisto(uint32_t histo_argb[HISTO_SIZE], + uint32_t argb) { + ++histo_argb[0 * 256 + (argb >> 24)]; + ++histo_argb[1 * 256 + ((argb >> 16) & 0xff)]; + ++histo_argb[2 * 256 + ((argb >> 8) & 0xff)]; + ++histo_argb[3 * 256 + (argb & 0xff)]; } //------------------------------------------------------------------------------ @@ -91,8 +113,6 @@ static WEBP_INLINE void PredictBatch(int mode, int x_start, int y, } #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)); @@ -291,23 +311,80 @@ static WEBP_INLINE void GetResidual( } } -// Returns best predictor and updates the accumulated histogram. +// Accessors to residual histograms. +static WEBP_INLINE uint32_t* GetHistoArgb(uint32_t* const all_histos, + int subsampling_index, int mode) { + return &all_histos[(subsampling_index * kNumPredModes + mode) * HISTO_SIZE]; +} + +static WEBP_INLINE const uint32_t* GetHistoArgbConst( + const uint32_t* const all_histos, int subsampling_index, int mode) { + return &all_histos[subsampling_index * kNumPredModes * HISTO_SIZE + + mode * HISTO_SIZE]; +} + +// Accessors to accumulated residual histogram. +static WEBP_INLINE uint32_t* GetAccumulatedHisto(uint32_t* all_accumulated, + int subsampling_index) { + return &all_accumulated[subsampling_index * HISTO_SIZE]; +} + +// Find and store the best predictor for a tile at subsampling +// 'subsampling_index'. +static void GetBestPredictorForTile(const uint32_t* const all_argb, + int subsampling_index, int tile_x, + int tile_y, int tiles_per_row, + uint32_t* all_accumulated_argb, + uint32_t** const all_modes, + uint32_t* const all_pred_histos) { + uint32_t* const accumulated_argb = + GetAccumulatedHisto(all_accumulated_argb, subsampling_index); + uint32_t* const modes = all_modes[subsampling_index]; + uint32_t* const pred_histos = + &all_pred_histos[subsampling_index * kNumPredModes]; + // 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; + int mode; + int64_t best_diff = WEBP_INT64_MAX; + uint32_t best_mode = 0; + const uint32_t* best_histo = + GetHistoArgbConst(all_argb, /*subsampling_index=*/0, best_mode); + for (mode = 0; mode < kNumPredModes; ++mode) { + const uint32_t* const histo_argb = + GetHistoArgbConst(all_argb, subsampling_index, mode); + const int64_t cur_diff = PredictionCostSpatialHistogram( + accumulated_argb, histo_argb, mode, left_mode, above_mode); + + if (cur_diff < best_diff) { + best_histo = histo_argb; + best_diff = cur_diff; + best_mode = mode; + } + } + // Update the accumulated histogram. + VP8LAddVectorEq(best_histo, accumulated_argb, HISTO_SIZE); + modes[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (best_mode << 8); + ++pred_histos[best_mode]; +} + +// Computes the residuals for the different predictors. // 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; +static void ComputeResidualsForTile( + int width, int height, int tile_x, int tile_y, int min_bits, + uint32_t update_up_to_index, uint32_t* const all_argb, + uint32_t* const argb_scratch, const uint32_t* const argb, + int max_quantization, int exact, int used_subtract_green) { + const int start_x = tile_x << min_bits; + const int start_y = tile_y << min_bits; + const int tile_size = 1 << min_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. @@ -318,35 +395,20 @@ static int GetBestPredictorForTile(int width, int height, #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)); + uint32_t* const histo_argb = + GetHistoArgb(all_argb, /*subsampling_index=*/0, mode); 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 @@ -382,41 +444,31 @@ static int GetBestPredictorForTile(int width, int height, 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; + if (update_up_to_index > 0) { + uint32_t subsampling_index; + for (subsampling_index = 1; subsampling_index <= update_up_to_index; + ++subsampling_index) { + uint32_t* const super_histo = + GetHistoArgb(all_argb, subsampling_index, mode); + for (relative_x = 0; relative_x < max_x; ++relative_x) { + UpdateHisto(super_histo, residuals[relative_x]); + } + } + } } } - - 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, +static void CopyImageWithPrediction(int width, int height, int bits, + const 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) { + 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 @@ -469,47 +521,307 @@ static void CopyImageWithPrediction(int width, int height, } } +// Checks whether 'image' can be subsampled by finding the biggest power of 2 +// squares (defined by 'best_bits') of uniform value it is made out of. +void VP8LOptimizeSampling(uint32_t* const image, int full_width, + int full_height, int bits, int max_bits, + int* best_bits_out) { + int width = VP8LSubSampleSize(full_width, bits); + int height = VP8LSubSampleSize(full_height, bits); + int old_width, x, y, square_size; + int best_bits = bits; + *best_bits_out = bits; + // Check rows first. + while (best_bits < max_bits) { + const int new_square_size = 1 << (best_bits + 1 - bits); + int is_good = 1; + square_size = 1 << (best_bits - bits); + for (y = 0; y + square_size < height; y += new_square_size) { + // Check the first lines of consecutive line groups. + if (memcmp(&image[y * width], &image[(y + square_size) * width], + width * sizeof(*image)) != 0) { + is_good = 0; + break; + } + } + if (is_good) { + ++best_bits; + } else { + break; + } + } + if (best_bits == bits) return; + + // Check columns. + while (best_bits > bits) { + int is_good = 1; + square_size = 1 << (best_bits - bits); + for (y = 0; is_good && y < height; ++y) { + for (x = 0; is_good && x < width; x += square_size) { + int i; + for (i = x + 1; i < GetMin(x + square_size, width); ++i) { + if (image[y * width + i] != image[y * width + x]) { + is_good = 0; + break; + } + } + } + } + if (is_good) { + break; + } + --best_bits; + } + if (best_bits == bits) return; + + // Subsample the image. + old_width = width; + square_size = 1 << (best_bits - bits); + width = VP8LSubSampleSize(full_width, best_bits); + height = VP8LSubSampleSize(full_height, best_bits); + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + image[y * width + x] = image[square_size * (y * old_width + x)]; + } + } + *best_bits_out = best_bits; +} + +// Computes the best predictor image. +// Finds the best predictors per tile. Once done, finds the best predictor image +// sampling. +// best_bits is set to 0 in case of error. +// The following requires some glossary: +// - a tile is a square of side 2^min_bits pixels. +// - a super-tile of a tile is a square of side 2^bits pixels with bits in +// [min_bits+1, max_bits]. +// - the max-tile of a tile is the square of 2^max_bits pixels containing it. +// If this max-tile crosses the border of an image, it is cropped. +// - tile, super-tiles and max_tile are aligned on powers of 2 in the original +// image. +// - coordinates for tile, super-tile, max-tile are respectively named +// tile_x, super_tile_x, max_tile_x at their bit scale. +// - in the max-tile, a tile has local coordinates (local_tile_x, local_tile_y). +// The tiles are processed in the following zigzag order to complete the +// super-tiles as soon as possible: +// 1 2| 5 6 +// 3 4| 7 8 +// -------------- +// 9 10| 13 14 +// 11 12| 15 16 +// When computing the residuals for a tile, the histogram of the above +// super-tile is updated. If this super-tile is finished, its histogram is used +// to update the histogram of the next super-tile and so on up to the max-tile. +static void GetBestPredictorsAndSubSampling( + int width, int height, const int min_bits, const int max_bits, + uint32_t* const argb_scratch, const uint32_t* const argb, + int max_quantization, int exact, int used_subtract_green, + const WebPPicture* const pic, int percent_range, int* const percent, + uint32_t** const all_modes, int* best_bits, uint32_t** best_mode) { + const uint32_t tiles_per_row = VP8LSubSampleSize(width, min_bits); + const uint32_t tiles_per_col = VP8LSubSampleSize(height, min_bits); + int64_t best_cost; + uint32_t subsampling_index; + const uint32_t max_subsampling_index = max_bits - min_bits; + // Compute the needed memory size for residual histograms, accumulated + // residual histograms and predictor histograms. + const int num_argb = (max_subsampling_index + 1) * kNumPredModes * HISTO_SIZE; + const int num_accumulated_rgb = (max_subsampling_index + 1) * HISTO_SIZE; + const int num_predictors = (max_subsampling_index + 1) * kNumPredModes; + uint32_t* const raw_data = (uint32_t*)WebPSafeCalloc( + num_argb + num_accumulated_rgb + num_predictors, sizeof(uint32_t)); + uint32_t* const all_argb = raw_data; + uint32_t* const all_accumulated_argb = all_argb + num_argb; + uint32_t* const all_pred_histos = all_accumulated_argb + num_accumulated_rgb; + const int max_tile_size = 1 << max_subsampling_index; // in tile size + int percent_start = *percent; + // When using the residuals of a tile for its super-tiles, you can either: + // - use each residual to update the histogram of the super-tile, with a cost + // of 4 * (1<> subsampling_index; + const uint32_t super_tile_y = tile_y >> subsampling_index; + const uint32_t super_tiles_per_row = + VP8LSubSampleSize(width, min_bits + subsampling_index); + GetBestPredictorForTile(all_argb, subsampling_index, super_tile_x, + super_tile_y, super_tiles_per_row, + all_accumulated_argb, all_modes, all_pred_histos); + if (subsampling_index == max_subsampling_index) break; + + // Update the following super-tile histogram if it has not been updated + // yet. + ++subsampling_index; + if (subsampling_index > update_up_to_index && + subsampling_index <= max_subsampling_index) { + VP8LAddVectorEq( + GetHistoArgbConst(all_argb, subsampling_index - 1, /*mode=*/0), + GetHistoArgb(all_argb, subsampling_index, /*mode=*/0), + HISTO_SIZE * kNumPredModes); + } + // Check whether the super-tile is not complete (if the smallest tile + // is not at the end of a line/column or at the beginning of a super-tile + // of size (1 << subsampling_index)). + if (!((tile_x == (tiles_per_row - 1) || + (local_tile_x + 1) % (1 << subsampling_index) == 0) && + (tile_y == (tiles_per_col - 1) || + (local_tile_y + 1) % (1 << subsampling_index) == 0))) { + --subsampling_index; + // subsampling_index now is the index of the last finished super-tile. + break; + } + } + // Reset all the histograms belonging to finished tiles. + memset(all_argb, 0, + HISTO_SIZE * kNumPredModes * (subsampling_index + 1) * + sizeof(*all_argb)); + + if (subsampling_index == max_subsampling_index) { + // If a new max-tile is started. + if (tile_x == (tiles_per_row - 1)) { + max_tile_x = 0; + ++max_tile_y; + } else { + ++max_tile_x; + } + local_tile_x = 0; + local_tile_y = 0; + } else { + // Proceed with the Z traversal. + uint32_t coord_x = local_tile_x >> subsampling_index; + uint32_t coord_y = local_tile_y >> subsampling_index; + if (tile_x == (tiles_per_row - 1) && coord_x % 2 == 0) { + ++coord_y; + } else { + if (coord_x % 2 == 0) { + ++coord_x; + } else { + // Z traversal. + ++coord_y; + --coord_x; + } + } + local_tile_x = coord_x << subsampling_index; + local_tile_y = coord_y << subsampling_index; + } + tile_x = max_tile_x * max_tile_size + local_tile_x; + tile_y = max_tile_y * max_tile_size + local_tile_y; + + if (tile_x == 0 && + !WebPReportProgress( + pic, percent_start + percent_range * tile_y / tiles_per_col, + percent)) { + WebPSafeFree(raw_data); + return; + } + } + + // Figure out the best sampling. + best_cost = WEBP_INT64_MAX; + for (subsampling_index = 0; subsampling_index <= max_subsampling_index; + ++subsampling_index) { + int plane; + const uint32_t* const accumulated = + GetAccumulatedHisto(all_accumulated_argb, subsampling_index); + int64_t cost = VP8LShannonEntropy( + &all_pred_histos[subsampling_index * kNumPredModes], kNumPredModes); + for (plane = 0; plane < 4; ++plane) { + cost += VP8LShannonEntropy(&accumulated[plane * 256], 256); + } + if (cost < best_cost) { + best_cost = cost; + *best_bits = min_bits + subsampling_index; + *best_mode = all_modes[subsampling_index]; + } + } + + WebPSafeFree(raw_data); + + VP8LOptimizeSampling(*best_mode, width, height, *best_bits, + MAX_TRANSFORM_BITS, best_bits); +} + // 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. -int 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 WebPPicture* const pic, int percent_range, - int* const percent) { - const int tiles_per_row = VP8LSubSampleSize(width, bits); - const int tiles_per_col = VP8LSubSampleSize(height, bits); +int VP8LResidualImage(int width, int height, int min_bits, int max_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 WebPPicture* const pic, + int percent_range, int* const percent, + int* const best_bits) { int percent_start = *percent; - int tile_y; - int histo[4][256]; const int max_quantization = 1 << VP8LNearLosslessBits(near_lossless_quality); if (low_effort) { + const int tiles_per_row = VP8LSubSampleSize(width, max_bits); + const int tiles_per_col = VP8LSubSampleSize(height, max_bits); int i; for (i = 0; i < tiles_per_row * tiles_per_col; ++i) { image[i] = ARGB_BLACK | (kPredLowEffort << 8); } + *best_bits = max_bits; } 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); - } - - if (!WebPReportProgress( - pic, percent_start + percent_range * tile_y / tiles_per_col, - percent)) { - return 0; - } + // Allocate data to try all samplings from min_bits to max_bits. + int bits; + uint32_t sum_num_pixels = 0u; + uint32_t *modes_raw, *best_mode; + uint32_t* modes[MAX_TRANSFORM_BITS + 1]; + uint32_t num_pixels[MAX_TRANSFORM_BITS + 1]; + for (bits = min_bits; bits <= max_bits; ++bits) { + const int tiles_per_row = VP8LSubSampleSize(width, bits); + const int tiles_per_col = VP8LSubSampleSize(height, bits); + num_pixels[bits] = tiles_per_row * tiles_per_col; + sum_num_pixels += num_pixels[bits]; } + modes_raw = (uint32_t*)WebPSafeMalloc(sum_num_pixels, sizeof(*modes_raw)); + if (modes_raw == NULL) return 0; + // Have modes point to the right global memory modes_raw. + modes[min_bits] = modes_raw; + for (bits = min_bits + 1; bits <= max_bits; ++bits) { + modes[bits] = modes[bits - 1] + num_pixels[bits - 1]; + } + // Find the best sampling. + GetBestPredictorsAndSubSampling( + width, height, min_bits, max_bits, argb_scratch, argb, max_quantization, + exact, used_subtract_green, pic, percent_range, percent, + &modes[min_bits], best_bits, &best_mode); + if (*best_bits == 0) { + WebPSafeFree(modes_raw); + return 0; + } + // Keep the best predictor image. + memcpy(image, best_mode, + VP8LSubSampleSize(width, *best_bits) * + VP8LSubSampleSize(height, *best_bits) * sizeof(*image)); + WebPSafeFree(modes_raw); } - CopyImageWithPrediction(width, height, bits, image, argb_scratch, argb, + CopyImageWithPrediction(width, height, *best_bits, image, argb_scratch, argb, low_effort, max_quantization, exact, used_subtract_green); return WebPReportProgress(pic, percent_start + percent_range, percent); @@ -519,68 +831,71 @@ int VP8LResidualImage(int width, int height, int bits, int low_effort, // 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; + 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; + 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_; + ((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]) { +static int64_t PredictionCostCrossColor(const uint32_t accumulated[256], + const uint32_t counts[256]) { // Favor low entropy, locally and globally. // Favor small absolute values for PredictionCostSpatial - static const float kExpValue = 2.4f; - return VP8LCombinedShannonEntropy(counts, accumulated) + - PredictionCostSpatial(counts, 3, kExpValue); + static const uint64_t kExpValue = 240; + return (int64_t)VP8LCombinedShannonEntropy(counts, accumulated) + + PredictionCostBias(counts, 3, kExpValue); } -static float GetPredictionCostCrossColorRed( +static int64_t 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; + const uint32_t accumulated_red_histo[256]) { + uint32_t histo[256] = { 0 }; + int64_t 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_x.green_to_red) { + // favor keeping the areas locally similar + cur_diff -= 3ll << LOG_2_PRECISION_BITS; } - if ((uint8_t)green_to_red == prev_y.green_to_red_) { - cur_diff -= 3; // favor keeping the areas locally similar + if ((uint8_t)green_to_red == prev_y.green_to_red) { + // favor keeping the areas locally similar + cur_diff -= 3ll << LOG_2_PRECISION_BITS; } if (green_to_red == 0) { - cur_diff -= 3; + cur_diff -= 3ll << LOG_2_PRECISION_BITS; } 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) { +static void GetBestGreenToRed(const uint32_t* argb, int stride, int tile_width, + int tile_height, VP8LMultipliers prev_x, + VP8LMultipliers prev_y, int quality, + const uint32_t 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); + int64_t 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 @@ -589,7 +904,7 @@ static void GetBestGreenToRed( // 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( + const int64_t 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) { @@ -598,48 +913,53 @@ static void GetBestGreenToRed( } } } - best_tx->green_to_red_ = (green_to_red_best & 0xff); + best_tx->green_to_red = (green_to_red_best & 0xff); } -static float GetPredictionCostCrossColorBlue( +static int64_t 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; + VP8LMultipliers prev_x, VP8LMultipliers prev_y, int green_to_blue, + int red_to_blue, const uint32_t accumulated_blue_histo[256]) { + uint32_t histo[256] = { 0 }; + int64_t 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_x.green_to_blue) { + // favor keeping the areas locally similar + cur_diff -= 3ll << LOG_2_PRECISION_BITS; } - if ((uint8_t)green_to_blue == prev_y.green_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar + if ((uint8_t)green_to_blue == prev_y.green_to_blue) { + // favor keeping the areas locally similar + cur_diff -= 3ll << LOG_2_PRECISION_BITS; } - 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_x.red_to_blue) { + // favor keeping the areas locally similar + cur_diff -= 3ll << LOG_2_PRECISION_BITS; } - if ((uint8_t)red_to_blue == prev_y.red_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar + if ((uint8_t)red_to_blue == prev_y.red_to_blue) { + // favor keeping the areas locally similar + cur_diff -= 3ll << LOG_2_PRECISION_BITS; } if (green_to_blue == 0) { - cur_diff -= 3; + cur_diff -= 3ll << LOG_2_PRECISION_BITS; } if (red_to_blue == 0) { - cur_diff -= 3; + cur_diff -= 3ll << LOG_2_PRECISION_BITS; } 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) { +static void GetBestGreenRedToBlue(const uint32_t* argb, int stride, + int tile_width, int tile_height, + VP8LMultipliers prev_x, + VP8LMultipliers prev_y, int quality, + const uint32_t 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 }; @@ -649,9 +969,9 @@ static void GetBestGreenRedToBlue( 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); + int64_t 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; @@ -659,7 +979,7 @@ static void GetBestGreenRedToBlue( 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( + const int64_t 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) { @@ -677,20 +997,17 @@ static void GetBestGreenRedToBlue( 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; + 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) { + int tile_x, int tile_y, int bits, VP8LMultipliers prev_x, + VP8LMultipliers prev_y, int quality, int xsize, int ysize, + const uint32_t accumulated_red_histo[256], + const uint32_t 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; @@ -728,13 +1045,13 @@ static void CopyTileWithColorTransform(int xsize, int ysize, int VP8LColorSpaceTransform(int width, int height, int bits, int quality, uint32_t* const argb, uint32_t* image, const WebPPicture* const pic, int percent_range, - int* const percent) { + int* const percent, int* const best_bits) { const int max_tile_size = 1 << bits; const int tile_xsize = VP8LSubSampleSize(width, bits); const int tile_ysize = VP8LSubSampleSize(height, bits); int percent_start = *percent; - int accumulated_red_histo[256] = { 0 }; - int accumulated_blue_histo[256] = { 0 }; + uint32_t accumulated_red_histo[256] = { 0 }; + uint32_t accumulated_blue_histo[256] = { 0 }; int tile_x, tile_y; VP8LMultipliers prev_x, prev_y; MultipliersClear(&prev_y); @@ -788,5 +1105,7 @@ int VP8LColorSpaceTransform(int width, int height, int bits, int quality, return 0; } } + VP8LOptimizeSampling(image, width, height, bits, MAX_TRANSFORM_BITS, + best_bits); return 1; } diff --git a/3rdparty/libwebp/src/enc/quant_enc.c b/3rdparty/libwebp/src/enc/quant_enc.c index 6d8202d277..4b8cb5e9be 100644 --- a/3rdparty/libwebp/src/enc/quant_enc.c +++ b/3rdparty/libwebp/src/enc/quant_enc.c @@ -14,10 +14,14 @@ #include #include #include // for abs() +#include +#include "src/dec/common_dec.h" +#include "src/dsp/dsp.h" #include "src/dsp/quant.h" -#include "src/enc/vp8i_enc.h" #include "src/enc/cost_enc.h" +#include "src/enc/vp8i_enc.h" +#include "src/webp/types.h" #define DO_TRELLIS_I4 1 #define DO_TRELLIS_I16 1 // not a huge gain, but ok at low bitrate. @@ -54,11 +58,11 @@ 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; + 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]); @@ -211,26 +215,26 @@ static int ExpandMatrix(VP8Matrix* const m, int type) { 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: + 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]; + 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]; + 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; + m->sharpen[i] = (kFreqSharpening[i] * m->q[i]) >> SHARPEN_BITS; } else { - m->sharpen_[i] = 0; + m->sharpen[i] = 0; } - sum += m->q_[i]; + sum += m->q[i]; } return (sum + 8) >> 4; } @@ -240,49 +244,49 @@ 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 + (enc->method >= 4) ? enc->config->sns_strength : 0; - const int num_segments = enc->segment_hdr_.num_segments_; + 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_; + 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->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->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)]; + 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); + 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; + 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_); + 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->min_disto = 20 * m->y1.q[0]; // quantization-aware min disto + m->max_edge = 0; - m->i4_penalty_ = 1000 * q_i4 * q_i4; + m->i4_penalty = 1000 * q_i4 * q_i4; } } @@ -296,21 +300,21 @@ static void SetupMatrices(VP8Encoder* enc) { 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; + const int level0 = 5 * enc->config->filter_strength; for (i = 0; i < NUM_MB_SEGMENTS; ++i) { - VP8SegmentInfo* const m = &enc->dqm_[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 qstep = kAcTable[clip(m->quant, 0, 127)] >> 2; const int base_strength = - VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, qstep); + 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; + 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; + 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; } //------------------------------------------------------------------------------ @@ -356,25 +360,25 @@ static double QualityToJPEGCompression(double c, double alpha) { static int SegmentsAreEquivalent(const VP8SegmentInfo* const S1, const VP8SegmentInfo* const S2) { - return (S1->quant_ == S2->quant_) && (S1->fstrength_ == S2->fstrength_); + 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 + // '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_ + // 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]; + 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]; + const VP8SegmentInfo* const S2 = &enc->dqm[s2]; if (SegmentsAreEquivalent(S1, S2)) { found = 1; break; @@ -383,18 +387,18 @@ static void SimplifySegments(VP8Encoder* const enc) { map[s1] = s2; if (!found) { if (num_final_segments != s1) { - enc->dqm_[num_final_segments] = enc->dqm_[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; + 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]; + enc->dqm[i] = enc->dqm[num_final_segments - 1]; } } } @@ -402,50 +406,50 @@ static void SimplifySegments(VP8Encoder* const enc) { 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 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.) : + 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 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); + 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_; + 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_; + enc->dqm[i].quant = enc->base_quant; } - // uv_alpha_ is normally spread around ~60. The useful range is + // 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); + 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; + 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 = -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; + 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 @@ -462,24 +466,26 @@ 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] = { +static 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); + 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); + 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_); +// Form all the ten Intra4x4 predictions in the 'yuv_p' cache +// for the 4x4 block it->i4 +static void MakeIntra4Preds(const VP8EncIterator* const it) { + VP8EncPredLuma4(it->yuv_p, it->i4_top); } //------------------------------------------------------------------------------ @@ -595,9 +601,9 @@ static int TrellisQuantizeBlock(const VP8Encoder* WEBP_RESTRICT const enc, int ctx0, int coeff_type, const VP8Matrix* WEBP_RESTRICT const mtx, int lambda) { - const ProbaArray* const probas = enc->proba_.coeffs_[coeff_type]; + const ProbaArray* const probas = enc->proba.coeffs[coeff_type]; CostArrayPtr const costs = - (CostArrayPtr)enc->proba_.remapped_costs_[coeff_type]; + (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]; @@ -609,7 +615,7 @@ static int TrellisQuantizeBlock(const VP8Encoder* WEBP_RESTRICT const enc, { score_t cost; - const int thresh = mtx->q_[1] * mtx->q_[1] / 4; + 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 @@ -641,13 +647,13 @@ static int TrellisQuantizeBlock(const VP8Encoder* WEBP_RESTRICT const enc, // 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 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]; + 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; @@ -755,7 +761,7 @@ static int TrellisQuantizeBlock(const VP8Encoder* WEBP_RESTRICT const enc, const int j = kZigzag[n]; out[n] = node->sign ? -node->level : node->level; nz |= node->level; - in[j] = out[n] * mtx->q_[j]; + in[j] = out[n] * mtx->q[j]; best_node = node->prev; } return (nz != 0); @@ -773,10 +779,10 @@ static int ReconstructIntra16(VP8EncIterator* WEBP_RESTRICT const it, VP8ModeScore* WEBP_RESTRICT const rd, uint8_t* WEBP_RESTRICT 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_]; + 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]; @@ -785,18 +791,18 @@ static int ReconstructIntra16(VP8EncIterator* WEBP_RESTRICT const it, 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; + nz |= VP8EncQuantizeBlockWHT(dc_tmp, rd->y_dc_levels, &dqm->y2) << 24; - if (DO_TRELLIS_I16 && it->do_trellis_) { + 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 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; + 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; } @@ -806,7 +812,7 @@ static int ReconstructIntra16(VP8EncIterator* WEBP_RESTRICT const it, // 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; + 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); } @@ -826,20 +832,20 @@ static int ReconstructIntra4(VP8EncIterator* WEBP_RESTRICT const it, const uint8_t* WEBP_RESTRICT const src, uint8_t* WEBP_RESTRICT 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_]; + 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_); + 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_); + nz = VP8EncQuantizeBlock(tmp, levels, &dqm->y1); } VP8ITransform(ref, tmp, yuv_out, 0); return nz; @@ -862,8 +868,8 @@ static int QuantizeSingle(int16_t* WEBP_RESTRICT const v, 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]; + 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; @@ -885,8 +891,8 @@ static void CorrectDCValues(const VP8EncIterator* WEBP_RESTRICT const it, // 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]; + 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); @@ -897,7 +903,7 @@ static void CorrectDCValues(const VP8EncIterator* WEBP_RESTRICT const it, 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 + // 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; @@ -910,8 +916,8 @@ static void StoreDiffusionErrors(VP8EncIterator* WEBP_RESTRICT const it, const VP8ModeScore* WEBP_RESTRICT 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]; + 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 @@ -929,10 +935,10 @@ static void StoreDiffusionErrors(VP8EncIterator* WEBP_RESTRICT const it, static int ReconstructUV(VP8EncIterator* WEBP_RESTRICT const it, VP8ModeScore* WEBP_RESTRICT const rd, uint8_t* WEBP_RESTRICT 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_]; + 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]; @@ -940,25 +946,25 @@ static int ReconstructUV(VP8EncIterator* WEBP_RESTRICT const it, 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 (it->top_derr != NULL) CorrectDCValues(it, &dqm->uv, tmp, rd); - if (DO_TRELLIS_UV && it->do_trellis_) { + 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 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; + 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; + nz |= VP8EncQuantize2Blocks(tmp[n], rd->uv_levels[n], &dqm->uv) << n; } } @@ -980,7 +986,7 @@ static void StoreMaxDelta(VP8SegmentInfo* const dqm, const int16_t DCs[16]) { 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; + if (max_v > dqm->max_edge) dqm->max_edge = max_v; } static void SwapModeScore(VP8ModeScore** a, VP8ModeScore** b) { @@ -996,25 +1002,25 @@ static void SwapPtr(uint8_t** a, uint8_t** b) { } static void SwapOut(VP8EncIterator* const it) { - SwapPtr(&it->yuv_out_, &it->yuv_out2_); + SwapPtr(&it->yuv_out, &it->yuv_out2); } static void PickBestIntra16(VP8EncIterator* WEBP_RESTRICT const it, VP8ModeScore* WEBP_RESTRICT 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; + 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); + 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 + uint8_t* const tmp_dst = it->yuv_out2 + Y_OFF_ENC; // scratch buffer rd_cur->mode_i16 = mode; // Reconstruct @@ -1046,13 +1052,13 @@ static void PickBestIntra16(VP8EncIterator* WEBP_RESTRICT const it, if (rd_best != rd) { memcpy(rd, rd_best, sizeof(*rd)); } - SetRDScore(dqm->lambda_mode_, rd); // finalize score for mode decision. + 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_) { + if ((rd->nz & 0x100ffff) == 0x1000000 && rd->D > dqm->min_disto) { StoreMaxDelta(dqm, rd->y_dc_levels); } } @@ -1062,51 +1068,51 @@ static void PickBestIntra16(VP8EncIterator* WEBP_RESTRICT const it, // return the cost array corresponding to the surrounding prediction modes. static const uint16_t* GetCostModeI4(VP8EncIterator* WEBP_RESTRICT 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]; + 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* WEBP_RESTRICT const it, VP8ModeScore* WEBP_RESTRICT 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; + 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) { + 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); + 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 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. + uint8_t* best_block = best_blocks + VP8Scan[it->i4]; + uint8_t* tmp_dst = it->yuv_p + I4TMP; // scratch buffer. InitScore(&rd_i4); - VP8MakeIntra4Preds(it); + MakeIntra4Preds(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_; + ReconstructIntra4(it, tmp_levels, src, tmp_dst, mode) << it->i4; // Compute RD-score rd_tmp.D = VP8SSE4x4(src, tmp_dst); @@ -1135,25 +1141,25 @@ static int PickBestIntra4(VP8EncIterator* WEBP_RESTRICT const it, 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_])); + memcpy(rd_best.y_ac_levels[it->i4], tmp_levels, + sizeof(rd_best.y_ac_levels[it->i4])); } } - SetRDScore(dqm->lambda_mode_, &rd_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_) { + 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_]); + 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); + 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 @@ -1169,11 +1175,11 @@ static int PickBestIntra4(VP8EncIterator* WEBP_RESTRICT const it, static void PickBestUV(VP8EncIterator* WEBP_RESTRICT const it, VP8ModeScore* WEBP_RESTRICT 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; + 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; @@ -1200,7 +1206,7 @@ static void PickBestUV(VP8EncIterator* WEBP_RESTRICT const it, 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) { + if (it->top_derr != NULL) { memcpy(rd->derr, rd_uv.derr, sizeof(rd_uv.derr)); } SwapPtr(&dst, &tmp_dst); @@ -1211,7 +1217,7 @@ static void PickBestUV(VP8EncIterator* WEBP_RESTRICT const it, if (dst != dst0) { // copy 16x8 block if needed VP8Copy16x8(dst, dst0); } - if (it->top_derr_ != NULL) { // store diffusion errors for next block + if (it->top_derr != NULL) { // store diffusion errors for next block StoreDiffusionErrors(it, rd); } } @@ -1221,26 +1227,26 @@ static void PickBestUV(VP8EncIterator* WEBP_RESTRICT const it, static void SimpleQuantize(VP8EncIterator* WEBP_RESTRICT const it, VP8ModeScore* WEBP_RESTRICT const rd) { - const VP8Encoder* const enc = it->enc_; - const int is_i16 = (it->mb_->type_ == 1); + 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]); + 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)); + 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]; + MakeIntra4Preds(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_); + nz |= ReconstructUV(it, rd, it->yuv_out + U_OFF_ENC, it->mb->uv_mode); rd->nz = nz; } @@ -1251,23 +1257,23 @@ static void RefineUsingDistortion(VP8EncIterator* WEBP_RESTRICT const it, score_t best_score = MAX_COST; int nz = 0; int mode; - int is_i16 = try_both_modes || (it->mb_->type_ == 1); + int is_i16 = try_both_modes || (it->mb->type == 1); - const VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_]; + 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 score_i4 = dqm->i4_penalty; score_t i4_bit_sum = 0; - const score_t bit_limit = try_both_modes ? it->enc_->mb_header_limit_ + 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; + 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 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) { @@ -1279,10 +1285,10 @@ static void RefineUsingDistortion(VP8EncIterator* WEBP_RESTRICT const it, best_score = score; } } - if (it->x_ == 0 || it->y_ == 0) { + 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; + best_mode = (it->x == 0) ? 0 : 2; try_both_modes = 0; // stick to i16 } } @@ -1299,12 +1305,12 @@ static void RefineUsingDistortion(VP8EncIterator* WEBP_RESTRICT const 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 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); + MakeIntra4Preds(it); for (mode = 0; mode < NUM_BMODES; ++mode) { - const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[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) { @@ -1313,18 +1319,18 @@ static void RefineUsingDistortion(VP8EncIterator* WEBP_RESTRICT const it, } } i4_bit_sum += mode_costs[best_i4_mode]; - rd->modes_i4[it->i4_] = 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_; + } 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)); + } while (VP8IteratorRotateI4(it, it->yuv_out2 + Y_OFF_ENC)); } // Final reconstruction, depending on which mode is selected. @@ -1333,16 +1339,16 @@ static void RefineUsingDistortion(VP8EncIterator* WEBP_RESTRICT const it, SwapOut(it); best_score = score_i4; } else { - nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF_ENC, it->preds_[0]); + 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; + 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 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) { @@ -1352,7 +1358,7 @@ static void RefineUsingDistortion(VP8EncIterator* WEBP_RESTRICT const it, } VP8SetIntraUVMode(it, best_mode); } - nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF_ENC, it->mb_->uv_mode_); + nz |= ReconstructUV(it, rd, it->yuv_out + U_OFF_ENC, it->mb->uv_mode); rd->nz = nz; rd->score = best_score; @@ -1365,7 +1371,7 @@ int VP8Decimate(VP8EncIterator* WEBP_RESTRICT const it, VP8ModeScore* WEBP_RESTRICT const rd, VP8RDLevel rd_opt) { int is_skipped; - const int method = it->enc_->method_; + const int method = it->enc->method; InitScore(rd); @@ -1375,14 +1381,14 @@ int VP8Decimate(VP8EncIterator* WEBP_RESTRICT const it, VP8MakeChroma8Preds(it); if (rd_opt > RD_OPT_NONE) { - it->do_trellis_ = (rd_opt >= RD_OPT_TRELLIS_ALL); + 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; + it->do_trellis = 1; SimpleQuantize(it, rd); } } else { diff --git a/3rdparty/libwebp/src/enc/syntax_enc.c b/3rdparty/libwebp/src/enc/syntax_enc.c index 9b8f524d69..6edc3c21f3 100644 --- a/3rdparty/libwebp/src/enc/syntax_enc.c +++ b/3rdparty/libwebp/src/enc/syntax_enc.c @@ -12,18 +12,23 @@ // Author: Skal (pascal.massimino@gmail.com) #include +#include +#include "src/dec/common_dec.h" +#include "src/webp/types.h" +#include "src/enc/vp8i_enc.h" +#include "src/utils/bit_writer_utils.h" #include "src/utils/utils.h" +#include "src/webp/encode.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. + 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) { @@ -36,7 +41,7 @@ static int PutPaddingByte(const WebPPicture* const pic) { static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc, size_t riff_size) { - const WebPPicture* const pic = enc->pic_; + const WebPPicture* const pic = enc->pic; uint8_t riff[RIFF_HEADER_SIZE] = { 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P' }; @@ -49,7 +54,7 @@ static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc, } static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) { - const WebPPicture* const pic = enc->pic_; + const WebPPicture* const pic = enc->pic; uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = { 'V', 'P', '8', 'X' }; @@ -59,7 +64,7 @@ static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) { assert(pic->width >= 1 && pic->height >= 1); assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE); - if (enc->has_alpha_) { + if (enc->has_alpha) { flags |= ALPHA_FLAG; } @@ -74,26 +79,26 @@ static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) { } static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) { - const WebPPicture* const pic = enc->pic_; + const WebPPicture* const pic = enc->pic; uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = { 'A', 'L', 'P', 'H' }; - assert(enc->has_alpha_); + assert(enc->has_alpha); // Alpha chunk header. - PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_); + 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)) { + 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)) { + if ((enc->alpha_data_size & 1) && !PutPaddingByte(pic)) { return VP8_ENC_ERROR_BAD_WRITE; } return VP8_ENC_OK; @@ -148,7 +153,7 @@ static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic, // 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_; + WebPPicture* const pic = enc->pic; WebPEncodingError err = VP8_ENC_OK; // RIFF header. @@ -162,7 +167,7 @@ static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0, } // Alpha. - if (enc->has_alpha_) { + if (enc->has_alpha) { err = PutAlphaChunk(enc); if (err != VP8_ENC_OK) goto Error; } @@ -172,7 +177,7 @@ static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0, if (err != VP8_ENC_OK) goto Error; // VP8 frame header. - err = PutVP8FrameHeader(pic, enc->profile_, size0); + err = PutVP8FrameHeader(pic, enc->profile, size0); if (err != VP8_ENC_OK) goto Error; // All OK. @@ -186,27 +191,27 @@ static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0, // 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))) { + 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_); + 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); + VP8PutSignedBits(bw, enc->dqm[s].quant, 7); } for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - VP8PutSignedBits(bw, enc->dqm_[s].fstrength_, 6); + VP8PutSignedBits(bw, enc->dqm[s].fstrength, 6); } } - if (hdr->update_map_) { + if (hdr->update_map) { for (s = 0; s < 3; ++s) { - if (VP8PutBitUniform(bw, (proba->segments_[s] != 255u))) { - VP8PutBits(bw, proba->segments_[s], 8); + if (VP8PutBitUniform(bw, (proba->segments[s] != 255u))) { + VP8PutBits(bw, proba->segments[s], 8); } } } @@ -216,18 +221,18 @@ static void PutSegmentHeader(VP8BitWriter* const bw, // 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); + 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); + // '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); + VP8PutSignedBits(bw, hdr->i4x4_lf_delta, 6); VP8PutBits(bw, 0, 3); // all others unused } } @@ -236,12 +241,12 @@ static void PutFilterHeader(VP8BitWriter* const bw, // 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); + 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 @@ -249,8 +254,8 @@ 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); + 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); } @@ -267,25 +272,25 @@ static int EmitPartitionsSize(const VP8Encoder* const enc, //------------------------------------------------------------------------------ static int GeneratePartition0(VP8Encoder* const enc) { - VP8BitWriter* const bw = &enc->bw_; - const int mb_size = enc->mb_w_ * enc->mb_h_; + 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); + 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); + 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_); + VP8WriteProbas(bw, &enc->proba); pos2 = VP8BitWriterPos(bw); VP8CodeIntraModes(enc); VP8BitWriterFinish(bw); @@ -293,36 +298,36 @@ static int GeneratePartition0(VP8Encoder* const enc) { 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_; + 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); + 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); + 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_; + 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; + 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; @@ -334,9 +339,9 @@ int VP8EncWrite(VP8Encoder* const enc) { // 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); + 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; @@ -347,9 +352,9 @@ int VP8EncWrite(VP8Encoder* const enc) { 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); + 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. @@ -368,13 +373,13 @@ int VP8EncWrite(VP8Encoder* const enc) { } // 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); + 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_); + VP8BitWriterWipeOut(enc->parts + p); // will free the internal buffer. + ok = ok && WebPReportProgress(pic, enc->percent + percent_per_part, + &enc->percent); } // Padding byte @@ -382,11 +387,10 @@ int VP8EncWrite(VP8Encoder* const enc) { ok = PutPaddingByte(pic); } - enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size); - ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_); + enc->coded_size = (int)(CHUNK_HEADER_SIZE + riff_size); + ok = ok && WebPReportProgress(pic, final_percent, &enc->percent); if (!ok) WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); return ok; } //------------------------------------------------------------------------------ - diff --git a/3rdparty/libwebp/src/enc/token_enc.c b/3rdparty/libwebp/src/enc/token_enc.c index 3a2192acac..9c8b8ee9c1 100644 --- a/3rdparty/libwebp/src/enc/token_enc.c +++ b/3rdparty/libwebp/src/enc/token_enc.c @@ -20,9 +20,13 @@ #include #include +#include "src/dec/common_dec.h" +#include "src/dsp/dsp.h" #include "src/enc/cost_enc.h" #include "src/enc/vp8i_enc.h" +#include "src/utils/bit_writer_utils.h" #include "src/utils/utils.h" +#include "src/webp/types.h" #if !defined(DISABLE_TOKEN_BUFFER) @@ -34,51 +38,51 @@ 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 + VP8Tokens* next; // pointer to next page }; -// Token data is located in memory just after the next_ field. +// 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; + 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_; + VP8Tokens* p = b->pages; while (p != NULL) { - VP8Tokens* const next = p->next_; + VP8Tokens* const next = p->next; WebPSafeFree(p); p = next; } - VP8TBufferInit(b, b->page_size_); + 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); + 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; + b->error = 1; return 0; } - page->next_ = NULL; + page->next = NULL; - *b->last_page_ = page; - b->last_page_ = &page->next_; - b->left_ = b->page_size_; - b->tokens_ = (token_t*)TOKEN_DATA(page); + *b->last_page = page; + b->last_page = &page->next; + b->left = b->page_size; + b->tokens = (token_t*)TOKEN_DATA(page); return 1; } @@ -92,9 +96,9 @@ static WEBP_INLINE uint32_t AddToken(VP8TBuffer* const b, uint32_t bit, 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; + if (b->left > 0 || TBufferNewPage(b)) { + const int slot = --b->left; + b->tokens[slot] = (bit << 15) | proba_idx; } VP8RecordStats(bit, stats); return bit; @@ -104,9 +108,9 @@ 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; + if (b->left > 0 || TBufferNewPage(b)) { + const int slot = --b->left; + b->tokens[slot] = (bit << 15) | FIXED_PROBA_BIT | proba; } } @@ -199,12 +203,12 @@ int VP8RecordCoeffTokens(int ctx, const struct VP8Residual* const res, int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, const uint8_t* const probas, int final_pass) { - const VP8Tokens* p = b->pages_; - assert(!b->error_); + 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 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]; @@ -218,19 +222,19 @@ int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, if (final_pass) WebPSafeFree((void*)p); p = next; } - if (final_pass) b->pages_ = NULL; + 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_); + 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 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]; @@ -259,4 +263,3 @@ void VP8TBufferClear(VP8TBuffer* const b) { } #endif // !DISABLE_TOKEN_BUFFER - diff --git a/3rdparty/libwebp/src/enc/tree_enc.c b/3rdparty/libwebp/src/enc/tree_enc.c index 64ed28360b..f6dbb25f7a 100644 --- a/3rdparty/libwebp/src/enc/tree_enc.c +++ b/3rdparty/libwebp/src/enc/tree_enc.c @@ -11,7 +11,12 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include + +#include "src/dec/common_dec.h" +#include "src/webp/types.h" #include "src/enc/vp8i_enc.h" +#include "src/utils/bit_writer_utils.h" //------------------------------------------------------------------------------ // Default probabilities @@ -154,13 +159,13 @@ const uint8_t }; 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, + 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; + probas->dirty = 1; } // Paragraph 11.5. 900bytes. @@ -311,22 +316,22 @@ static void PutSegment(VP8BitWriter* const bw, int s, const uint8_t* p) { } void VP8CodeIntraModes(VP8Encoder* const enc) { - VP8BitWriter* const bw = &enc->bw_; + 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_); + 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 (enc->proba.use_skip_proba) { + VP8PutBit(bw, mb->skip, enc->proba.skip_proba); } - if (VP8PutBit(bw, (mb->type_ != 0), 145)) { // i16x16 + if (VP8PutBit(bw, (mb->type != 0), 145)) { // i16x16 PutI16Mode(bw, preds[0]); } else { - const int preds_w = enc->preds_w_; + const int preds_w = enc->preds_w; const uint8_t* top_pred = preds - preds_w; int x, y; for (y = 0; y < 4; ++y) { @@ -339,7 +344,7 @@ void VP8CodeIntraModes(VP8Encoder* const enc) { preds += preds_w; } } - PutUVMode(bw, mb->uv_mode_); + PutUVMode(bw, mb->uv_mode); } while (VP8IteratorNext(&it)); } @@ -488,7 +493,7 @@ void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas) { 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 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); @@ -497,8 +502,7 @@ void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas) { } } } - if (VP8PutBitUniform(bw, probas->use_skip_proba_)) { - VP8PutBits(bw, probas->skip_proba_, 8); + if (VP8PutBitUniform(bw, probas->use_skip_proba)) { + VP8PutBits(bw, probas->skip_proba, 8); } } - diff --git a/3rdparty/libwebp/src/enc/vp8i_enc.h b/3rdparty/libwebp/src/enc/vp8i_enc.h index 00ff1be795..22a2d87b22 100644 --- a/3rdparty/libwebp/src/enc/vp8i_enc.h +++ b/3rdparty/libwebp/src/enc/vp8i_enc.h @@ -15,12 +15,15 @@ #define WEBP_ENC_VP8I_ENC_H_ #include // for memcpy() + #include "src/dec/common_dec.h" +#include "src/dsp/cpu.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" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { @@ -31,7 +34,7 @@ extern "C" { // version numbers #define ENC_MAJ_VERSION 1 -#define ENC_MIN_VERSION 4 +#define ENC_MIN_VERSION 6 #define ENC_REV_VERSION 0 enum { MAX_LF_LEVELS = 64, // Maximum loop filter level @@ -48,9 +51,9 @@ typedef enum { // Rate-distortion optimization levels // 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 predicted blocks can be accessed using offsets to 'yuv_p' and // the arrays VP8*ModeOffsets[]. -// * YUV Samples area (yuv_in_/yuv_out_/yuv_out2_) +// * 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): // +----+----+ @@ -59,7 +62,7 @@ typedef enum { // Rate-distortion optimization levels // V_OFF_ENC |YYYY|....| <- 25% wasted U/V area // |YYYY|....| // +----+----+ -// * Prediction area ('yuv_p_', size = PRED_SIZE_ENC) +// * Prediction area ('yuv_p', size = PRED_SIZE_ENC) // Intra16 predictions (16x16 block each, two per row): // |I16DC16|I16TM16| // |I16VE16|I16HE16| @@ -78,7 +81,6 @@ typedef enum { // Rate-distortion optimization levels 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 @@ -138,32 +140,32 @@ 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. + 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 + 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 + 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 + 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; //------------------------------------------------------------------------------ @@ -171,37 +173,37 @@ typedef struct { 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 + 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 + 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. + 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 + 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_; + 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 + score_t i4_penalty; // penalty for using Intra4 } VP8SegmentInfo; typedef int8_t DError[2 /* u/v */][2 /* top or left */]; @@ -224,51 +226,53 @@ typedef struct { // 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 + 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 +#if WEBP_AARCH64 && BPS == 32 + uint8_t i4_boundary[40]; // 32+8 boundary samples needed by intra4x4 +#else + uint8_t i4_boundary[37]; // 32+5 boundary samples needed by intra4x4 +#endif + 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 + 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_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 + 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]; + // 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) @@ -283,7 +287,8 @@ void VP8IteratorImport(VP8EncIterator* const it, uint8_t* const tmp_32); 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. +// 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); @@ -311,13 +316,13 @@ 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 + 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 + int error; // true in case of malloc error } VP8TBuffer; // initialize an empty buffer @@ -344,72 +349,72 @@ size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas); // VP8Encoder struct VP8Encoder { - const WebPConfig* config_; // user configuration and parameters - WebPPicture* pic_; // input / output picture + const WebPConfig* config; // user configuration and parameters + WebPPicture* pic; // input / output picture // headers - VP8EncFilterHeader filter_hdr_; // filtering information - VP8EncSegmentHeader segment_hdr_; // segment information + VP8EncFilterHeader filter_hdr; // filtering information + VP8EncSegmentHeader segment_hdr; // segment information - int profile_; // VP8's profile, deduced from Config. + 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) + 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_; + int num_parts; // per-partition boolean decoders. - VP8BitWriter bw_; // part0 - VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions - VP8TBuffer tokens_; // token buffer + VP8BitWriter bw; // part0 + VP8BitWriter parts[MAX_NUM_PARTITIONS]; // token partitions + VP8TBuffer tokens; // token buffer - int percent_; // for progress + 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_; + 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 + 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 + 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_; + 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]; + 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 + 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) + 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) }; //------------------------------------------------------------------------------ @@ -440,13 +445,10 @@ 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 +// 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 +// 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]); @@ -463,11 +465,11 @@ int WebPReportProgress(const WebPPicture* const pic, // in analysis.c // Main analysis loop. Decides the segmentations and complexity. -// Assigns a first guess for Intra16 and uvmode_ prediction modes. +// 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. +// 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* WEBP_RESTRICT const it, diff --git a/3rdparty/libwebp/src/enc/vp8l_enc.c b/3rdparty/libwebp/src/enc/vp8l_enc.c index 40eafa4169..57651b8d1a 100644 --- a/3rdparty/libwebp/src/enc/vp8l_enc.c +++ b/3rdparty/libwebp/src/enc/vp8l_enc.c @@ -14,6 +14,7 @@ #include #include +#include #include "src/dsp/lossless.h" #include "src/dsp/lossless_common.h" @@ -24,12 +25,18 @@ #include "src/utils/bit_writer_utils.h" #include "src/utils/huffman_encode_utils.h" #include "src/utils/palette.h" +#include "src/utils/thread_utils.h" #include "src/utils/utils.h" #include "src/webp/encode.h" #include "src/webp/format_constants.h" +#include "src/webp/types.h" // Maximum number of histogram images (sub-blocks). #define MAX_HUFF_IMAGE_SIZE 2600 +#define MAX_HUFFMAN_BITS (MIN_HUFFMAN_BITS + (1 << NUM_HUFFMAN_BITS) - 1) +// Empirical value for which it becomes too computationally expensive to +// compute the best predictor image. +#define MAX_PREDICTOR_IMAGE_SIZE (1 << 14) // ----------------------------------------------------------------------------- // Palette @@ -62,23 +69,35 @@ typedef enum { kHistoTotal // Must be last. } HistoIx; + +#define NUM_BUCKETS 256 + +typedef uint32_t HistogramBuckets[NUM_BUCKETS]; + +// Keeping track of histograms, indexed by HistoIx. +// Ideally, this would just be a struct with meaningful fields, but the +// calculation of `entropy_comp` uses the index. One refactoring at a time :) +typedef struct { + HistogramBuckets category[kHistoTotal]; +} Histograms; + static void AddSingleSubGreen(uint32_t p, - uint32_t* const r, uint32_t* const b) { + HistogramBuckets r, HistogramBuckets b) { const int green = (int)p >> 8; // The upper bits are masked away later. ++r[(((int)p >> 16) - green) & 0xff]; ++b[(((int)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) { + HistogramBuckets a, HistogramBuckets r, + HistogramBuckets g, HistogramBuckets 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) { +static WEBP_INLINE uint8_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; @@ -90,8 +109,7 @@ static int AnalyzeEntropy(const uint32_t* argb, 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; + Histograms* histo; if (use_palette && palette_size <= 16) { // In the case of small palettes, we pack 2, 4 or 8 pixels together. In @@ -100,7 +118,8 @@ static int AnalyzeEntropy(const uint32_t* argb, *red_and_blue_always_zero = 1; return 1; } - histo = (uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256); + + histo = (Histograms*)WebPSafeCalloc(1, sizeof(*histo)); if (histo != NULL) { int i, x, y; const uint32_t* prev_row = NULL; @@ -115,48 +134,48 @@ static int AnalyzeEntropy(const uint32_t* argb, continue; } AddSingle(pix, - &histo[kHistoAlpha * 256], - &histo[kHistoRed * 256], - &histo[kHistoGreen * 256], - &histo[kHistoBlue * 256]); + histo->category[kHistoAlpha], + histo->category[kHistoRed], + histo->category[kHistoGreen], + histo->category[kHistoBlue]); AddSingle(pix_diff, - &histo[kHistoAlphaPred * 256], - &histo[kHistoRedPred * 256], - &histo[kHistoGreenPred * 256], - &histo[kHistoBluePred * 256]); + histo->category[kHistoAlphaPred], + histo->category[kHistoRedPred], + histo->category[kHistoGreenPred], + histo->category[kHistoBluePred]); AddSingleSubGreen(pix, - &histo[kHistoRedSubGreen * 256], - &histo[kHistoBlueSubGreen * 256]); + histo->category[kHistoRedSubGreen], + histo->category[kHistoBlueSubGreen]); AddSingleSubGreen(pix_diff, - &histo[kHistoRedPredSubGreen * 256], - &histo[kHistoBluePredSubGreen * 256]); + histo->category[kHistoRedPredSubGreen], + histo->category[kHistoBluePredSubGreen]); { // Approximate the palette by the entropy of the multiplicative hash. - const uint32_t hash = HashPix(pix); - ++histo[kHistoPalette * 256 + hash]; + const uint8_t hash = HashPix(pix); + ++histo->category[kHistoPalette][hash]; } } prev_row = curr_row; curr_row += argb_stride; } { - float entropy_comp[kHistoTotal]; - float entropy[kNumEntropyIx]; + uint64_t entropy_comp[kHistoTotal]; + uint64_t 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]; + ++histo->category[kHistoRedPredSubGreen][0]; + ++histo->category[kHistoBluePredSubGreen][0]; + ++histo->category[kHistoRedPred][0]; + ++histo->category[kHistoGreenPred][0]; + ++histo->category[kHistoBluePred][0]; + ++histo->category[kHistoAlphaPred][0]; for (j = 0; j < kHistoTotal; ++j) { - entropy_comp[j] = VP8LBitsEntropy(&histo[j * 256], 256); + entropy_comp[j] = VP8LBitsEntropy(histo->category[j], NUM_BUCKETS); } entropy[kDirect] = entropy_comp[kHistoAlpha] + entropy_comp[kHistoRed] + @@ -179,19 +198,19 @@ static int AnalyzeEntropy(const uint32_t* argb, // 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) * + entropy[kSpatial] += (uint64_t)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); + entropy[kSpatialSubGreen] += + (uint64_t)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; + entropy[kPalette] += (palette_size * 8ull) << LOG_2_PRECISION_BITS; *min_entropy_ix = kDirect; for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) { @@ -212,12 +231,12 @@ static int AnalyzeEntropy(const uint32_t* argb, { 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) { + const HistogramBuckets* const red_histo = + &histo->category[kHistoPairs[*min_entropy_ix][0]]; + const HistogramBuckets* const blue_histo = + &histo->category[kHistoPairs[*min_entropy_ix][1]]; + for (i = 1; i < NUM_BUCKETS; ++i) { + if (((*red_histo)[i] | (*blue_histo)[i]) != 0) { *red_and_blue_always_zero = 0; break; } @@ -231,17 +250,33 @@ static int AnalyzeEntropy(const uint32_t* argb, } } +// Clamp histogram and transform bits. +static int ClampBits(int width, int height, int bits, int min_bits, + int max_bits, int image_size_max) { + int image_size; + bits = (bits < min_bits) ? min_bits : (bits > max_bits) ? max_bits : bits; + image_size = VP8LSubSampleSize(width, bits) * VP8LSubSampleSize(height, bits); + while (bits < max_bits && image_size > image_size_max) { + ++bits; + image_size = + VP8LSubSampleSize(width, bits) * VP8LSubSampleSize(height, bits); + } + // In case the bits reduce the image too much, choose the smallest value + // setting the histogram image size to 1. + while (bits > min_bits && image_size == 1) { + image_size = VP8LSubSampleSize(width, bits - 1) * + VP8LSubSampleSize(height, bits - 1); + if (image_size != 1) break; + --bits; + } + return bits; +} + 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; + const int histo_bits = (use_palette ? 9 : 7) - method; + return ClampBits(width, height, histo_bits, MIN_HUFFMAN_BITS, + MAX_HUFFMAN_BITS, MAX_HUFF_IMAGE_SIZE); } static int GetTransformBits(int method, int histo_bits) { @@ -255,14 +290,14 @@ static int GetTransformBits(int method, int histo_bits) { // 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_; + 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_; + 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 @@ -273,14 +308,14 @@ 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 WebPPicture* const pic = enc->pic; const int width = pic->width; const int height = pic->height; - const WebPConfig* const config = enc->config_; + const WebPConfig* const config = enc->config; const int method = config->method; const int low_effort = (config->method == 0); int i; - int use_palette; + int use_palette, transform_bits; int n_lz77s; // If set to 0, analyze the cache with the computed cache value. If 1, also // analyze with no-cache. @@ -288,31 +323,33 @@ static int EncoderAnalyze(VP8LEncoder* const enc, assert(pic != NULL && pic->argb != NULL); // Check whether a palette is possible. - enc->palette_size_ = GetColorPalette(pic, enc->palette_sorted_); - use_palette = (enc->palette_size_ <= MAX_PALETTE_SIZE); + enc->palette_size = GetColorPalette(pic, enc->palette_sorted); + use_palette = (enc->palette_size <= MAX_PALETTE_SIZE); if (!use_palette) { - enc->palette_size_ = 0; + enc->palette_size = 0; } // Empirical bit sizes. - enc->histo_bits_ = GetHistoBits(method, use_palette, - pic->width, pic->height); - enc->transform_bits_ = GetTransformBits(method, enc->histo_bits_); + enc->histo_bits = GetHistoBits(method, use_palette, + pic->width, pic->height); + transform_bits = GetTransformBits(method, enc->histo_bits); + enc->predictor_transform_bits = transform_bits; + enc->cross_color_transform_bits = transform_bits; if (low_effort) { // AnalyzeEntropy is somewhat slow. - crunch_configs[0].entropy_idx_ = use_palette ? kPalette : kSpatialSubGreen; - crunch_configs[0].palette_sorting_type_ = + 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; + 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)) { + enc->palette_size, transform_bits, &min_entropy_ix, + red_and_blue_always_zero)) { return 0; } if (method == 6 && config->quality == 100) { @@ -336,14 +373,14 @@ static int EncoderAnalyze(VP8LEncoder* const enc, typed_sorting_method == kSortedDefault) { continue; } - crunch_configs[(*crunch_configs_size)].entropy_idx_ = i; - crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = + crunch_configs[(*crunch_configs_size)].entropy_idx = i; + crunch_configs[(*crunch_configs_size)].palette_sorting_type = typed_sorting_method; ++*crunch_configs_size; } } else { - crunch_configs[(*crunch_configs_size)].entropy_idx_ = i; - crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = + crunch_configs[(*crunch_configs_size)].entropy_idx = i; + crunch_configs[(*crunch_configs_size)].palette_sorting_type = kUnusedPalette; ++*crunch_configs_size; } @@ -352,8 +389,8 @@ static int EncoderAnalyze(VP8LEncoder* const enc, } 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_ = + 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. @@ -361,8 +398,8 @@ static int EncoderAnalyze(VP8LEncoder* const enc, // 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; + crunch_configs[1].entropy_idx = kPaletteAndSpatial; + crunch_configs[1].palette_sorting_type = kMinimizeDelta; } } } @@ -373,17 +410,17 @@ static int EncoderAnalyze(VP8LEncoder* const enc, int j; for (j = 0; j < n_lz77s; ++j) { assert(j < CRUNCH_SUBCONFIGS_MAX); - crunch_configs[i].sub_configs_[j].lz77_ = + 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[j].do_no_cache = do_no_cache; } - crunch_configs[i].sub_configs_size_ = n_lz77s; + crunch_configs[i].sub_configs_size = n_lz77s; } return 1; } static int EncoderInit(VP8LEncoder* const enc) { - const WebPPicture* const pic = enc->pic_; + const WebPPicture* const pic = enc->pic; const int width = pic->width; const int height = pic->height; const int pix_cnt = width * height; @@ -391,9 +428,9 @@ static int EncoderInit(VP8LEncoder* const enc) { // 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; + if (!VP8LHashChainInit(&enc->hash_chain, pix_cnt)) return 0; - for (i = 0; i < 4; ++i) VP8LBackwardRefsInit(&enc->refs_[i], refs_block_size); + for (i = 0; i < 4; ++i) VP8LBackwardRefsInit(&enc->refs[i], refs_block_size); return 1; } @@ -418,7 +455,7 @@ static int GetHuffBitLengthsAndCodes( assert(histo != NULL); for (k = 0; k < 5; ++k) { const int num_symbols = - (k == 0) ? VP8LHistogramNumCodes(histo->palette_code_bits_) : + (k == 0) ? VP8LHistogramNumCodes(histo->palette_code_bits) : (k == 4) ? NUM_DISTANCE_CODES : 256; codes[k].num_symbols = num_symbols; total_length_size += num_symbols; @@ -456,11 +493,11 @@ static int GetHuffBitLengthsAndCodes( 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); + 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: @@ -661,11 +698,12 @@ static WEBP_INLINE void WriteHuffmanCodeWithExtraBits( VP8LPutBits(bw, (bits << depth) | symbol, depth + n_bits); } -static int StoreImageToBitMask( - VP8LBitWriter* const bw, int width, int histo_bits, - const VP8LBackwardRefs* const refs, - const uint16_t* histogram_symbols, - const HuffmanTreeCode* const huffman_codes, const WebPPicture* const pic) { +static int StoreImageToBitMask(VP8LBitWriter* const bw, int width, + int histo_bits, + const VP8LBackwardRefs* const refs, + const uint32_t* histogram_symbols, + const HuffmanTreeCode* const huffman_codes, + const WebPPicture* const pic) { 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. @@ -673,7 +711,7 @@ static int StoreImageToBitMask( int y = 0; int tile_x = x & tile_mask; int tile_y = y & tile_mask; - int histogram_ix = histogram_symbols[0]; + int histogram_ix = (histogram_symbols[0] >> 8) & 0xffff; const HuffmanTreeCode* codes = huffman_codes + 5 * histogram_ix; VP8LRefsCursor c = VP8LRefsCursorInit(refs); while (VP8LRefsCursorOk(&c)) { @@ -681,8 +719,10 @@ static int StoreImageToBitMask( 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)]; + histogram_ix = (histogram_symbols[(y >> histo_bits) * histo_xsize + + (x >> histo_bits)] >> + 8) & + 0xffff; codes = huffman_codes + 5 * histogram_ix; } if (PixOrCopyIsLiteral(v)) { @@ -718,7 +758,7 @@ static int StoreImageToBitMask( } VP8LRefsCursorNext(&c); } - if (bw->error_) { + if (bw->error) { return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } return 1; @@ -738,7 +778,7 @@ static int EncodeImageNoHuffman(VP8LBitWriter* const bw, VP8LBackwardRefs* refs; HuffmanTreeToken* tokens = NULL; HuffmanTreeCode huffman_codes[5] = {{0, NULL, NULL}}; - const uint16_t histogram_symbols[1] = {0}; // only one tree, one symbol + const uint32_t histogram_symbols[1] = {0}; // only one tree, one symbol int cache_bits = 0; VP8LHistogramSet* histogram_image = NULL; HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc( @@ -769,7 +809,9 @@ static int EncodeImageNoHuffman(VP8LBitWriter* const bw, VP8LHistogramSetClear(histogram_image); // Build histogram image and symbols from backward references. - VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]); + VP8LHistogramStoreRefs(refs, /*distance_modifier=*/NULL, + /*distance_modifier_arg0=*/0, + histogram_image->histograms[0]); // Create Huffman bit lengths and codes for each histogram image. assert(histogram_image->size == 1); @@ -821,32 +863,32 @@ static int 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, const CrunchConfig* const config, - int* cache_bits, int histogram_bits, size_t init_byte_position, + int* cache_bits, int histogram_bits_in, size_t init_byte_position, int* const hdr_size, int* const data_size, const WebPPicture* const pic, int percent_range, int* const percent) { const uint32_t histogram_image_xysize = - VP8LSubSampleSize(width, histogram_bits) * - VP8LSubSampleSize(height, histogram_bits); + VP8LSubSampleSize(width, histogram_bits_in) * + VP8LSubSampleSize(height, histogram_bits_in); int remaining_percent = percent_range; int percent_start = *percent; VP8LHistogramSet* histogram_image = NULL; VP8LHistogram* tmp_histo = NULL; - int histogram_image_size = 0; + uint32_t i, 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)); + uint32_t* const histogram_argb = (uint32_t*)WebPSafeMalloc( + histogram_image_xysize, sizeof(*histogram_argb)); 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(histogram_bits_in >= MIN_HUFFMAN_BITS); + assert(histogram_bits_in <= MAX_HUFFMAN_BITS); assert(hdr_size != NULL); assert(data_size != NULL); @@ -857,7 +899,7 @@ static int EncodeImageInternal( } // Make sure we can allocate the different objects. - if (huff_tree == NULL || histogram_symbols == NULL || + if (huff_tree == NULL || histogram_argb == NULL || !VP8LHashChainInit(&hash_chain_histogram, histogram_image_xysize)) { WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); goto Error; @@ -875,30 +917,31 @@ static int EncodeImageInternal( // analysis. cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits; // If several iterations will happen, clone into bw_best. - if ((config->sub_configs_size_ > 1 || config->sub_configs_[0].do_no_cache_) && + if ((config->sub_configs_size > 1 || config->sub_configs[0].do_no_cache) && !VP8LBitWriterClone(bw, &bw_best)) { WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); goto Error; } - for (sub_configs_idx = 0; sub_configs_idx < config->sub_configs_size_; + 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]; + &config->sub_configs[sub_configs_idx]; int cache_bits_best, i_cache; - int i_remaining_percent = remaining_percent / config->sub_configs_size_; + int i_remaining_percent = remaining_percent / config->sub_configs_size; int i_percent_range = i_remaining_percent / 4; i_remaining_percent -= i_percent_range; if (!VP8LGetBackwardReferences( - width, height, argb, quality, low_effort, sub_config->lz77_, - cache_bits_init, sub_config->do_no_cache_, hash_chain, + 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, pic, i_percent_range, percent)) { goto Error; } - for (i_cache = 0; i_cache < (sub_config->do_no_cache_ ? 2 : 1); ++i_cache) { + 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; + int histogram_bits = histogram_bits_in; // 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; @@ -920,7 +963,7 @@ static int EncodeImageInternal( if (!VP8LGetHistoImageSymbols( width, height, &refs_array[i_cache], quality, low_effort, histogram_bits, cache_bits_tmp, histogram_image, tmp_histo, - histogram_symbols, pic, i_percent_range, percent)) { + histogram_argb, pic, i_percent_range, percent)) { goto Error; } // Create Huffman bit lengths and codes for each histogram image. @@ -953,26 +996,19 @@ static int EncodeImageInternal( } // Huffman image + meta huffman. + histogram_image_size = 0; + for (i = 0; i < histogram_image_xysize; ++i) { + if (histogram_argb[i] >= histogram_image_size) { + histogram_image_size = histogram_argb[i] + 1; + } + histogram_argb[i] <<= 8; + } + 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) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - goto Error; - } - for (i = 0; i < histogram_image_xysize; ++i) { - const int symbol_index = histogram_symbols[i] & 0xffff; - histogram_argb[i] = (symbol_index << 8); - if (symbol_index >= max_index) { - max_index = symbol_index + 1; - } - } - histogram_image_size = max_index; - + VP8LOptimizeSampling(histogram_argb, width, height, histogram_bits_in, + MAX_HUFFMAN_BITS, &histogram_bits); VP8LPutBits(bw, histogram_bits - 2, 3); i_percent_range = i_remaining_percent / 2; i_remaining_percent -= i_percent_range; @@ -981,15 +1017,12 @@ static int EncodeImageInternal( VP8LSubSampleSize(width, histogram_bits), VP8LSubSampleSize(height, histogram_bits), quality, low_effort, pic, i_percent_range, percent)) { - WebPSafeFree(histogram_argb); goto Error; } - WebPSafeFree(histogram_argb); } // 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) { @@ -1012,7 +1045,7 @@ static int EncodeImageInternal( // Store actual literals. hdr_size_tmp = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position); if (!StoreImageToBitMask(bw, width, histogram_bits, &refs_array[i_cache], - histogram_symbols, huffman_codes, pic)) { + histogram_argb, huffman_codes, pic)) { goto Error; } // Keep track of the smallest image so far. @@ -1049,7 +1082,7 @@ static int EncodeImageInternal( WebPSafeFree(huffman_codes->codes); WebPSafeFree(huffman_codes); } - WebPSafeFree(histogram_symbols); + WebPSafeFree(histogram_argb); VP8LBitWriterWipeOut(&bw_best); return (pic->error_code == VP8_ENC_OK); } @@ -1061,59 +1094,62 @@ static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height, VP8LBitWriter* const bw) { VP8LPutBits(bw, TRANSFORM_PRESENT, 1); VP8LPutBits(bw, SUBTRACT_GREEN_TRANSFORM, 2); - VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); + VP8LSubtractGreenFromBlueAndRed(enc->argb, width * height); } -static int ApplyPredictFilter(const VP8LEncoder* const enc, int width, - int height, int quality, int low_effort, +static int ApplyPredictFilter(VP8LEncoder* const enc, int width, int height, + int quality, int low_effort, int used_subtract_green, VP8LBitWriter* const bw, - int percent_range, int* const percent) { - 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. + int percent_range, int* const percent, + int* const best_bits) { const int near_lossless_strength = - enc->use_palette_ ? 100 : enc->config_->near_lossless; + enc->use_palette ? 100 : enc->config->near_lossless; + const int max_bits = ClampBits(width, height, enc->predictor_transform_bits, + MIN_TRANSFORM_BITS, MAX_TRANSFORM_BITS, + MAX_PREDICTOR_IMAGE_SIZE); + const int min_bits = ClampBits( + width, height, + max_bits - 2 * (enc->config->method > 4 ? enc->config->method - 4 : 0), + MIN_TRANSFORM_BITS, MAX_TRANSFORM_BITS, MAX_PREDICTOR_IMAGE_SIZE); - if (!VP8LResidualImage( - width, height, pred_bits, low_effort, enc->argb_, enc->argb_scratch_, - enc->transform_data_, near_lossless_strength, enc->config_->exact, - used_subtract_green, enc->pic_, percent_range / 2, percent)) { + if (!VP8LResidualImage(width, height, min_bits, max_bits, low_effort, + enc->argb, enc->argb_scratch, enc->transform_data, + near_lossless_strength, enc->config->exact, + used_subtract_green, enc->pic, percent_range / 2, + percent, best_bits)) { return 0; } VP8LPutBits(bw, TRANSFORM_PRESENT, 1); VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2); - assert(pred_bits >= 2); - VP8LPutBits(bw, pred_bits - 2, 3); + assert(*best_bits >= MIN_TRANSFORM_BITS && *best_bits <= MAX_TRANSFORM_BITS); + VP8LPutBits(bw, *best_bits - MIN_TRANSFORM_BITS, NUM_TRANSFORM_BITS); return EncodeImageNoHuffman( - bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_, - (VP8LBackwardRefs*)&enc->refs_[0], transform_width, transform_height, - quality, low_effort, enc->pic_, percent_range - percent_range / 2, - percent); + bw, enc->transform_data, &enc->hash_chain, &enc->refs[0], + VP8LSubSampleSize(width, *best_bits), + VP8LSubSampleSize(height, *best_bits), quality, low_effort, enc->pic, + percent_range - percent_range / 2, percent); } -static int ApplyCrossColorFilter(const VP8LEncoder* const enc, int width, - int height, int quality, int low_effort, +static int ApplyCrossColorFilter(VP8LEncoder* const enc, int width, int height, + int quality, int low_effort, VP8LBitWriter* const bw, int percent_range, - int* const percent) { - 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); + int* const percent, int* const best_bits) { + const int min_bits = enc->cross_color_transform_bits; - if (!VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality, - enc->argb_, enc->transform_data_, enc->pic_, - percent_range / 2, percent)) { + if (!VP8LColorSpaceTransform(width, height, min_bits, quality, enc->argb, + enc->transform_data, enc->pic, percent_range / 2, + percent, best_bits)) { return 0; } VP8LPutBits(bw, TRANSFORM_PRESENT, 1); VP8LPutBits(bw, CROSS_COLOR_TRANSFORM, 2); - assert(ccolor_transform_bits >= 2); - VP8LPutBits(bw, ccolor_transform_bits - 2, 3); + assert(*best_bits >= MIN_TRANSFORM_BITS && *best_bits <= MAX_TRANSFORM_BITS); + VP8LPutBits(bw, *best_bits - MIN_TRANSFORM_BITS, NUM_TRANSFORM_BITS); return EncodeImageNoHuffman( - bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_, - (VP8LBackwardRefs*)&enc->refs_[0], transform_width, transform_height, - quality, low_effort, enc->pic_, percent_range - percent_range / 2, - percent); + bw, enc->transform_data, &enc->hash_chain, &enc->refs[0], + VP8LSubSampleSize(width, *best_bits), + VP8LSubSampleSize(height, *best_bits), quality, low_effort, enc->pic, + percent_range - percent_range / 2, percent); } // ----------------------------------------------------------------------------- @@ -1137,13 +1173,13 @@ static int WriteImageSize(const WebPPicture* const pic, VP8LPutBits(bw, width, VP8L_IMAGE_SIZE_BITS); VP8LPutBits(bw, height, VP8L_IMAGE_SIZE_BITS); - return !bw->error_; + 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_; + return !bw->error; } static int WriteImage(const WebPPicture* const pic, VP8LBitWriter* const bw, @@ -1155,7 +1191,7 @@ static int WriteImage(const WebPPicture* const pic, VP8LBitWriter* const bw, const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad; *coded_size = 0; - if (bw->error_) { + if (bw->error) { return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } @@ -1177,16 +1213,16 @@ static int WriteImage(const WebPPicture* const pic, VP8LBitWriter* const bw, // ----------------------------------------------------------------------------- static void ClearTransformBuffer(VP8LEncoder* const enc) { - WebPSafeFree(enc->transform_mem_); - enc->transform_mem_ = NULL; - enc->transform_mem_size_ = 0; + 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_ +// enc->transform_bits +// enc->use_predict, enc->use_cross_color static int AllocateTransformBuffer(VP8LEncoder* const enc, int width, int height) { const uint64_t image_size = (uint64_t)width * height; @@ -1194,50 +1230,50 @@ static int AllocateTransformBuffer(VP8LEncoder* const enc, int width, // 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) + 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_) - ? (uint64_t)VP8LSubSampleSize(width, enc->transform_bits_) * - VP8LSubSampleSize(height, enc->transform_bits_) + (enc->use_predict || enc->use_cross_color) + ? (uint64_t)VP8LSubSampleSize(width, MIN_TRANSFORM_BITS) * + VP8LSubSampleSize(height, MIN_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_) { + 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) { - return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } - enc->transform_mem_ = mem; - enc->transform_mem_size_ = (size_t)mem_size; - enc->argb_content_ = kEncoderNone; + enc->transform_mem = mem; + enc->transform_mem_size = (size_t)mem_size; + enc->argb_content = kEncoderNone; } - enc->argb_ = mem; + enc->argb = mem; mem = (uint32_t*)WEBP_ALIGN(mem + image_size); - enc->argb_scratch_ = mem; + enc->argb_scratch = mem; mem = (uint32_t*)WEBP_ALIGN(mem + argb_scratch_size); - enc->transform_data_ = mem; + enc->transform_data = mem; - enc->current_width_ = width; + enc->current_width = width; return 1; } static int MakeInputImageCopy(VP8LEncoder* const enc) { - const WebPPicture* const picture = enc->pic_; + const WebPPicture* const picture = enc->pic; const int width = picture->width; const int height = picture->height; if (!AllocateTransformBuffer(enc, width, height)) return 0; - if (enc->argb_content_ == kEncoderARGB) return 1; + if (enc->argb_content == kEncoderARGB) return 1; { - uint32_t* dst = enc->argb_; + uint32_t* dst = enc->argb; const uint32_t* src = picture->argb; int y; for (y = 0; y < height; ++y) { @@ -1246,8 +1282,8 @@ static int MakeInputImageCopy(VP8LEncoder* const enc) { src += picture->argb_stride; } } - enc->argb_content_ = kEncoderARGB; - assert(enc->current_width_ == width); + enc->argb_content = kEncoderARGB; + assert(enc->current_width == width); return 1; } @@ -1373,15 +1409,13 @@ static int ApplyPalette(const uint32_t* src, uint32_t src_stride, uint32_t* dst, #undef PALETTE_INV_SIZE #undef APPLY_PALETTE_GREEDY_MAX -// Note: Expects "enc->palette_" to be set properly. -static int MapImageFromPalette(VP8LEncoder* const enc, int in_place) { - const WebPPicture* const pic = enc->pic_; +// Note: Expects "enc->palette" to be set properly. +static int MapImageFromPalette(VP8LEncoder* const enc) { + 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_; + const uint32_t* const palette = enc->palette; + const int palette_size = enc->palette_size; int xbits; // Replace each input pixel by corresponding palette index. @@ -1395,34 +1429,41 @@ static int MapImageFromPalette(VP8LEncoder* const enc, int in_place) { if (!AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height)) { return 0; } - if (!ApplyPalette(src, src_stride, - enc->argb_, enc->current_width_, - palette, palette_size, width, height, xbits, pic)) { + if (!ApplyPalette(pic->argb, pic->argb_stride, enc->argb, + enc->current_width, palette, palette_size, width, height, + xbits, pic)) { return 0; } - enc->argb_content_ = kEncoderPalette; + enc->argb_content = kEncoderPalette; return 1; } -// Save palette_[] to bitstream. -static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort, - VP8LEncoder* const enc, - int percent_range, int* const percent) { +// Save palette[] to bitstream. +static int EncodePalette(VP8LBitWriter* const bw, int low_effort, + VP8LEncoder* const enc, int percent_range, + int* const percent) { int i; uint32_t tmp_palette[MAX_PALETTE_SIZE]; - const int palette_size = enc->palette_size_; - const uint32_t* const palette = enc->palette_; + const int palette_size = enc->palette_size; + const uint32_t* const palette = enc->palette; + // If the last element is 0, do not store it and count on automatic palette + // 0-filling. This can only happen if there is no pixel packing, hence if + // there are strictly more than 16 colors (after 0 is removed). + const uint32_t encoded_palette_size = + (enc->palette[palette_size - 1] == 0 && palette_size > 17) + ? palette_size - 1 + : palette_size; 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) { + VP8LPutBits(bw, encoded_palette_size - 1, 8); + for (i = encoded_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, enc->pic_, percent_range, percent); + return EncodeImageNoHuffman( + bw, tmp_palette, &enc->hash_chain, &enc->refs[0], encoded_palette_size, + 1, /*quality=*/20, low_effort, enc->pic, percent_range, percent); } // ----------------------------------------------------------------------------- @@ -1435,9 +1476,9 @@ static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config, WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); return NULL; } - enc->config_ = config; - enc->pic_ = picture; - enc->argb_content_ = kEncoderNone; + enc->config = config; + enc->pic = picture; + enc->argb_content = kEncoderNone; VP8LEncDspInit(); @@ -1447,8 +1488,8 @@ static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config, static void VP8LEncoderDelete(VP8LEncoder* enc) { if (enc != NULL) { int i; - VP8LHashChainClear(&enc->hash_chain_); - for (i = 0; i < 4; ++i) VP8LBackwardRefsClear(&enc->refs_[i]); + VP8LHashChainClear(&enc->hash_chain); + for (i = 0; i < 4; ++i) VP8LBackwardRefsClear(&enc->refs[i]); ClearTransformBuffer(enc); WebPSafeFree(enc); } @@ -1458,27 +1499,27 @@ static void VP8LEncoderDelete(VP8LEncoder* enc) { // Main call typedef struct { - const WebPConfig* config_; - const WebPPicture* picture_; - VP8LBitWriter* bw_; - VP8LEncoder* enc_; - CrunchConfig crunch_configs_[CRUNCH_CONFIGS_MAX]; - int num_crunch_configs_; - int red_and_blue_always_zero_; - WebPAuxStats* stats_; + const WebPConfig* config; + const WebPPicture* picture; + VP8LBitWriter* bw; + VP8LEncoder* enc; + CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX]; + int num_crunch_configs; + int red_and_blue_always_zero; + 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 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_; + const WebPConfig* const config = params->config; + const WebPPicture* const picture = params->picture; + VP8LBitWriter* const bw = params->bw; + VP8LEncoder* const enc = params->enc; + 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_; + WebPAuxStats* const stats = params->stats; #endif const int quality = (int)config->quality; const int low_effort = (config->method == 0); @@ -1493,7 +1534,6 @@ static int EncodeStreamHook(void* input, void* data2) { #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; @@ -1506,51 +1546,52 @@ static int EncodeStreamHook(void* input, void* data2) { } for (idx = 0; idx < num_crunch_configs; ++idx) { - const int entropy_idx = crunch_configs[idx].entropy_idx_; + const int entropy_idx = crunch_configs[idx].entropy_idx; int remaining_percent = 97 / num_crunch_configs, percent_range; - enc->use_palette_ = + int predictor_transform_bits = 0, cross_color_transform_bits = 0; + enc->use_palette = (entropy_idx == kPalette) || (entropy_idx == kPaletteAndSpatial); - enc->use_subtract_green_ = + enc->use_subtract_green = (entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen); - enc->use_predict_ = (entropy_idx == kSpatial) || - (entropy_idx == kSpatialSubGreen) || - (entropy_idx == kPaletteAndSpatial); + 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; + 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_; + 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]); + 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_; + use_near_lossless = (config->near_lossless < 100) && !enc->use_palette && + !enc->use_predict; if (use_near_lossless) { if (!AllocateTransformBuffer(enc, width, height)) goto Error; - if ((enc->argb_content_ != kEncoderNearLossless) && - !VP8ApplyNearLossless(picture, config->near_lossless, enc->argb_)) { + if ((enc->argb_content != kEncoderNearLossless) && + !VP8ApplyNearLossless(picture, config->near_lossless, enc->argb)) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); goto Error; } - enc->argb_content_ = kEncoderNearLossless; + enc->argb_content = kEncoderNearLossless; } else { - enc->argb_content_ = kEncoderNone; + enc->argb_content = kEncoderNone; } #else - enc->argb_content_ = kEncoderNone; + enc->argb_content = kEncoderNone; #endif // Encode palette - if (enc->use_palette_) { - if (!PaletteSort(crunch_configs[idx].palette_sorting_type_, enc->pic_, - enc->palette_sorted_, enc->palette_size_, - enc->palette_)) { - WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + if (enc->use_palette) { + if (!PaletteSort(crunch_configs[idx].palette_sorting_type, enc->pic, + enc->palette_sorted, enc->palette_size, + enc->palette)) { + WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY); goto Error; } percent_range = remaining_percent / 4; @@ -1558,45 +1599,45 @@ static int EncodeStreamHook(void* input, void* data2) { goto Error; } remaining_percent -= percent_range; - if (!MapImageFromPalette(enc, use_delta_palette)) goto Error; + if (!MapImageFromPalette(enc)) goto Error; // If using a color cache, do not have it bigger than the number of // colors. - if (enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) { - enc->cache_bits_ = BitsLog2Floor(enc->palette_size_) + 1; + if (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) { - if (!MakeInputImageCopy(enc)) goto Error; - } + // In case image is not packed. + if (enc->argb_content != kEncoderNearLossless && + enc->argb_content != kEncoderPalette) { + if (!MakeInputImageCopy(enc)) goto Error; + } - // ----------------------------------------------------------------------- - // Apply transforms and write transform data. + // ------------------------------------------------------------------------- + // Apply transforms and write transform data. - if (enc->use_subtract_green_) { - ApplySubtractGreen(enc, enc->current_width_, height, bw); - } + if (enc->use_subtract_green) { + ApplySubtractGreen(enc, enc->current_width, height, bw); + } - if (enc->use_predict_) { - percent_range = remaining_percent / 3; - if (!ApplyPredictFilter(enc, enc->current_width_, height, quality, - low_effort, enc->use_subtract_green_, bw, - percent_range, &percent)) { - goto Error; - } - remaining_percent -= percent_range; + if (enc->use_predict) { + percent_range = remaining_percent / 3; + if (!ApplyPredictFilter(enc, enc->current_width, height, quality, + low_effort, enc->use_subtract_green, bw, + percent_range, &percent, + &predictor_transform_bits)) { + goto Error; } + remaining_percent -= percent_range; + } - if (enc->use_cross_color_) { - percent_range = remaining_percent / 2; - if (!ApplyCrossColorFilter(enc, enc->current_width_, height, quality, - low_effort, bw, percent_range, &percent)) { - goto Error; - } - remaining_percent -= percent_range; + if (enc->use_cross_color) { + percent_range = remaining_percent / 2; + if (!ApplyCrossColorFilter(enc, enc->current_width, height, quality, + low_effort, bw, percent_range, &percent, + &cross_color_transform_bits)) { + goto Error; } + remaining_percent -= percent_range; } VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms. @@ -1604,9 +1645,9 @@ static int EncodeStreamHook(void* input, void* data2) { // ------------------------------------------------------------------------- // Encode and write the transformed image. if (!EncodeImageInternal( - bw, enc->argb_, &enc->hash_chain_, enc->refs_, enc->current_width_, + bw, enc->argb, &enc->hash_chain, enc->refs, enc->current_width, height, quality, low_effort, &crunch_configs[idx], - &enc->cache_bits_, enc->histo_bits_, byte_position, &hdr_size, + &enc->cache_bits, enc->histo_bits, byte_position, &hdr_size, &data_size, picture, remaining_percent, &percent)) { goto Error; } @@ -1620,14 +1661,15 @@ static int EncodeStreamHook(void* input, void* data2) { // 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_; + 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 = predictor_transform_bits; + stats->cross_color_transform_bits = cross_color_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; @@ -1642,7 +1684,7 @@ static int EncodeStreamHook(void* input, void* data2) { Error: VP8LBitWriterWipeOut(&bw_best); // The hook should return false in case of error. - return (params->picture_->error_code == VP8_ENC_OK); + return (params->picture->error_code == VP8_ENC_OK); } int VP8LEncodeStream(const WebPConfig* const config, @@ -1685,17 +1727,17 @@ int VP8LEncodeStream(const WebPConfig* const config, 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] = + 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; + 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.crunch_configs[idx] = crunch_configs[idx]; } - params_main.num_crunch_configs_ = num_crunch_configs_main; + params_main.num_crunch_configs = num_crunch_configs_main; // Fill in the parameters for the thread workers. { @@ -1705,13 +1747,13 @@ int VP8LEncodeStream(const WebPConfig* const config, WebPWorker* const worker = (idx == 0) ? &worker_main : &worker_side; StreamEncodeContext* const param = (idx == 0) ? ¶ms_main : ¶ms_side; - param->config_ = config; - param->red_and_blue_always_zero_ = red_and_blue_always_zero; + param->config = config; + param->red_and_blue_always_zero = red_and_blue_always_zero; if (idx == 0) { - param->picture_ = picture; - param->stats_ = picture->stats; - param->bw_ = bw_main; - param->enc_ = enc_main; + param->picture = picture; + param->stats = picture->stats; + param->bw = bw_main; + param->enc = enc_main; } else { // Create a side picture (error_code is not thread-safe). if (!WebPPictureView(picture, /*left=*/0, /*top=*/0, picture->width, @@ -1719,14 +1761,14 @@ int VP8LEncodeStream(const WebPConfig* const config, assert(0); } picture_side.progress_hook = NULL; // Progress hook is not thread-safe. - param->picture_ = &picture_side; // No need to free a view afterwards. - param->stats_ = (picture->stats == NULL) ? NULL : &stats_side; + param->picture = &picture_side; // No need to free a view afterwards. + param->stats = (picture->stats == NULL) ? NULL : &stats_side; // Create a side bit writer. if (!VP8LBitWriterClone(bw_main, &bw_side)) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); goto Error; } - param->bw_ = &bw_side; + param->bw = &bw_side; // Create a side encoder. enc_side = VP8LEncoderNew(config, &picture_side); if (enc_side == NULL || !EncoderInit(enc_side)) { @@ -1734,14 +1776,17 @@ int VP8LEncodeStream(const WebPConfig* const config, 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; + enc_side->histo_bits = enc_main->histo_bits; + enc_side->predictor_transform_bits = + enc_main->predictor_transform_bits; + enc_side->cross_color_transform_bits = + enc_main->cross_color_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); @@ -1883,7 +1928,7 @@ int VP8LEncodeImage(const WebPConfig* const config, } Error: - if (bw.error_) { + if (bw.error) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); } VP8LBitWriterWipeOut(&bw); diff --git a/3rdparty/libwebp/src/enc/vp8li_enc.h b/3rdparty/libwebp/src/enc/vp8li_enc.h index c5b60dcb39..9193fe0119 100644 --- a/3rdparty/libwebp/src/enc/vp8li_enc.h +++ b/3rdparty/libwebp/src/enc/vp8li_enc.h @@ -17,12 +17,16 @@ #ifdef HAVE_CONFIG_H #include "src/webp/config.h" #endif + +#include + // 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/webp/types.h" #include "src/enc/backward_references_enc.h" #include "src/enc/histogram_enc.h" #include "src/utils/bit_writer_utils.h" @@ -33,8 +37,8 @@ extern "C" { #endif -// maximum value of transform_bits_ in VP8LEncoder. -#define MAX_TRANSFORM_BITS 6 +// maximum value of 'transform_bits' in VP8LEncoder. +#define MAX_TRANSFORM_BITS (MIN_TRANSFORM_BITS + (1 << NUM_TRANSFORM_BITS) - 1) typedef enum { kEncoderNone = 0, @@ -44,38 +48,39 @@ typedef enum { } VP8LEncoderARGBContent; typedef struct { - const WebPConfig* config_; // user configuration and parameters - const WebPPicture* pic_; // input picture. + 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. + 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. + 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. + int histo_bits; + int predictor_transform_bits; // <= MAX_TRANSFORM_BITS + int cross_color_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]; + 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. + struct VP8LBackwardRefs refs[4]; // Backward Refs array for temporaries. + VP8LHashChain hash_chain; // HashChain data for constructing + // backward references. } VP8LEncoder; //------------------------------------------------------------------------------ @@ -104,16 +109,21 @@ int VP8ApplyNearLossless(const WebPPicture* const picture, int quality, // pic and percent are for progress. // Returns false in case of error (stored in pic->error_code). -int 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, const WebPPicture* const pic, - int percent_range, int* const percent); +int VP8LResidualImage(int width, int height, int min_bits, int max_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, + const WebPPicture* const pic, int percent_range, + int* const percent, int* const best_bits); int VP8LColorSpaceTransform(int width, int height, int bits, int quality, uint32_t* const argb, uint32_t* image, const WebPPicture* const pic, int percent_range, - int* const percent); + int* const percent, int* const best_bits); + +void VP8LOptimizeSampling(uint32_t* const image, int full_width, + int full_height, int bits, int max_bits, + int* best_bits_out); //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/enc/webp_enc.c b/3rdparty/libwebp/src/enc/webp_enc.c index 583fe6a8bb..1e7ee67c2c 100644 --- a/3rdparty/libwebp/src/enc/webp_enc.c +++ b/3rdparty/libwebp/src/enc/webp_enc.c @@ -12,14 +12,18 @@ // Author: Skal (pascal.massimino@gmail.com) #include +#include #include #include -#include +#include "src/dec/common_dec.h" +#include "src/webp/types.h" +#include "src/dsp/dsp.h" #include "src/enc/cost_enc.h" #include "src/enc/vp8i_enc.h" #include "src/enc/vp8li_enc.h" #include "src/utils/utils.h" +#include "src/webp/encode.h" // #define PRINT_MEMORY_INFO @@ -38,36 +42,36 @@ int WebPGetEncoderVersion(void) { //------------------------------------------------------------------------------ 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; + 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; + 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. + // 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) { + 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; + for (i = 0; i < 4 * enc->mb_h; ++i) { + left[i * enc->preds_w] = B_DC_PRED; } - enc->nz_[-1] = 0; // constant + enc->nz[-1] = 0; // constant } -// Mapping from config->method_ to coding tools used. +// Mapping from config->method to coding tools used. //-------------------+---+---+---+---+---+---+---+ // Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 | //-------------------+---+---+---+---+---+---+---+ @@ -93,31 +97,31 @@ static void ResetBoundaryPredictions(VP8Encoder* const enc) { //-------------------+---+---+---+---+---+---+---+ static void MapConfigToTools(VP8Encoder* const enc) { - const WebPConfig* const config = enc->config_; + 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_ = + 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->mb_header_limit = + (score_t)256 * 510 * 8 * 1024 / (enc->mb_w * enc->mb_h); - enc->thread_level_ = config->thread_level; + enc->thread_level = config->thread_level; - enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0); + 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 + 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 + if (enc->use_tokens) { + enc->num_parts = 1; // doesn't work with multi-partition } } } @@ -150,18 +154,18 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, 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 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 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 + 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; + 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; + 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 @@ -206,32 +210,32 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, 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; + 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_; + enc->preds = mem + 1 + enc->preds_w; mem += preds_size; - enc->nz_ = 1 + (uint32_t*)WEBP_ALIGN(mem); + enc->nz = 1 + (uint32_t*)WEBP_ALIGN(mem); mem += nz_size; - enc->lf_stats_ = lf_stats_size ? (LFStats*)WEBP_ALIGN(mem) : NULL; + 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; + 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; + 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; + enc->config = config; + enc->profile = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2; + enc->pic = picture; + enc->percent = 0; MapConfigToTools(enc); VP8EncDspInit(); @@ -246,7 +250,7 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, // 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)); + VP8TBufferInit(&enc->tokens, (int)(mb_w * mb_h * 4 * scale)); } return enc; } @@ -255,7 +259,7 @@ static int DeleteVP8Encoder(VP8Encoder* enc) { int ok = 1; if (enc != NULL) { ok = VP8EncDeleteAlpha(enc); - VP8TBufferClear(&enc->tokens_); + VP8TBufferClear(&enc->tokens); WebPSafeFree(enc); } return ok; @@ -269,9 +273,9 @@ static double GetPSNR(uint64_t err, uint64_t size) { } 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_; + 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); @@ -282,24 +286,24 @@ static void FinalizePSNR(const VP8Encoder* const enc) { static void StoreStats(VP8Encoder* const enc) { #if !defined(WEBP_DISABLE_STATS) - WebPAuxStats* const stats = enc->pic_->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_; + 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]; + stats->residual_bytes[s][i] = enc->residual_bytes[s][i]; } } FinalizePSNR(enc); - stats->coded_size = enc->coded_size_; + stats->coded_size = enc->coded_size; for (i = 0; i < 3; ++i) { - stats->block_count[i] = enc->block_count_[i]; + stats->block_count[i] = enc->block_count[i]; } } #else // defined(WEBP_DISABLE_STATS) - WebPReportProgress(enc->pic_, 100, &enc->percent_); // done! + WebPReportProgress(enc->pic, 100, &enc->percent); // done! #endif // !defined(WEBP_DISABLE_STATS) } @@ -380,7 +384,7 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) { // Analysis is done, proceed to actual coding. ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel - if (!enc->use_tokens_) { + if (!enc->use_tokens) { ok = ok && VP8EncLoop(enc); } else { ok = ok && VP8EncTokenLoop(enc); diff --git a/3rdparty/libwebp/src/mux/anim_encode.c b/3rdparty/libwebp/src/mux/anim_encode.c index 31bd0457bf..baf4f816d3 100644 --- a/3rdparty/libwebp/src/mux/anim_encode.c +++ b/3rdparty/libwebp/src/mux/anim_encode.c @@ -15,6 +15,7 @@ #include // for pow() #include #include // for abs() +#include #include "src/mux/animi.h" #include "src/utils/utils.h" @@ -22,6 +23,7 @@ #include "src/webp/encode.h" #include "src/webp/format_constants.h" #include "src/webp/mux.h" +#include "src/webp/mux_types.h" #include "src/webp/types.h" #if defined(_MSC_VER) && _MSC_VER < 1900 @@ -35,70 +37,70 @@ // Stores frame rectangle dimensions. typedef struct { - int x_offset_, y_offset_, width_, height_; + 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. + 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. + 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 + 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' + // only valid if 'options.allow_mixed' // is true. - WebPPicture* curr_canvas_; // Only pointer; we don't own memory. + 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 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. + 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 + 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. + 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 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 + 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 + 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. + 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. + WebPMux* mux; // Muxer to assemble the WebP bitstream. + char error_str[ERROR_STR_MAX_LENGTH]; // Error string. Empty if no error. }; // ----------------------------------------------------------------------------- @@ -109,11 +111,11 @@ struct WebPAnimEncoder { // 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; + 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) { @@ -191,7 +193,8 @@ int WebPAnimEncoderOptionsInitInternal(WebPAnimEncoderOptions* enc_options, return 1; } -// This starting value is more fit to WebPCleanupTransparentAreaLossless(). +// This value is used to match a later call to WebPReplaceTransparentPixels(), +// making it a no-op for lossless (see WebPEncode()). #define TRANSPARENT_COLOR 0x00000000 static void ClearRectangle(WebPPicture* const picture, @@ -209,26 +212,26 @@ static void ClearRectangle(WebPPicture* const picture, 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_); + 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. + 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) { + 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, + if (snprintf(enc->error_str, ERROR_STR_MAX_LENGTH, "%s: %d.", str, error_code) < 0) { assert(0); // FIX ME! } @@ -252,52 +255,52 @@ WebPAnimEncoder* WebPAnimEncoderNewInternal( MarkNoError(enc); // Dimensions and options. - *(int*)&enc->canvas_width_ = width; - *(int*)&enc->canvas_height_ = height; + *(int*)&enc->canvas_width = width; + *(int*)&enc->canvas_height = height; if (enc_options != NULL) { - *(WebPAnimEncoderOptions*)&enc->options_ = *enc_options; - SanitizeEncoderOptions((WebPAnimEncoderOptions*)&enc->options_); + *(WebPAnimEncoderOptions*)&enc->options = *enc_options; + SanitizeEncoderOptions((WebPAnimEncoderOptions*)&enc->options); } else { - DefaultEncoderOptions((WebPAnimEncoderOptions*)&enc->options_); + DefaultEncoderOptions((WebPAnimEncoderOptions*)&enc->options); } // Canvas buffers. - if (!WebPPictureInit(&enc->curr_canvas_copy_) || - !WebPPictureInit(&enc->prev_canvas_) || - !WebPPictureInit(&enc->prev_canvas_disposed_)) { + 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_)) { + 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; + 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; + 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->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->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; + 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. @@ -309,25 +312,25 @@ WebPAnimEncoder* WebPAnimEncoderNewInternal( // 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); + 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) { + 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]); + for (i = 0; i < enc->size; ++i) { + FrameRelease(&enc->encoded_frames[i]); } - WebPSafeFree(enc->encoded_frames_); + WebPSafeFree(enc->encoded_frames); } - WebPMuxDelete(enc->mux_); + WebPMuxDelete(enc->mux); WebPSafeFree(enc); } } @@ -338,8 +341,8 @@ void WebPAnimEncoderDelete(WebPAnimEncoder* enc) { // 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]; + assert(enc->start + position < enc->size); + return &enc->encoded_frames[enc->start + position]; } typedef int (*ComparePixelsFunc)(const uint32_t*, int, const uint32_t*, int, @@ -399,7 +402,7 @@ static WEBP_INLINE int ComparePixelsLossy(const uint32_t* src, int src_step, } static int IsEmptyRect(const FrameRectangle* const rect) { - return (rect->width_ == 0) || (rect->height_ == 0); + return (rect->width == 0) || (rect->height == 0); } static int QualityToMaxDiff(float quality) { @@ -421,113 +424,113 @@ static void MinimizeChangeRectangle(const WebPPicture* const src, // 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); + 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) { + 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]; + &src->argb[rect->y_offset * src->argb_stride + i]; const uint32_t* const dst_argb = - &dst->argb[rect->y_offset_ * dst->argb_stride + i]; + &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_; + rect->height, max_allowed_diff)) { + --rect->width; // Redundant column. + ++rect->x_offset; } else { break; } } - if (rect->width_ == 0) goto NoChange; + if (rect->width == 0) goto NoChange; // Right boundary. - for (i = rect->x_offset_ + rect->width_ - 1; i >= rect->x_offset_; --i) { + 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]; + &src->argb[rect->y_offset * src->argb_stride + i]; const uint32_t* const dst_argb = - &dst->argb[rect->y_offset_ * dst->argb_stride + i]; + &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->height, max_allowed_diff)) { + --rect->width; // Redundant column. } else { break; } } - if (rect->width_ == 0) goto NoChange; + if (rect->width == 0) goto NoChange; // Top boundary. - for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) { + 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_]; + &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_, + &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_; + --rect->height; // Redundant row. + ++rect->y_offset; } else { break; } } - if (rect->height_ == 0) goto NoChange; + if (rect->height == 0) goto NoChange; // Bottom boundary. - for (j = rect->y_offset_ + rect->height_ - 1; j >= rect->y_offset_; --j) { + 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_]; + &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_, + &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->height; // Redundant row. } else { break; } } - if (rect->height_ == 0) goto NoChange; + if (rect->height == 0) goto NoChange; if (IsEmptyRect(rect)) { NoChange: - rect->x_offset_ = 0; - rect->y_offset_ = 0; - rect->width_ = 0; - rect->height_ = 0; + 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; + 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 + 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. + 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_)) { + 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_); + WebPPictureFree(¶ms->sub_frame_ll); + WebPPictureFree(¶ms->sub_frame_lossy); } // Given previous and current canvas, picks the optimal rectangle for the @@ -550,16 +553,16 @@ static int GetSubRect(const WebPPicture* const prev_canvas, 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); + 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); + 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 @@ -569,20 +572,20 @@ static int GetSubRects(const WebPPicture* const prev_canvas, 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; + 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_)) { + 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. + 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_); + 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) { @@ -605,17 +608,17 @@ int WebPAnimEncoderRefineRect( left = clip(*x_offset, 0, curr_canvas->width - 1); bottom = clip(*y_offset + *height, 0, curr_canvas->height); top = clip(*y_offset, 0, curr_canvas->height - 1); - 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_); + 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_; + *x_offset = rect.x_offset; + *y_offset = rect.y_offset; + *width = rect.width; + *height = rect.height; return 1; } @@ -629,7 +632,7 @@ static void DisposeFrameRectangle(int dispose_method, } static uint32_t RectArea(const FrameRectangle* const rect) { - return (uint32_t)rect->width_ * rect->height_; + return (uint32_t)rect->width * rect->height; } static int IsLosslessBlendingPossible(const WebPPicture* const src, @@ -637,10 +640,10 @@ static int IsLosslessBlendingPossible(const WebPPicture* const src, 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) { + 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; @@ -661,10 +664,10 @@ static int IsLossyBlendingPossible(const WebPPicture* const src, 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) { + 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; @@ -689,10 +692,10 @@ static int IncreaseTransparency(const WebPPicture* const src, 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) { + 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) { + 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; @@ -715,10 +718,10 @@ static int FlattenSimilarBlocks(const WebPPicture* const src, 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); + 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 @@ -776,10 +779,10 @@ static int EncodeFrame(const WebPConfig* const config, WebPPicture* const pic, // 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. + 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. @@ -794,17 +797,17 @@ static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame, 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 = + 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(). + candidate->info.duration = 0; // Set in next call to WebPAnimEncoderAdd(). // Encode picture. - WebPMemoryWriterInit(&candidate->mem_); + WebPMemoryWriterInit(&candidate->mem); if (!config.lossless && use_blending) { // Disable filtering to avoid blockiness in reconstructed frames at the @@ -812,25 +815,25 @@ static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame, config.autofilter = 0; config.filter_strength = 0; } - if (!EncodeFrame(&config, sub_frame, &candidate->mem_)) { + if (!EncodeFrame(&config, sub_frame, &candidate->mem)) { error_code = sub_frame->error_code; goto Err; } - candidate->evaluate_ = 1; + candidate->evaluate = 1; return error_code; Err: - WebPMemoryWriterClear(&candidate->mem_); + 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; + 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; } } @@ -859,30 +862,30 @@ static WebPEncodingError GenerateCandidates( Candidate* const candidate_lossy = is_dispose_none ? &candidates[LOSSY_DISP_NONE] : &candidates[LOSSY_DISP_BG]; - WebPPicture* const curr_canvas = &enc->curr_canvas_copy_; + WebPPicture* const curr_canvas = &enc->curr_canvas_copy; const WebPPicture* const prev_canvas = - is_dispose_none ? &enc->prev_canvas_ : &enc->prev_canvas_disposed_; + 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_); + IsLosslessBlendingPossible(prev_canvas, curr_canvas, ¶ms->rect_ll); use_blending_lossy = !is_key_frame && - IsLossyBlendingPossible(prev_canvas, curr_canvas, ¶ms->rect_lossy_, + IsLossyBlendingPossible(prev_canvas, curr_canvas, ¶ms->rect_lossy, config_lossy->quality); // Pick candidates to be tried. - if (!enc->options_.allow_mixed) { + if (!enc->options.allow_mixed) { evaluate_ll = is_lossless; evaluate_lossy = !is_lossless; - } else if (enc->options_.minimize_size) { + } 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); + const int num_colors = WebPGetColorPalette(¶ms->sub_frame_ll, NULL); evaluate_ll = (num_colors < MAX_COLORS_LOSSLESS); evaluate_lossy = (num_colors >= MIN_COLORS_LOSSY); } @@ -891,25 +894,25 @@ static WebPEncodingError GenerateCandidates( if (evaluate_ll) { CopyCurrentCanvas(enc); if (use_blending_ll) { - enc->curr_canvas_copy_modified_ = - IncreaseTransparency(prev_canvas, ¶ms->rect_ll_, curr_canvas); + enc->curr_canvas_copy_modified = + IncreaseTransparency(prev_canvas, ¶ms->rect_ll, curr_canvas); } - error_code = EncodeCandidate(¶ms->sub_frame_ll_, ¶ms->rect_ll_, + 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, + 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_, + 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; + enc->curr_canvas_copy_modified = 1; } return error_code; } @@ -926,36 +929,36 @@ static void GetEncodedData(const WebPMemoryWriter* const memory, // 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; + 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. + assert(enc->count >= 2); // As current and previous frames are in enc. - if (enc->prev_candidate_undecided_) { + 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; + 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_; + 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; + 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(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; + 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. @@ -978,28 +981,28 @@ static int IncreasePreviousDuration(WebPAnimEncoder* const enc, int duration) { }; 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; + (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)) { + &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; + ++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; + prev_enc_frame->sub_frame.duration = new_duration; + prev_enc_frame->key_frame.duration = new_duration; } return 1; } @@ -1015,8 +1018,8 @@ static void PickBestCandidate(WebPAnimEncoder* const enc, 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 (candidates[i].evaluate) { + const size_t candidate_size = candidates[i].mem.size; if (candidate_size < best_size) { best_idx = i; best_size = candidate_size; @@ -1025,13 +1028,13 @@ static void PickBestCandidate(WebPAnimEncoder* const enc, } assert(best_idx != -1); for (i = 0; i < CANDIDATE_COUNT; ++i) { - if (candidates[i].evaluate_) { + 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); + ? &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 @@ -1042,10 +1045,10 @@ static void PickBestCandidate(WebPAnimEncoder* const enc, : WEBP_MUX_DISPOSE_BACKGROUND; SetPreviousDisposeMethod(enc, prev_dispose_method); } - enc->prev_rect_ = candidates[i].rect_; // save for next frame. + enc->prev_rect = candidates[i].rect; // save for next frame. } else { - WebPMemoryWriterClear(&candidates[i].mem_); - candidates[i].evaluate_ = 0; + WebPMemoryWriterClear(&candidates[i].mem); + candidates[i].evaluate = 0; } } } @@ -1062,13 +1065,13 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, 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_; + 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_; + 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. @@ -1087,7 +1090,7 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, // 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_; + !is_key_frame && !enc->prev_candidate_undecided; SubFrameParams dispose_none_params; SubFrameParams dispose_bg_params; @@ -1096,8 +1099,8 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, 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; + 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) || @@ -1114,8 +1117,8 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, goto Err; } - if ((consider_lossless && IsEmptyRect(&dispose_none_params.rect_ll_)) || - (consider_lossy && IsEmptyRect(&dispose_none_params.rect_lossy_))) { + 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); @@ -1125,9 +1128,9 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, if (dispose_bg_possible) { // Change-rectangle assuming previous frame was DISPOSE_BACKGROUND. - WebPPicture* const prev_canvas_disposed = &enc->prev_canvas_disposed_; + WebPPicture* const prev_canvas_disposed = &enc->prev_canvas_disposed; WebPCopyPixels(prev_canvas, prev_canvas_disposed); - DisposeFrameRectangle(WEBP_MUX_DISPOSE_BACKGROUND, &enc->prev_rect_, + DisposeFrameRectangle(WEBP_MUX_DISPOSE_BACKGROUND, &enc->prev_rect, prev_canvas_disposed); if (!GetSubRects(prev_canvas_disposed, curr_canvas, is_key_frame, @@ -1136,32 +1139,32 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Err; } - assert(!IsEmptyRect(&dispose_bg_params.rect_ll_)); - assert(!IsEmptyRect(&dispose_bg_params.rect_lossy_)); + 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; + 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_)) || + 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; + 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_) { + 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_); + 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, @@ -1175,8 +1178,8 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, Err: for (i = 0; i < CANDIDATE_COUNT; ++i) { - if (candidates[i].evaluate_) { - WebPMemoryWriterClear(&candidates[i].mem_); + if (candidates[i].evaluate) { + WebPMemoryWriterClear(&candidates[i].mem); } } @@ -1189,8 +1192,8 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, // 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); + return ((int64_t)encoded_frame->key_frame.bitstream.size - + encoded_frame->sub_frame.bitstream.size); } static int CacheFrame(WebPAnimEncoder* const enc, @@ -1198,30 +1201,30 @@ static int CacheFrame(WebPAnimEncoder* const enc, int ok = 0; int frame_skipped = 0; WebPEncodingError error_code = VP8_ENC_OK; - const size_t position = enc->count_; + const size_t position = enc->count; EncodedFrame* const encoded_frame = GetFrame(enc, position); - ++enc->count_; + ++enc->count; - if (enc->is_first_frame_) { // Add this as a key-frame. + 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; + 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) { + ++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; + 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; @@ -1230,103 +1233,103 @@ static int CacheFrame(WebPAnimEncoder* const 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_; + 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_; + 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; + 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. + 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; + 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->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; + 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; + WebPCopyPixels(enc->curr_canvas, &enc->prev_canvas); + enc->is_first_frame = 0; Skip: ok = 1; - ++enc->in_frame_count_; + ++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_; + --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 + 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) { + 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); + 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) { + 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_; + ++enc->out_frame_count; FrameRelease(curr); - ++enc->start_; - --enc->flush_count_; - --enc->count_; - if (enc->keyframe_ != KEYFRAME_NONE) --enc->keyframe_; + ++enc->start; + --enc->flush_count; + --enc->count; + if (enc->keyframe != KEYFRAME_NONE) --enc->keyframe; } - if (enc->count_ == 1 && enc->start_ != 0) { + 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; + 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; } @@ -1344,10 +1347,10 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp, } MarkNoError(enc); - if (!enc->is_first_frame_) { + 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_; + (uint32_t)timestamp - enc->prev_timestamp; if (prev_frame_duration >= MAX_DURATION) { if (frame != NULL) { frame->error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION; @@ -1359,30 +1362,30 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp, return 0; } // IncreasePreviousDuration() may add a frame to avoid exceeding - // MAX_DURATION which could cause CacheFrame() to over read encoded_frames_ + // MAX_DURATION which could cause CacheFrame() to over read 'encoded_frames' // before the next flush. - if (enc->count_ == enc->size_ && !FlushFrames(enc)) { + if (enc->count == enc->size && !FlushFrames(enc)) { return 0; } } else { - enc->first_timestamp_ = timestamp; + enc->first_timestamp = timestamp; } if (frame == NULL) { // Special: last call. - enc->got_null_frame_ = 1; - enc->prev_timestamp_ = timestamp; + enc->got_null_frame = 1; + enc->prev_timestamp = timestamp; return 1; } - if (frame->width != enc->canvas_width_ || - frame->height != enc->canvas_height_) { + 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) { + if (enc->options.verbose) { fprintf(stderr, "WARNING: Converting frame from YUV(A) to ARGB format; " "this incurs a small loss.\n"); } @@ -1405,17 +1408,17 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp, } config.lossless = 1; } - assert(enc->curr_canvas_ == NULL); - enc->curr_canvas_ = frame; // Store reference. - assert(enc->curr_canvas_copy_modified_ == 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; + enc->curr_canvas = NULL; + enc->curr_canvas_copy_modified = 1; if (ok) { - enc->prev_timestamp_ = timestamp; + enc->prev_timestamp = timestamp; } return ok; } @@ -1455,17 +1458,17 @@ WEBP_NODISCARD static int DecodeFrameOntoCanvas( static int FrameToFullCanvas(WebPAnimEncoder* const enc, const WebPMuxFrameInfo* const frame, WebPData* const full_image) { - WebPPicture* const canvas_buf = &enc->curr_canvas_copy_; + 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; + 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 (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); @@ -1493,7 +1496,7 @@ static WebPMuxError OptimizeSingleFrame(WebPAnimEncoder* const enc, WebPData webp_data2; WebPMux* const mux = WebPMuxCreate(webp_data, 0); if (mux == NULL) return WEBP_MUX_BAD_DATA; - assert(enc->out_frame_count_ == 1); + assert(enc->out_frame_count == 1); WebPDataInit(&frame.bitstream); WebPDataInit(&full_image); WebPDataInit(&webp_data2); @@ -1540,40 +1543,40 @@ int WebPAnimEncoderAssemble(WebPAnimEncoder* enc, WebPData* webp_data) { return 0; } - if (enc->in_frame_count_ == 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) { + 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)); + (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_; + 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_); + 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); + 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) { + if (enc->out_frame_count == 1) { err = OptimizeSingleFrame(enc, webp_data); if (err != WEBP_MUX_OK) goto Err; } @@ -1586,26 +1589,26 @@ int WebPAnimEncoderAssemble(WebPAnimEncoder* enc, WebPData* webp_data) { const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc) { if (enc == NULL) return NULL; - return enc->error_str_; + return enc->error_str; } WebPMuxError WebPAnimEncoderSetChunk( WebPAnimEncoder* enc, const char fourcc[4], const WebPData* chunk_data, int copy_data) { if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT; - return WebPMuxSetChunk(enc->mux_, fourcc, chunk_data, copy_data); + return WebPMuxSetChunk(enc->mux, fourcc, chunk_data, copy_data); } WebPMuxError WebPAnimEncoderGetChunk( const WebPAnimEncoder* enc, const char fourcc[4], WebPData* chunk_data) { if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT; - return WebPMuxGetChunk(enc->mux_, fourcc, chunk_data); + return WebPMuxGetChunk(enc->mux, fourcc, chunk_data); } WebPMuxError WebPAnimEncoderDeleteChunk( WebPAnimEncoder* enc, const char fourcc[4]) { if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT; - return WebPMuxDeleteChunk(enc->mux_, fourcc); + return WebPMuxDeleteChunk(enc->mux, fourcc); } // ----------------------------------------------------------------------------- diff --git a/3rdparty/libwebp/src/mux/muxedit.c b/3rdparty/libwebp/src/mux/muxedit.c index 48c6834a4d..09a8acab90 100644 --- a/3rdparty/libwebp/src/mux/muxedit.c +++ b/3rdparty/libwebp/src/mux/muxedit.c @@ -13,8 +13,16 @@ // Vikas (vikasa@google.com) #include +#include +#include + +#include "src/dec/vp8_dec.h" #include "src/mux/muxi.h" #include "src/utils/utils.h" +#include "src/webp/format_constants.h" +#include "src/webp/mux.h" +#include "src/webp/mux_types.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Life of a mux object. @@ -22,8 +30,8 @@ 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; + mux->canvas_width = 0; // just to be explicit + mux->canvas_height = 0; } WebPMux* WebPNewInternal(int version) { @@ -45,13 +53,13 @@ static void DeleteAllImages(WebPMuxImage** const 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_); + 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) { @@ -86,12 +94,12 @@ static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, 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_); + 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 @@ -141,11 +149,11 @@ static WebPMuxError GetImageData(const WebPData* const bitstream, 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_; + wpi = mux->images; + assert(wpi != NULL && wpi->img != NULL); + *image = wpi->img->data; + if (wpi->alpha != NULL) { + *alpha = wpi->alpha->data; } WebPMuxDelete(mux); } @@ -158,11 +166,11 @@ static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) { assert(chunk_list); while (*chunk_list) { WebPChunk* const chunk = *chunk_list; - if (chunk->tag_ == tag) { + if (chunk->tag == tag) { *chunk_list = ChunkDelete(chunk); err = WEBP_MUX_OK; } else { - chunk_list = &chunk->next_; + chunk_list = &chunk->next; } } return err; @@ -213,8 +221,8 @@ static WebPMuxError AddDataToChunkList( return err; } -// Extracts image & alpha data from the given bitstream and then sets wpi.alpha_ -// and wpi.img_ appropriately. +// 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; @@ -225,10 +233,10 @@ static WebPMuxError SetAlphaAndImageChunks( if (err != WEBP_MUX_OK) return err; if (alpha.bytes != NULL) { err = AddDataToChunkList(&alpha, copy_data, kChunks[IDX_ALPHA].tag, - &wpi->alpha_); + &wpi->alpha); if (err != WEBP_MUX_OK) return err; } - err = AddDataToChunkList(&image, copy_data, image_tag, &wpi->img_); + 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; } @@ -243,9 +251,9 @@ WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream, return WEBP_MUX_INVALID_ARGUMENT; } - if (mux->images_ != NULL) { + if (mux->images != NULL) { // Only one 'simple image' can be added in mux. So, remove present images. - DeleteAllImages(&mux->images_); + DeleteAllImages(&mux->images); } MuxImageInit(&wpi); @@ -253,7 +261,7 @@ WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream, if (err != WEBP_MUX_OK) goto Err; // Add this WebPMuxImage to mux. - err = MuxImagePush(&wpi, &mux->images_); + err = MuxImagePush(&wpi, &mux->images); if (err != WEBP_MUX_OK) goto Err; // All is well. @@ -278,10 +286,10 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info, 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 (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. } @@ -290,7 +298,7 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info, MuxImageInit(&wpi); err = SetAlphaAndImageChunks(&info->bitstream, copy_data, &wpi); if (err != WEBP_MUX_OK) goto Err; - assert(wpi.img_ != NULL); // As SetAlphaAndImageChunks() was successful. + assert(wpi.img != NULL); // As SetAlphaAndImageChunks() was successful. { WebPData frame; @@ -305,16 +313,16 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info, err = WEBP_MUX_INVALID_ARGUMENT; goto Err; } - err = CreateFrameData(wpi.width_, wpi.height_, &tmp, &frame); + 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. + 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_); + err = MuxImagePush(&wpi, &mux->images); if (err != WEBP_MUX_OK) goto Err; // All is well. @@ -367,8 +375,8 @@ WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux, 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; + mux->canvas_width = width; + mux->canvas_height = height; return WEBP_MUX_OK; } @@ -382,7 +390,7 @@ WebPMuxError WebPMuxDeleteChunk(WebPMux* mux, const char fourcc[4]) { WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) { if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - return MuxImageDeleteNth(&mux->images_, nth); + return MuxImageDeleteNth(&mux->images, nth); } //------------------------------------------------------------------------------ @@ -391,9 +399,9 @@ WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) { 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 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->tag == kChunks[IDX_ANMF].tag); assert(frame_chunk != NULL); if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT; @@ -407,7 +415,7 @@ 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_; + const WebPChunk* const frame_chunk = wpi->header; WebPMuxError err; assert(wpi != NULL); assert(frame_chunk != NULL); @@ -417,8 +425,8 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi, 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_; + if (width != NULL) *width = wpi->width; + if (height != NULL) *height = wpi->height; return WEBP_MUX_OK; } @@ -429,16 +437,16 @@ static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux, assert(mux != NULL); assert(width != NULL && height != NULL); - wpi = mux->images_; + wpi = mux->images; assert(wpi != NULL); - assert(wpi->img_ != NULL); + assert(wpi->img != NULL); - if (wpi->next_ != 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); + // 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_) { + 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); @@ -455,8 +463,8 @@ static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux, *height = max_y; } else { // For a single image, canvas dimensions are same as image dimensions. - *width = wpi->width_; - *height = wpi->height_; + *width = wpi->width; + *height = wpi->height; } return WEBP_MUX_OK; } @@ -476,9 +484,9 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { const WebPMuxImage* images = NULL; assert(mux != NULL); - images = mux->images_; // First image. - if (images == NULL || images->img_ == NULL || - images->img_->data_.bytes == NULL) { + images = mux->images; // First image. + if (images == NULL || images->img == NULL || + images->img->data.bytes == NULL) { return WEBP_MUX_INVALID_ARGUMENT; } @@ -488,17 +496,17 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Set flags. - if (mux->iccp_ != NULL && mux->iccp_->data_.bytes != NULL) { + if (mux->iccp != NULL && mux->iccp->data.bytes != NULL) { flags |= ICCP_FLAG; } - if (mux->exif_ != NULL && mux->exif_->data_.bytes != NULL) { + if (mux->exif != NULL && mux->exif->data.bytes != NULL) { flags |= EXIF_FLAG; } - if (mux->xmp_ != NULL && mux->xmp_->data_.bytes != NULL) { + if (mux->xmp != NULL && mux->xmp->data.bytes != NULL) { flags |= XMP_FLAG; } - if (images->header_ != NULL) { - if (images->header_->tag_ == kChunks[IDX_ANMF].tag) { + if (images->header != NULL) { + if (images->header->tag == kChunks[IDX_ANMF].tag) { // This is an image with animation. flags |= ANIMATION_FLAG; } @@ -517,15 +525,15 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { return WEBP_MUX_INVALID_ARGUMENT; } - if (mux->canvas_width_ != 0 || mux->canvas_height_ != 0) { - if (width > mux->canvas_width_ || height > mux->canvas_height_) { + 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_; + width = mux->canvas_width; + height = mux->canvas_height; } - if (flags == 0 && mux->unknown_ == NULL) { + if (flags == 0 && mux->unknown == NULL) { // For simple file format, VP8X chunk should not be added. return WEBP_MUX_OK; } @@ -556,17 +564,17 @@ static WebPMuxError MuxCleanup(WebPMux* const mux) { if (err != WEBP_MUX_OK) return err; if (num_frames == 1) { WebPMuxImage* frame = NULL; - err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame); + err = MuxImageGetNth((const WebPMuxImage**)&mux->images, 1, &frame); if (err != WEBP_MUX_OK) return err; // 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; + 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; } } @@ -585,7 +593,7 @@ 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_; + wpi_list = wpi_list->next; } return size; } @@ -594,7 +602,7 @@ static size_t ImageListDiskSize(const WebPMuxImage* wpi_list) { 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_; + wpi_list = wpi_list->next; } return dst; } @@ -622,23 +630,23 @@ WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) { 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; + 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); + 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. diff --git a/3rdparty/libwebp/src/mux/muxi.h b/3rdparty/libwebp/src/mux/muxi.h index 74ae3fac12..f277758a35 100644 --- a/3rdparty/libwebp/src/mux/muxi.h +++ b/3rdparty/libwebp/src/mux/muxi.h @@ -16,9 +16,13 @@ #include #include + #include "src/dec/vp8i_dec.h" #include "src/dec/vp8li_dec.h" +#include "src/webp/format_constants.h" #include "src/webp/mux.h" +#include "src/webp/mux_types.h" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { @@ -28,47 +32,47 @@ extern "C" { // Defines and constants. #define MUX_MAJ_VERSION 1 -#define MUX_MIN_VERSION 4 +#define MUX_MIN_VERSION 6 #define MUX_REV_VERSION 0 // Chunk object. typedef struct WebPChunk WebPChunk; struct WebPChunk { - uint32_t tag_; - int owner_; // True if *data_ memory is owned internally. + 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_; + 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_; + 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_; + WebPMuxImage* images; + WebPChunk* iccp; + WebPChunk* exif; + WebPChunk* xmp; + WebPChunk* anim; + WebPChunk* vp8x; - WebPChunk* unknown_; - int canvas_width_; - int canvas_height_; + WebPChunk* unknown; + int canvas_width; + int canvas_height; }; // CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only. @@ -136,10 +140,10 @@ WebPMuxError ChunkSetHead(WebPChunk* const chunk, WebPChunk** const chunk_list); // *chunk_list. WebPMuxError ChunkAppend(WebPChunk* const chunk, WebPChunk*** const chunk_list); -// Releases chunk and returns chunk->next_. +// Releases chunk and returns chunk->next. WebPChunk* ChunkRelease(WebPChunk* const chunk); -// Deletes given chunk & returns chunk->next_. +// Deletes given chunk & returns chunk->next. WebPChunk* ChunkDelete(WebPChunk* const chunk); // Deletes all chunks in the given chunk list. @@ -153,7 +157,7 @@ static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) { // 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; + const size_t data_size = chunk->data.size; return SizeWithPadding(data_size); } diff --git a/3rdparty/libwebp/src/mux/muxinternal.c b/3rdparty/libwebp/src/mux/muxinternal.c index 75b6b416b9..4460e0641e 100644 --- a/3rdparty/libwebp/src/mux/muxinternal.c +++ b/3rdparty/libwebp/src/mux/muxinternal.c @@ -13,8 +13,15 @@ // Vikas (vikasa@google.com) #include +#include +#include + #include "src/mux/muxi.h" +#include "src/webp/types.h" #include "src/utils/utils.h" +#include "src/webp/format_constants.h" +#include "src/webp/mux.h" +#include "src/webp/mux_types.h" #define UNDEFINED_CHUNK_SIZE ((uint32_t)(-1)) @@ -45,16 +52,16 @@ int WebPGetMuxVersion(void) { void ChunkInit(WebPChunk* const chunk) { assert(chunk); memset(chunk, 0, sizeof(*chunk)); - chunk->tag_ = NIL_TAG; + chunk->tag = NIL_TAG; } WebPChunk* ChunkRelease(WebPChunk* const chunk) { WebPChunk* next; if (chunk == NULL) return NULL; - if (chunk->owner_) { - WebPDataClear(&chunk->data_); + if (chunk->owner) { + WebPDataClear(&chunk->data); } - next = chunk->next_; + next = chunk->next; ChunkInit(chunk); return next; } @@ -92,8 +99,8 @@ CHUNK_INDEX ChunkGetIndexFromFourCC(const char fourcc[4]) { // 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_; + while (chunk != NULL && chunk->tag != tag) { + chunk = chunk->next; } return chunk; } @@ -104,7 +111,7 @@ WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) { if (first == NULL) return NULL; while (--iter != 0) { - WebPChunk* next_chunk = ChunkSearchNextInList(first->next_, tag); + WebPChunk* next_chunk = ChunkSearchNextInList(first->next, tag); if (next_chunk == NULL) break; first = next_chunk; } @@ -125,13 +132,13 @@ WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data, 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. + 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->data = *data; } } - chunk->tag_ = tag; + chunk->tag = tag; return WEBP_MUX_OK; } @@ -147,8 +154,8 @@ WebPMuxError ChunkSetHead(WebPChunk* const chunk, 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->owner = 0; + new_chunk->next = NULL; *chunk_list = new_chunk; return WEBP_MUX_OK; } @@ -162,9 +169,9 @@ WebPMuxError ChunkAppend(WebPChunk* const chunk, err = ChunkSetHead(chunk, *chunk_list); } else { WebPChunk* last_chunk = **chunk_list; - while (last_chunk->next_ != NULL) last_chunk = last_chunk->next_; - err = ChunkSetHead(chunk, &last_chunk->next_); - if (err == WEBP_MUX_OK) *chunk_list = &last_chunk->next_; + while (last_chunk->next != NULL) last_chunk = last_chunk->next; + err = ChunkSetHead(chunk, &last_chunk->next); + if (err == WEBP_MUX_OK) *chunk_list = &last_chunk->next; } return err; } @@ -188,13 +195,13 @@ void ChunkListDelete(WebPChunk** const chunk_list) { // Chunk serialization methods. static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) { - const size_t chunk_size = chunk->data_.size; + const size_t chunk_size = chunk->data.size; assert(chunk); - assert(chunk->tag_ != NIL_TAG); - PutLE32(dst + 0, chunk->tag_); + 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); + 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); @@ -203,7 +210,7 @@ static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) { uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) { while (chunk_list != NULL) { dst = ChunkEmit(chunk_list, dst); - chunk_list = chunk_list->next_; + chunk_list = chunk_list->next; } return dst; } @@ -212,7 +219,7 @@ size_t ChunkListDiskSize(const WebPChunk* chunk_list) { size_t size = 0; while (chunk_list != NULL) { size += ChunkDiskSize(chunk_list); - chunk_list = chunk_list->next_; + chunk_list = chunk_list->next; } return size; } @@ -228,14 +235,14 @@ void MuxImageInit(WebPMuxImage* const 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 + // 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_); + ChunkListDelete(&wpi->header); + ChunkListDelete(&wpi->alpha); + ChunkListDelete(&wpi->img); + ChunkListDelete(&wpi->unknown); - next = wpi->next_; + next = wpi->next; MuxImageInit(wpi); return next; } @@ -248,9 +255,9 @@ 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_; + 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; } } @@ -258,13 +265,13 @@ static WebPChunk** GetChunkListFromId(const WebPMuxImage* const wpi, int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) { int count = 0; const WebPMuxImage* current; - for (current = wpi_list; current != NULL; current = current->next_) { + 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_); + const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag); if (wpi_chunk_id == id) ++count; // Count images with a matching 'id'. } } @@ -272,7 +279,7 @@ int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) { return count; } -// Outputs a pointer to 'prev_wpi->next_', +// 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, @@ -290,7 +297,7 @@ static int SearchImageToGetOrDelete(WebPMuxImage** wpi_list, uint32_t nth, WebPMuxImage* const cur_wpi = *wpi_list; ++count; if (count == nth) return 1; // Found. - wpi_list = &cur_wpi->next_; + wpi_list = &cur_wpi->next; *location = wpi_list; } return 0; // Not found. @@ -304,17 +311,17 @@ WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list) { while (*wpi_list != NULL) { WebPMuxImage* const cur_wpi = *wpi_list; - if (cur_wpi->next_ == NULL) break; - wpi_list = &cur_wpi->next_; + 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; + new_wpi->next = NULL; if (*wpi_list != NULL) { - (*wpi_list)->next_ = new_wpi; + (*wpi_list)->next = new_wpi; } else { *wpi_list = new_wpi; } @@ -361,23 +368,23 @@ WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth, // 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_); + 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 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_); + 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); + memcpy(dst + CHUNK_HEADER_SIZE, header->data.bytes, header_size); if (header_size & 1) { dst[CHUNK_HEADER_SIZE + header_size] = 0; // Add padding. } @@ -390,12 +397,12 @@ uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) { // 2. ALPH chunk (if present). // 3. VP8/VP8L chunk. assert(wpi); - if (wpi->header_ != NULL) { - dst = ChunkEmitSpecial(wpi->header_, MuxImageDiskSize(wpi), dst); + 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); + 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; } @@ -404,8 +411,8 @@ uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) { int MuxHasAlpha(const WebPMuxImage* images) { while (images != NULL) { - if (images->has_alpha_) return 1; - images = images->next_; + if (images->has_alpha) return 1; + images = images->next; } return 0; } @@ -421,12 +428,12 @@ uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t 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_; + 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; } } @@ -470,7 +477,7 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; // Verify mux has at least one image. - if (mux->images_ == NULL) return WEBP_MUX_INVALID_ARGUMENT; + if (mux->images == NULL) return WEBP_MUX_INVALID_ARGUMENT; err = WebPMuxGetFeatures(mux, &flags); if (err != WEBP_MUX_OK) return err; @@ -503,15 +510,15 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { return WEBP_MUX_INVALID_ARGUMENT; } if (!has_animation) { - const WebPMuxImage* images = mux->images_; + const WebPMuxImage* images = mux->images; // There can be only one image. - if (images == NULL || images->next_ != NULL) { + 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_) { + if (mux->canvas_width > 0) { + if (images->width != mux->canvas_width || + images->height != mux->canvas_height) { return WEBP_MUX_INVALID_ARGUMENT; } } @@ -519,7 +526,7 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { } // Verify either VP8X chunk is present OR there is only one elem in - // mux->images_. + // 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); @@ -528,7 +535,7 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { // 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 (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; @@ -546,4 +553,3 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { #undef NO_FLAG //------------------------------------------------------------------------------ - diff --git a/3rdparty/libwebp/src/mux/muxread.c b/3rdparty/libwebp/src/mux/muxread.c index afd3542e12..b778070f70 100644 --- a/3rdparty/libwebp/src/mux/muxread.c +++ b/3rdparty/libwebp/src/mux/muxread.c @@ -13,8 +13,15 @@ // Vikas (vikasa@google.com) #include +#include + +#include "src/dec/vp8_dec.h" #include "src/mux/muxi.h" #include "src/utils/utils.h" +#include "src/webp/format_constants.h" +#include "src/webp/mux.h" +#include "src/webp/mux_types.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // Helper method(s). @@ -26,7 +33,7 @@ const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \ kChunks[(INDEX)].tag); \ if (chunk) { \ - *data = chunk->data_; \ + *data = chunk->data; \ return WEBP_MUX_OK; \ } else { \ return WEBP_MUX_NOT_FOUND; \ @@ -41,11 +48,11 @@ static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx, 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_); + 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; } @@ -77,9 +84,9 @@ static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk, } 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); + 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 ? @@ -88,29 +95,29 @@ int MuxImageFinalize(WebPMuxImage* const wpi) { assert(img != NULL); if (ok) { // Ignore ALPH chunk accompanying VP8L. - if (is_lossless && (wpi->alpha_ != NULL)) { - ChunkDelete(wpi->alpha_); - wpi->alpha_ = NULL; + 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); + 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* 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_; + WebPChunk** unknown_chunk_list = &wpi->unknown; ChunkInit(&subchunk); - assert(chunk->tag_ == kChunks[IDX_ANMF].tag); - assert(!wpi->is_partial_); + assert(chunk->tag == kChunks[IDX_ANMF].tag); + assert(!wpi->is_partial); // ANMF. { @@ -120,12 +127,12 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data, // be at least 'hdr_size'. if (size < hdr_size) goto Fail; if (ChunkAssignData(&subchunk, &temp, copy_data, - chunk->tag_) != WEBP_MUX_OK) { + chunk->tag) != WEBP_MUX_OK) { goto Fail; } } - if (ChunkSetHead(&subchunk, &wpi->header_) != WEBP_MUX_OK) goto Fail; - wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks. + if (ChunkSetHead(&subchunk, &wpi->header) != WEBP_MUX_OK) goto Fail; + wpi->is_partial = 1; // Waiting for ALPH and/or VP8/VP8L chunks. // Rest of the chunks. subchunk_size = ChunkDiskSize(&subchunk) - CHUNK_HEADER_SIZE; @@ -138,20 +145,20 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data, copy_data) != WEBP_MUX_OK) { goto Fail; } - switch (ChunkGetIdFromTag(subchunk.tag_)) { + 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. + 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 (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. + wpi->is_partial = 0; // wpi is completely filled. break; case WEBP_CHUNK_UNKNOWN: - if (wpi->is_partial_) { + if (wpi->is_partial) { goto Fail; // Encountered an unknown chunk // before some image chunks. } @@ -166,7 +173,7 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data, bytes += subchunk_size; size -= subchunk_size; } - if (wpi->is_partial_) goto Fail; + if (wpi->is_partial) goto Fail; return 1; Fail: @@ -223,11 +230,13 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, // 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; + // Make sure the whole RIFF header is available. + if (riff_size < RIFF_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; + // There's no point in reading past the end of the RIFF chunk. Note riff_size + // includes CHUNK_HEADER_SIZE after SizeWithPadding(). + if (size > riff_size) { + size = riff_size; } end = data + size; @@ -247,29 +256,29 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, goto Err; } data_size = ChunkDiskSize(&chunk); - id = ChunkGetIdFromTag(chunk.tag_); + 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. + 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 (ChunkSetHead(&chunk, &wpi->img) != WEBP_MUX_OK) goto Err; if (!MuxImageFinalize(wpi)) goto Err; - wpi->is_partial_ = 0; // wpi is completely filled. + 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; + // 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 (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 + 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] = @@ -278,8 +287,8 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, 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; + mux->canvas_width = GetLE24(data + 12) + 1; + mux->canvas_height = GetLE24(data + 15) + 1; } break; } @@ -289,7 +298,7 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, } // Incomplete image. - if (wpi->is_partial_) goto Err; + if (wpi->is_partial) goto Err; // Validate mux if complete. if (MuxValidate(mux) != WEBP_MUX_OK) goto Err; @@ -309,8 +318,8 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, // 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); + 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. @@ -340,18 +349,18 @@ static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux, w = GetLE24(data.bytes + 4) + 1; h = GetLE24(data.bytes + 7) + 1; } else { - const WebPMuxImage* const wpi = mux->images_; + const WebPMuxImage* const wpi = mux->images; // Grab user-forced canvas size as default. - w = mux->canvas_width_; - h = mux->canvas_height_; + 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_; + w = wpi->width; + h = wpi->height; } if (wpi != NULL) { - if (wpi->has_alpha_) f |= ALPHA_FLAG; + if (wpi->has_alpha) f |= ALPHA_FLAG; } } if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA; @@ -394,29 +403,29 @@ static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi, uint8_t* dst; // Allocate data. - const int need_vp8x = (wpi->alpha_ != NULL); + 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; + 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_); + 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); + // 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. + dst = EmitVP8XChunk(dst, wpi->width, wpi->height, ALPHA_FLAG); // VP8X. + dst = ChunkListEmit(wpi->alpha, dst); // ALPH. } // Bitstream. - dst = ChunkListEmit(wpi->img_, dst); + dst = ChunkListEmit(wpi->img, dst); assert(dst == data + size); // Output. @@ -439,9 +448,9 @@ WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4], return MuxGet(mux, idx, 1, chunk_data); } else { // An unknown chunk type. const WebPChunk* const chunk = - ChunkSearchList(mux->unknown_, 1, ChunkGetTagFromFourCC(fourcc)); + ChunkSearchList(mux->unknown, 1, ChunkGetTagFromFourCC(fourcc)); if (chunk == NULL) return WEBP_MUX_NOT_FOUND; - *chunk_data = chunk->data_; + *chunk_data = chunk->data; return WEBP_MUX_OK; } } @@ -455,18 +464,18 @@ static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi, info->dispose_method = WEBP_MUX_DISPOSE_NONE; info->blend_method = WEBP_MUX_BLEND; // Extract data for related fields. - info->id = ChunkGetIdFromTag(wpi->img_->tag_); + 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 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(). + assert(wpi->header != NULL); // Already checked by WebPMuxGetFrame(). // Get frame chunk. - frame_data = &wpi->header_->data_; + 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); @@ -478,7 +487,7 @@ static WebPMuxError MuxGetFrameInternal(const WebPMuxImage* const wpi, (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_); + frame->id = ChunkGetIdFromTag(wpi->header->tag); return SynthesizeBitstream(wpi, &frame->bitstream); } @@ -492,11 +501,11 @@ WebPMuxError WebPMuxGetFrame( } // Get the nth WebPMuxImage. - err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, &wpi); + err = MuxImageGetNth((const WebPMuxImage**)&mux->images, nth, &wpi); if (err != WEBP_MUX_OK) return err; // Get frame info. - if (wpi->header_ == NULL) { + if (wpi->header == NULL) { return MuxGetImageInternal(wpi, frame); } else { return MuxGetFrameInternal(wpi, frame); @@ -533,8 +542,8 @@ static CHUNK_INDEX ChunkGetIndexFromId(WebPChunkId id) { 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) { + for (current = chunk_list; current != NULL; current = current->next) { + if (tag == NIL_TAG || current->tag == tag) { count++; // Count chunks whose tags match. } } @@ -548,7 +557,7 @@ WebPMuxError WebPMuxNumChunks(const WebPMux* mux, } if (IsWPI(id)) { - *num_elements = MuxImageCount(mux->images_, id); + *num_elements = MuxImageCount(mux->images, id); } else { WebPChunk* const* chunk_list = MuxGetChunkListFromId(mux, id); const CHUNK_INDEX idx = ChunkGetIndexFromId(id); diff --git a/3rdparty/libwebp/src/utils/bit_reader_inl_utils.h b/3rdparty/libwebp/src/utils/bit_reader_inl_utils.h index 24f3af7b54..8179c2fa5d 100644 --- a/3rdparty/libwebp/src/utils/bit_reader_inl_utils.h +++ b/3rdparty/libwebp/src/utils/bit_reader_inl_utils.h @@ -20,12 +20,15 @@ #include "src/webp/config.h" #endif +#include #include // for memcpy +#include "src/dsp/cpu.h" #include "src/dsp/dsp.h" #include "src/utils/bit_reader_utils.h" #include "src/utils/endian_inl_utils.h" #include "src/utils/utils.h" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { @@ -53,33 +56,33 @@ void VP8LoadFinalBytes(VP8BitReader* const br); //------------------------------------------------------------------------------ // Inlined critical functions -// makes sure br->value_ has at least BITS bits worth of data +// 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); + assert(br != NULL && br->buf != NULL); // Read 'BITS' bits at a time if possible. - if (br->buf_ < br->buf_max_) { + 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_; + 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" + "ulw %[in_bits], 0(%[p_buf]) \n\t" ".set pop \n\t" : [in_bits]"=r"(in_bits) - : [p_buf_]"r"(p_buf_) + : [p_buf]"r"(p_buf) : "memory", "at" ); #else lbit_t in_bits; - memcpy(&in_bits, br->buf_, sizeof(in_bits)); + memcpy(&in_bits, br->buf, sizeof(in_bits)); #endif - br->buf_ += BITS >> 3; + br->buf += BITS >> 3; #if !defined(WORDS_BIGENDIAN) #if (BITS > 32) bits = BSwap64(in_bits); @@ -96,8 +99,8 @@ void VP8LoadNewBytes(VP8BitReader* WEBP_RESTRICT const br) { 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; + br->value = bits | (br->value << BITS); + br->bits += BITS; } else { VP8LoadFinalBytes(br); // no need to be inlined } @@ -108,28 +111,28 @@ 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) { + // alter br->range value. + range_t range = br->range; + if (br->bits < 0) { VP8LoadNewBytes(br); } { - const int pos = br->bits_; + const int pos = br->bits; const range_t split = (range * prob) >> 8; - const range_t value = (range_t)(br->value_ >> pos); + 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; + br->value -= (bit_t)(split + 1) << pos; } else { range = split + 1; } { const int shift = 7 ^ BitsLog2Floor(range); range <<= shift; - br->bits_ -= shift; + br->bits -= shift; } - br->range_ = range - 1; + br->range = range - 1; BT_TRACK(br); return bit; } @@ -139,18 +142,18 @@ static WEBP_INLINE int VP8GetBit(VP8BitReader* WEBP_RESTRICT const br, static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE int VP8GetSigned(VP8BitReader* WEBP_RESTRICT const br, int v, const char label[]) { - if (br->bits_ < 0) { + 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 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_ += (range_t)mask; - br->range_ |= 1; - br->value_ -= (bit_t)((split + 1) & (uint32_t)mask) << pos; + br->bits -= 1; + br->range += (range_t)mask; + br->range |= 1; + br->value -= (bit_t)((split + 1) & (uint32_t)mask) << pos; BT_TRACK(br); return (v ^ mask) - mask; } @@ -160,19 +163,19 @@ 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) { + // alter br->range value. + range_t range = br->range; + if (br->bits < 0) { VP8LoadNewBytes(br); } { - const int pos = br->bits_; + const int pos = br->bits; const range_t split = (range * prob) >> 8; - const range_t value = (range_t)(br->value_ >> pos); + 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; + br->value -= (bit_t)(split + 1) << pos; bit = 1; } else { range = split; @@ -181,9 +184,9 @@ static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* WEBP_RESTRICT const br, if (range <= (range_t)0x7e) { const int shift = kVP8Log2Range[range]; range = kVP8NewRange[range]; - br->bits_ -= shift; + br->bits -= shift; } - br->range_ = range; + br->range = range; BT_TRACK(br); return bit; } diff --git a/3rdparty/libwebp/src/utils/bit_reader_utils.c b/3rdparty/libwebp/src/utils/bit_reader_utils.c index a26557aa49..5e3a8b37ef 100644 --- a/3rdparty/libwebp/src/utils/bit_reader_utils.c +++ b/3rdparty/libwebp/src/utils/bit_reader_utils.c @@ -15,8 +15,14 @@ #include "src/webp/config.h" #endif +#include +#include + +#include "src/webp/types.h" #include "src/dsp/cpu.h" #include "src/utils/bit_reader_inl_utils.h" +#include "src/utils/bit_reader_utils.h" +#include "src/utils/endian_inl_utils.h" #include "src/utils/utils.h" //------------------------------------------------------------------------------ @@ -25,11 +31,11 @@ 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; + assert(start != NULL); + 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, @@ -37,19 +43,19 @@ void VP8InitBitReader(VP8BitReader* const br, 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; + 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; + if (br->buf != NULL) { + br->buf += offset; + br->buf_end += offset; + br->buf_max += offset; } } @@ -86,17 +92,17 @@ const uint8_t kVP8NewRange[128] = { }; void VP8LoadFinalBytes(VP8BitReader* const br) { - assert(br != NULL && br->buf_ != NULL); + 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; + 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. + br->bits = 0; // This is to avoid undefined behaviour with shifts. } } @@ -124,7 +130,8 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int bits, #if defined(__arm__) || defined(_M_ARM) || WEBP_AARCH64 || \ defined(__i386__) || defined(_M_IX86) || \ - defined(__x86_64__) || defined(_M_X64) + defined(__x86_64__) || defined(_M_X64) || \ + defined(__wasm__) #define VP8L_USE_FAST_LOAD #endif @@ -146,20 +153,20 @@ void VP8LInitBitReader(VP8LBitReader* const br, const uint8_t* const start, 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; + br->len = length; + br->val = 0; + br->bit_pos = 0; + br->eos = 0; - if (length > sizeof(br->val_)) { - length = sizeof(br->val_); + 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; + br->val = value; + br->pos = length; + br->buf = start; } void VP8LBitReaderSetBuffer(VP8LBitReader* const br, @@ -167,24 +174,24 @@ void VP8LBitReaderSetBuffer(VP8LBitReader* const br, 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); + 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. + 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; + 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); @@ -192,14 +199,14 @@ static void ShiftBytes(VP8LBitReader* const br) { } void VP8LDoFillBitWindow(VP8LBitReader* const br) { - assert(br->bit_pos_ >= VP8L_WBITS); + 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; + 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 @@ -209,10 +216,10 @@ void VP8LDoFillBitWindow(VP8LBitReader* const br) { 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) { + 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; + const int new_bits = br->bit_pos + n_bits; + br->bit_pos = new_bits; ShiftBytes(br); return val; } else { @@ -272,17 +279,17 @@ void BitTrace(const struct VP8BitReader* const br, const char label[]) { if (!init_done) { memset(kLabels, 0, sizeof(kLabels)); atexit(PrintBitTraces); - buf_start = br->buf_; + buf_start = br->buf; init_done = 1; } - pos = (int)(br->buf_ - buf_start) * 8 - br->bits_; + 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_; + buf_start = br->buf; pos = 0; last_pos = 0; } - if (br->range_ >= 0x7f) pos += kVP8Log2Range[br->range_ - 0x7f]; + if (br->range >= 0x7f) pos += kVP8Log2Range[br->range - 0x7f]; for (i = 0; i < last_label; ++i) { if (!strcmp(label, kLabels[i].label)) break; } diff --git a/3rdparty/libwebp/src/utils/bit_reader_utils.h b/3rdparty/libwebp/src/utils/bit_reader_utils.h index 25ff31e5d9..ddce860645 100644 --- a/3rdparty/libwebp/src/utils/bit_reader_utils.h +++ b/3rdparty/libwebp/src/utils/bit_reader_utils.h @@ -16,6 +16,8 @@ #define WEBP_UTILS_BIT_READER_UTILS_H_ #include +#include + #ifdef _MSC_VER #include // _byteswap_ulong #endif @@ -47,14 +49,14 @@ extern void BitTrace(const struct VP8BitReader* const br, const char label[]); 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. +// 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. @@ -69,14 +71,16 @@ extern "C" { #define BITS 56 #elif defined(__mips__) // MIPS #define BITS 24 +#elif defined(__wasm__) // WASM +#define BITS 56 #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) +// 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; @@ -92,14 +96,14 @@ typedef uint32_t range_t; 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 + 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 + 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. @@ -139,12 +143,12 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits, 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 + 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, @@ -158,34 +162,34 @@ void VP8LBitReaderSetBuffer(VP8LBitReader* const br, // 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. +// 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))); + 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. +// 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)); + 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. +// 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; + 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); + if (br->bit_pos >= VP8L_WBITS) VP8LDoFillBitWindow(br); } #ifdef __cplusplus diff --git a/3rdparty/libwebp/src/utils/bit_writer_utils.c b/3rdparty/libwebp/src/utils/bit_writer_utils.c index 2f408508f1..12cee6e8d0 100644 --- a/3rdparty/libwebp/src/utils/bit_writer_utils.c +++ b/3rdparty/libwebp/src/utils/bit_writer_utils.c @@ -13,10 +13,11 @@ // Vikas Arora (vikaas.arora@gmail.com) #include -#include // for memcpy() #include +#include // for memcpy() #include "src/utils/bit_writer_utils.h" +#include "src/webp/types.h" #include "src/utils/endian_inl_utils.h" #include "src/utils/utils.h" @@ -26,54 +27,54 @@ 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 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; + bw->error = 1; return 0; } - if (needed_size <= bw->max_pos_) return 1; + 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_; + 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; + bw->error = 1; return 0; } - if (bw->pos_ > 0) { - assert(bw->buf_ != NULL); - memcpy(new_buf, bw->buf_, bw->pos_); + 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; + 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; + 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)) { + 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 (pos > 0) bw->buf[pos - 1]++; } - if (bw->run_ > 0) { + if (bw->run > 0) { const int value = (bits & 0x100) ? 0x00 : 0xff; - for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value; + for (; bw->run > 0; --bw->run) bw->buf[pos++] = value; } - bw->buf_[pos++] = bits & 0xff; - bw->pos_ = pos; + bw->buf[pos++] = bits & 0xff; + bw->pos = pos; } else { - bw->run_++; // delay writing of bytes 0xff, pending eventual carry. + bw->run++; // delay writing of bytes 0xff, pending eventual carry. } } @@ -106,36 +107,36 @@ static const uint8_t kNewRange[128] = { }; int VP8PutBit(VP8BitWriter* const bw, int bit, int prob) { - const int split = (bw->range_ * prob) >> 8; + const int split = (bw->range * prob) >> 8; if (bit) { - bw->value_ += split + 1; - bw->range_ -= split + 1; + bw->value += split + 1; + bw->range -= split + 1; } else { - bw->range_ = split; + 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); + 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; + const int split = bw->range >> 1; if (bit) { - bw->value_ += split + 1; - bw->range_ -= split + 1; + bw->value += split + 1; + bw->range -= split + 1; } else { - bw->range_ = split; + 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); + 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; } @@ -160,37 +161,37 @@ void VP8PutSignedBits(VP8BitWriter* const bw, int value, int nb_bits) { //------------------------------------------------------------------------------ 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; + 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 + VP8PutBits(bw, 0, 9 - bw->nb_bits); + bw->nb_bits = 0; // pad with zeroes Flush(bw); - return bw->buf_; + 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 (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; + memcpy(bw->buf + bw->pos, data, size); + bw->pos += size; return 1; } void VP8BitWriterWipeOut(VP8BitWriter* const bw) { if (bw != NULL) { - WebPSafeFree(bw->buf_); + WebPSafeFree(bw->buf); memset(bw, 0, sizeof(*bw)); } } @@ -206,12 +207,12 @@ void VP8BitWriterWipeOut(VP8BitWriter* const bw) { 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 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; + bw->error = 1; return 0; } if (max_bytes > 0 && size_required <= max_bytes) return 1; @@ -221,16 +222,16 @@ static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) { allocated_size = (((allocated_size >> 10) + 1) << 10); allocated_buf = (uint8_t*)WebPSafeMalloc(1ULL, allocated_size); if (allocated_buf == NULL) { - bw->error_ = 1; + bw->error = 1; return 0; } if (current_size > 0) { - memcpy(allocated_buf, bw->buf_, current_size); + 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; + WebPSafeFree(bw->buf); + bw->buf = allocated_buf; + bw->cur = bw->buf + current_size; + bw->end = bw->buf + allocated_size; return 1; } @@ -241,31 +242,31 @@ int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t 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_); + 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; + 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_); + 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_; + 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) { @@ -276,19 +277,19 @@ void VP8LBitWriterSwap(VP8LBitWriter* const src, VP8LBitWriter* const dst) { 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 (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; + 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; + *(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) { @@ -296,8 +297,8 @@ void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits) { // 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_; + 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) { @@ -312,36 +313,36 @@ void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_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 (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; + 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; + *(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; + 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; + 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; + bw->used = 0; } - return bw->buf_; + return bw->buf; } //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/utils/bit_writer_utils.h b/3rdparty/libwebp/src/utils/bit_writer_utils.h index b9d5102a5a..48a7bdbfce 100644 --- a/3rdparty/libwebp/src/utils/bit_writer_utils.h +++ b/3rdparty/libwebp/src/utils/bit_writer_utils.h @@ -14,6 +14,8 @@ #ifndef WEBP_UTILS_BIT_WRITER_UTILS_H_ #define WEBP_UTILS_BIT_WRITER_UTILS_H_ +#include + #include "src/webp/types.h" #ifdef __cplusplus @@ -25,14 +27,14 @@ extern "C" { 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 + 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. @@ -54,17 +56,17 @@ int VP8BitWriterAppend(VP8BitWriter* const bw, // 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; + 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_; + return bw->buf; } // Returns the size of the internal buffer. static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) { - return bw->pos_; + return bw->pos; } //------------------------------------------------------------------------------ @@ -87,21 +89,21 @@ typedef uint16_t vp8l_wtype_t; #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 + 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 + // 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_; + int error; } VP8LBitWriter; static WEBP_INLINE size_t VP8LBitWriterNumBytes(const VP8LBitWriter* const bw) { - return (bw->cur_ - bw->buf_) + ((bw->used_ + 7) >> 3); + return (bw->cur - bw->buf) + ((bw->used + 7) >> 3); } // Returns false in case of memory allocation error. @@ -129,16 +131,16 @@ void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits); // 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. +// 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) { + if (bw->used >= 32) { VP8LPutBitsFlushBits(bw); } - bw->bits_ |= (vp8l_atype_t)bits << bw->used_; - bw->used_ += n_bits; + bw->bits |= (vp8l_atype_t)bits << bw->used; + bw->used += n_bits; } } else { VP8LPutBitsInternal(bw, bits, n_bits); diff --git a/3rdparty/libwebp/src/utils/color_cache_utils.c b/3rdparty/libwebp/src/utils/color_cache_utils.c index 7b5222b6e5..cd8be1f73a 100644 --- a/3rdparty/libwebp/src/utils/color_cache_utils.c +++ b/3rdparty/libwebp/src/utils/color_cache_utils.c @@ -14,7 +14,9 @@ #include #include #include + #include "src/utils/color_cache_utils.h" +#include "src/webp/types.h" #include "src/utils/utils.h" //------------------------------------------------------------------------------ @@ -24,18 +26,18 @@ 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; + 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; + WebPSafeFree(color_cache->colors); + color_cache->colors = NULL; } } @@ -43,7 +45,7 @@ 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_)); + assert(src->hash_bits == dst->hash_bits); + memcpy(dst->colors, src->colors, + ((size_t)1u << dst->hash_bits) * sizeof(*dst->colors)); } diff --git a/3rdparty/libwebp/src/utils/color_cache_utils.h b/3rdparty/libwebp/src/utils/color_cache_utils.h index b45d47c2d5..ac8d981548 100644 --- a/3rdparty/libwebp/src/utils/color_cache_utils.h +++ b/3rdparty/libwebp/src/utils/color_cache_utils.h @@ -17,6 +17,7 @@ #include +#include "src/dsp/cpu.h" #include "src/dsp/dsp.h" #include "src/webp/types.h" @@ -26,9 +27,9 @@ extern "C" { // Main color cache struct. typedef struct { - uint32_t* colors_; // color entries - int hash_shift_; // Hash shift: 32 - hash_bits_. - int hash_bits_; + uint32_t* colors; // color entries + int hash_shift; // Hash shift: 32 - 'hash_bits'. + int hash_bits; } VP8LColorCache; static const uint32_t kHashMul = 0x1e35a7bdu; @@ -40,32 +41,32 @@ int VP8LHashPix(uint32_t argb, int shift) { static WEBP_INLINE uint32_t VP8LColorCacheLookup( const VP8LColorCache* const cc, uint32_t key) { - assert((key >> cc->hash_bits_) == 0u); - return cc->colors_[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; + 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; + 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 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; + const int key = VP8LHashPix(argb, cc->hash_shift); + return (cc->colors[key] == argb) ? key : -1; } //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/utils/filters_utils.c b/3rdparty/libwebp/src/utils/filters_utils.c index bbc2c34d93..9286d3715a 100644 --- a/3rdparty/libwebp/src/utils/filters_utils.c +++ b/3rdparty/libwebp/src/utils/filters_utils.c @@ -11,10 +11,13 @@ // // Author: Urvang (urvang@google.com) -#include "src/utils/filters_utils.h" #include #include +#include "src/dsp/dsp.h" +#include "src/webp/types.h" +#include "src/utils/filters_utils.h" + // ----------------------------------------------------------------------------- // Quick estimate of a potentially interesting filter mode to try. diff --git a/3rdparty/libwebp/src/utils/filters_utils.h b/3rdparty/libwebp/src/utils/filters_utils.h index 61da66e212..8e9418df21 100644 --- a/3rdparty/libwebp/src/utils/filters_utils.h +++ b/3rdparty/libwebp/src/utils/filters_utils.h @@ -14,8 +14,8 @@ #ifndef WEBP_UTILS_FILTERS_UTILS_H_ #define WEBP_UTILS_FILTERS_UTILS_H_ -#include "src/webp/types.h" #include "src/dsp/dsp.h" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { diff --git a/3rdparty/libwebp/src/utils/huffman_encode_utils.c b/3rdparty/libwebp/src/utils/huffman_encode_utils.c index 585db91951..1710063f89 100644 --- a/3rdparty/libwebp/src/utils/huffman_encode_utils.c +++ b/3rdparty/libwebp/src/utils/huffman_encode_utils.c @@ -14,7 +14,9 @@ #include #include #include + #include "src/utils/huffman_encode_utils.h" +#include "src/webp/types.h" #include "src/utils/utils.h" #include "src/webp/format_constants.h" @@ -122,24 +124,24 @@ static void OptimizeHuffmanForRle(int length, uint8_t* const good_for_rle, 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_) { + if (t1->total_count > t2->total_count) { return -1; - } else if (t1->total_count_ < t2->total_count_) { + } else if (t1->total_count < t2->total_count) { return 1; } else { - assert(t1->value_ != t2->value_); - return (t1->value_ < t2->value_) ? -1 : 1; + 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); + 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; + bit_depths[tree->value] = level; } } @@ -198,10 +200,10 @@ static void GenerateOptimalTree(const uint32_t* const histogram, 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; + tree[idx].total_count = count; + tree[idx].value = j; + tree[idx].pool_index_left = -1; + tree[idx].pool_index_right = -1; ++idx; } } @@ -215,29 +217,29 @@ static void GenerateOptimalTree(const uint32_t* const histogram, 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_; + 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) { + 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].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[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; + bit_depths[tree[0].value] = 1; } { diff --git a/3rdparty/libwebp/src/utils/huffman_encode_utils.h b/3rdparty/libwebp/src/utils/huffman_encode_utils.h index 3f7f1d8074..4252e82347 100644 --- a/3rdparty/libwebp/src/utils/huffman_encode_utils.h +++ b/3rdparty/libwebp/src/utils/huffman_encode_utils.h @@ -35,10 +35,10 @@ typedef struct { // 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. + 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. diff --git a/3rdparty/libwebp/src/utils/huffman_utils.c b/3rdparty/libwebp/src/utils/huffman_utils.c index 16f9faaa9a..b3f93a0f05 100644 --- a/3rdparty/libwebp/src/utils/huffman_utils.c +++ b/3rdparty/libwebp/src/utils/huffman_utils.c @@ -14,9 +14,11 @@ #include #include #include + #include "src/utils/huffman_utils.h" #include "src/utils/utils.h" #include "src/webp/format_constants.h" +#include "src/webp/types.h" // Huffman data read via DecodeImageStream is represented in two (red and green) // bytes. diff --git a/3rdparty/libwebp/src/utils/huffman_utils.h b/3rdparty/libwebp/src/utils/huffman_utils.h index d511dc052c..5e19a7e2c2 100644 --- a/3rdparty/libwebp/src/utils/huffman_utils.h +++ b/3rdparty/libwebp/src/utils/huffman_utils.h @@ -15,6 +15,7 @@ #define WEBP_UTILS_HUFFMAN_UTILS_H_ #include + #include "src/webp/format_constants.h" #include "src/webp/types.h" diff --git a/3rdparty/libwebp/src/utils/palette.c b/3rdparty/libwebp/src/utils/palette.c index 515da21019..6251db19d1 100644 --- a/3rdparty/libwebp/src/utils/palette.c +++ b/3rdparty/libwebp/src/utils/palette.c @@ -15,12 +15,14 @@ #include #include +#include #include "src/dsp/lossless_common.h" #include "src/utils/color_cache_utils.h" #include "src/utils/utils.h" #include "src/webp/encode.h" #include "src/webp/format_constants.h" +#include "src/webp/types.h" // ----------------------------------------------------------------------------- @@ -191,6 +193,12 @@ static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted, // 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. + if (num_colors > 17) { + if (palette[0] == 0) { + --num_colors; + SwapColor(&palette[num_colors], &palette[0]); + } + } for (i = 0; i < num_colors; ++i) { int best_ix = i; uint32_t best_score = ~0U; @@ -384,8 +392,13 @@ int PaletteSort(PaletteSorting method, const struct WebPPicture* const pic, uint32_t* const palette) { switch (method) { case kSortedDefault: - // Nothing to do, we have already sorted the palette. - memcpy(palette, palette_sorted, num_colors * sizeof(*palette)); + if (palette_sorted[0] == 0 && num_colors > 17) { + memcpy(palette, palette_sorted + 1, + (num_colors - 1) * sizeof(*palette_sorted)); + palette[num_colors - 1] = 0; + } else { + memcpy(palette, palette_sorted, num_colors * sizeof(*palette)); + } return 1; case kMinimizeDelta: PaletteSortMinimizeDeltas(palette_sorted, num_colors, palette); diff --git a/3rdparty/libwebp/src/utils/palette.h b/3rdparty/libwebp/src/utils/palette.h index 34479e463f..417c61fa5e 100644 --- a/3rdparty/libwebp/src/utils/palette.h +++ b/3rdparty/libwebp/src/utils/palette.h @@ -53,6 +53,8 @@ int GetColorPalette(const struct WebPPicture* const pic, // Sorts the palette according to the criterion defined by 'method'. // 'palette_sorted' is the input palette sorted lexicographically, as done in // PrepareMapToPalette. Returns 0 on memory allocation error. +// For kSortedDefault and kMinimizeDelta methods, 0 (if present) is set as the +// last element to optimize later storage. int PaletteSort(PaletteSorting method, const struct WebPPicture* const pic, const uint32_t* const palette_sorted, uint32_t num_colors, uint32_t* const palette); diff --git a/3rdparty/libwebp/src/utils/quant_levels_dec_utils.c b/3rdparty/libwebp/src/utils/quant_levels_dec_utils.c index 97e7893704..b42aa1b377 100644 --- a/3rdparty/libwebp/src/utils/quant_levels_dec_utils.c +++ b/3rdparty/libwebp/src/utils/quant_levels_dec_utils.c @@ -19,6 +19,7 @@ #include // for memset #include "src/utils/utils.h" +#include "src/webp/types.h" // #define USE_DITHERING // uncomment to enable ordered dithering (not vital) @@ -43,30 +44,30 @@ static const uint8_t kOrderedDither[DSIZE][DSIZE] = { #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 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 + int radius; // filter radius (=delay) + int scale; // normalization factor, in FIX bits precision - void* mem_; // all memory + void* mem; // all memory // various scratch buffers - uint16_t* start_; - uint16_t* cur_; - uint16_t* end_; - uint16_t* top_; - uint16_t* average_; + 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 + 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 + int16_t* correction; // size = 1 + 2*LUT_SIZE -> ~4k memory } SmoothParams; //------------------------------------------------------------------------------ @@ -79,11 +80,11 @@ static WEBP_INLINE uint8_t clip_8b(int v) { // 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_; + 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; @@ -95,24 +96,24 @@ static void VFilter(SmoothParams* const p) { 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 + 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_; + 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_; + 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 @@ -132,17 +133,17 @@ static void HFilter(SmoothParams* const p) { // 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_; + 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]; + const uint8_t* const dither = kOrderedDither[p->row % DSIZE]; #endif - uint8_t* const dst = p->dst_; + 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_) { + 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]); @@ -151,7 +152,7 @@ static void ApplyFilter(SmoothParams* const p) { #endif } } - p->dst_ += p->stride_; // advance output pointer + p->dst += p->stride; // advance output pointer } //------------------------------------------------------------------------------ @@ -183,28 +184,28 @@ static void InitCorrectionLUT(int16_t* const lut, int min_dist) { 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 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; + if (v < p->min) p->min = v; + if (v > p->max) p->max = v; used_levels[v] = 1; } - data += p->stride_; + data += p->stride; } // Compute the mininum distance between two non-zero levels. - p->min_level_dist_ = p->max_ - p->min_; + p->min_level_dist = p->max - p->min; last_level = -1; for (i = 0; i < 256; ++i) { if (used_levels[i]) { - ++p->num_levels_; + ++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; + if (level_dist < p->min_level_dist) { + p->min_level_dist = level_dist; } } last_level = i; @@ -217,46 +218,46 @@ 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 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->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_)); + 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; + 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; + 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_); + 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_); + WebPSafeFree(p->mem); } int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, @@ -274,12 +275,12 @@ int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, 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_) { + 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_) { + if (p.row >= p.radius) { HFilter(&p); ApplyFilter(&p); } diff --git a/3rdparty/libwebp/src/utils/quant_levels_utils.c b/3rdparty/libwebp/src/utils/quant_levels_utils.c index d65ad3c29d..549417e024 100644 --- a/3rdparty/libwebp/src/utils/quant_levels_utils.c +++ b/3rdparty/libwebp/src/utils/quant_levels_utils.c @@ -13,7 +13,9 @@ // Author: Skal (pascal.massimino@gmail.com) #include +#include +#include "src/webp/types.h" #include "src/utils/quant_levels_utils.h" #define NUM_SYMBOLS 256 @@ -137,4 +139,3 @@ int QuantizeLevels(uint8_t* const data, int width, int height, return 1; } - diff --git a/3rdparty/libwebp/src/utils/random_utils.c b/3rdparty/libwebp/src/utils/random_utils.c index 7edb3fefbb..56380610fa 100644 --- a/3rdparty/libwebp/src/utils/random_utils.c +++ b/3rdparty/libwebp/src/utils/random_utils.c @@ -12,6 +12,8 @@ // Author: Skal (pascal.massimino@gmail.com) #include + +#include "src/webp/types.h" #include "src/utils/random_utils.h" //------------------------------------------------------------------------------ @@ -31,13 +33,12 @@ static const uint32_t kRandomTable[VP8_RANDOM_TABLE_SIZE] = { }; 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); + 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/3rdparty/libwebp/src/utils/random_utils.h b/3rdparty/libwebp/src/utils/random_utils.h index a5006f84f7..2fbb200257 100644 --- a/3rdparty/libwebp/src/utils/random_utils.h +++ b/3rdparty/libwebp/src/utils/random_utils.h @@ -15,6 +15,7 @@ #define WEBP_UTILS_RANDOM_UTILS_H_ #include + #include "src/webp/types.h" #ifdef __cplusplus @@ -25,9 +26,9 @@ extern "C" { #define VP8_RANDOM_TABLE_SIZE 55 typedef struct { - int index1_, index2_; - uint32_t tab_[VP8_RANDOM_TABLE_SIZE]; - int amp_; + int index1, index2; + uint32_t tab[VP8_RANDOM_TABLE_SIZE]; + int amp; } VP8Random; // Initializes random generator with an amplitude 'dithering' in range [0..1]. @@ -40,11 +41,11 @@ 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_]; + 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; + 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 @@ -53,7 +54,7 @@ static WEBP_INLINE int VP8RandomBits2(VP8Random* const rg, int num_bits, } static WEBP_INLINE int VP8RandomBits(VP8Random* const rg, int num_bits) { - return VP8RandomBits2(rg, num_bits, rg->amp_); + return VP8RandomBits2(rg, num_bits, rg->amp); } #ifdef __cplusplus diff --git a/3rdparty/libwebp/src/utils/rescaler_utils.c b/3rdparty/libwebp/src/utils/rescaler_utils.c index a0581a14b1..32dd0af2de 100644 --- a/3rdparty/libwebp/src/utils/rescaler_utils.c +++ b/3rdparty/libwebp/src/utils/rescaler_utils.c @@ -15,7 +15,9 @@ #include #include #include + #include "src/dsp/dsp.h" +#include "src/webp/types.h" #include "src/utils/rescaler_utils.h" #include "src/utils/utils.h" diff --git a/3rdparty/libwebp/src/utils/thread_utils.c b/3rdparty/libwebp/src/utils/thread_utils.c index 4e470e17ac..d61a0bb78b 100644 --- a/3rdparty/libwebp/src/utils/thread_utils.c +++ b/3rdparty/libwebp/src/utils/thread_utils.c @@ -13,6 +13,7 @@ #include #include // for memset() + #include "src/utils/thread_utils.h" #include "src/utils/utils.h" @@ -29,9 +30,9 @@ typedef CRITICAL_SECTION pthread_mutex_t; typedef CONDITION_VARIABLE pthread_cond_t; #else typedef struct { - HANDLE waiting_sem_; - HANDLE received_sem_; - HANDLE signal_event_; + HANDLE waiting_sem; + HANDLE received_sem; + HANDLE signal_event; } pthread_cond_t; #endif // _WIN32_WINNT >= 0x600 @@ -51,9 +52,9 @@ typedef struct { #endif // _WIN32 typedef struct { - pthread_mutex_t mutex_; - pthread_cond_t condition_; - pthread_t thread_; + pthread_mutex_t mutex; + pthread_cond_t condition; + pthread_t thread; } WebPWorkerImpl; #if defined(_WIN32) @@ -133,9 +134,9 @@ static int pthread_cond_destroy(pthread_cond_t* const condition) { #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); + ok &= (CloseHandle(condition->waiting_sem) != 0); + ok &= (CloseHandle(condition->received_sem) != 0); + ok &= (CloseHandle(condition->signal_event) != 0); #endif return !ok; } @@ -145,12 +146,12 @@ static int pthread_cond_init(pthread_cond_t* const condition, 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) { + 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; } @@ -163,12 +164,12 @@ static int pthread_cond_signal(pthread_cond_t* const condition) { #ifdef USE_WINDOWS_CONDITION_VARIABLE WakeConditionVariable(condition); #else - if (WaitForSingleObject(condition->waiting_sem_, 0) == WAIT_OBJECT_0) { + 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_); + 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) != + ok &= (WaitForSingleObject(condition->received_sem, INFINITE) != WAIT_OBJECT_0); } #endif @@ -183,12 +184,12 @@ static int pthread_cond_wait(pthread_cond_t* const condition, #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; + 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) == + ok = (WaitForSingleObject(condition->signal_event, INFINITE) == WAIT_OBJECT_0); - ok &= ReleaseSemaphore(condition->received_sem_, 1, NULL); + ok &= ReleaseSemaphore(condition->received_sem, 1, NULL); pthread_mutex_lock(mutex); #endif return !ok; @@ -203,17 +204,17 @@ static int pthread_cond_wait(pthread_cond_t* const condition, static THREADFN ThreadLoop(void* ptr) { WebPWorker* const worker = (WebPWorker*)ptr; - WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_; + 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_); + pthread_mutex_lock(&impl->mutex); + while (worker->status == OK) { // wait in idling mode + pthread_cond_wait(&impl->condition, &impl->mutex); } - if (worker->status_ == WORK) { + if (worker->status == WORK) { WebPGetWorkerInterface()->Execute(worker); - worker->status_ = OK; - } else if (worker->status_ == NOT_OK) { // finish the 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()) @@ -221,8 +222,8 @@ static THREADFN ThreadLoop(void* ptr) { // 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_); + pthread_mutex_unlock(&impl->mutex); + pthread_cond_signal(&impl->condition); } return THREAD_RETURN(NULL); // Thread is finished } @@ -230,30 +231,30 @@ static THREADFN ThreadLoop(void* ptr) { // 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 + // Checking 'status' without acquiring the lock first would result in a data // race. - WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_; + WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl; if (impl == NULL) return; - pthread_mutex_lock(&impl->mutex_); - if (worker->status_ >= OK) { + 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_); + 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; + 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_); + pthread_mutex_unlock(&impl->mutex); + pthread_cond_signal(&impl->condition); return; } } - pthread_mutex_unlock(&impl->mutex_); + pthread_mutex_unlock(&impl->mutex); } #endif // WEBP_USE_THREAD @@ -262,54 +263,54 @@ static void ChangeState(WebPWorker* const worker, WebPWorkerStatus new_status) { static void Init(WebPWorker* const worker) { memset(worker, 0, sizeof(*worker)); - worker->status_ = NOT_OK; + worker->status = NOT_OK; } static int Sync(WebPWorker* const worker) { #ifdef WEBP_USE_THREAD ChangeState(worker, OK); #endif - assert(worker->status_ <= OK); + 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) { + if (worker->status < OK) { #ifdef WEBP_USE_THREAD WebPWorkerImpl* const impl = (WebPWorkerImpl*)WebPSafeCalloc(1, sizeof(WebPWorkerImpl)); - worker->impl_ = (void*)impl; - if (worker->impl_ == NULL) { + worker->impl = (void*)impl; + if (worker->impl == NULL) { return 0; } - if (pthread_mutex_init(&impl->mutex_, NULL)) { + if (pthread_mutex_init(&impl->mutex, NULL)) { goto Error; } - if (pthread_cond_init(&impl->condition_, NULL)) { - pthread_mutex_destroy(&impl->mutex_); + 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_); + 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_); + pthread_mutex_destroy(&impl->mutex); + pthread_cond_destroy(&impl->condition); Error: WebPSafeFree(impl); - worker->impl_ = NULL; + worker->impl = NULL; return 0; } #else - worker->status_ = OK; + worker->status = OK; #endif - } else if (worker->status_ > OK) { + } else if (worker->status > OK) { ok = Sync(worker); } - assert(!ok || (worker->status_ == OK)); + assert(!ok || (worker->status == OK)); return ok; } @@ -329,20 +330,20 @@ static void Launch(WebPWorker* const worker) { static void End(WebPWorker* const worker) { #ifdef WEBP_USE_THREAD - if (worker->impl_ != NULL) { - WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_; + 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_); + pthread_join(impl->thread, NULL); + pthread_mutex_destroy(&impl->mutex); + pthread_cond_destroy(&impl->condition); WebPSafeFree(impl); - worker->impl_ = NULL; + worker->impl = NULL; } #else - worker->status_ = NOT_OK; - assert(worker->impl_ == NULL); + worker->status = NOT_OK; + assert(worker->impl == NULL); #endif - assert(worker->status_ == NOT_OK); + assert(worker->status == NOT_OK); } //------------------------------------------------------------------------------ diff --git a/3rdparty/libwebp/src/utils/thread_utils.h b/3rdparty/libwebp/src/utils/thread_utils.h index 29ad49f74b..3575815d98 100644 --- a/3rdparty/libwebp/src/utils/thread_utils.h +++ b/3rdparty/libwebp/src/utils/thread_utils.h @@ -37,8 +37,8 @@ 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_; + 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' diff --git a/3rdparty/libwebp/src/utils/utils.c b/3rdparty/libwebp/src/utils/utils.c index 408ce88f67..b80bae0125 100644 --- a/3rdparty/libwebp/src/utils/utils.c +++ b/3rdparty/libwebp/src/utils/utils.c @@ -13,9 +13,11 @@ #include "src/utils/utils.h" +#include #include #include // for memcpy() +#include "src/webp/types.h" #include "src/utils/palette.h" #include "src/webp/encode.h" @@ -60,9 +62,9 @@ static int countdown_to_fail = 0; // 0 = off typedef struct MemBlock MemBlock; struct MemBlock { - void* ptr_; - size_t size_; - MemBlock* next_; + void* ptr; + size_t size; + MemBlock* next; }; static MemBlock* all_blocks = NULL; @@ -83,7 +85,7 @@ static void PrintMemInfo(void) { fprintf(stderr, "high-water mark: %u\n", (uint32_t)high_water_mark); while (all_blocks != NULL) { MemBlock* b = all_blocks; - all_blocks = b->next_; + all_blocks = b->next; free(b); } } @@ -121,10 +123,10 @@ 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; + b->next = all_blocks; all_blocks = b; - b->ptr_ = ptr; - b->size_ = size; + b->ptr = ptr; + b->size = size; total_mem += size; total_mem_allocated += size; #if defined(PRINT_MEM_TRAFFIC) @@ -143,18 +145,18 @@ 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_; + 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_; + *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_); + (uint32_t)total_mem, (uint32_t)block->size); #endif free(block); } diff --git a/3rdparty/libwebp/src/webp/decode.h b/3rdparty/libwebp/src/webp/decode.h index d6895f5c55..1f0d0c22ea 100644 --- a/3rdparty/libwebp/src/webp/decode.h +++ b/3rdparty/libwebp/src/webp/decode.h @@ -14,13 +14,15 @@ #ifndef WEBP_WEBP_DECODE_H_ #define WEBP_WEBP_DECODE_H_ +#include + #include "./types.h" #ifdef __cplusplus extern "C" { #endif -#define WEBP_DECODER_ABI_VERSION 0x0209 // MAJOR(8b) + MINOR(8b) +#define WEBP_DECODER_ABI_VERSION 0x0210 // MAJOR(8b) + MINOR(8b) // Note: forward declaring enumerations is not allowed in (strict) C and C++, // the types are left here for reference. @@ -451,7 +453,9 @@ struct WebPDecoderOptions { // 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 scaled_width, scaled_height; // final resolution. if one is 0, it is + // guessed from the other one to keep the + // original ratio. 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 @@ -479,6 +483,11 @@ WEBP_NODISCARD static WEBP_INLINE int WebPInitDecoderConfig( return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION); } +// Returns true if 'config' is non-NULL and all configuration parameters are +// within their valid ranges. +WEBP_NODISCARD WEBP_EXTERN int WebPValidateDecoderConfig( + const WebPDecoderConfig* config); + // 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 diff --git a/3rdparty/libwebp/src/webp/demux.h b/3rdparty/libwebp/src/webp/demux.h index 8d246550ca..0f5bc2b81f 100644 --- a/3rdparty/libwebp/src/webp/demux.h +++ b/3rdparty/libwebp/src/webp/demux.h @@ -48,6 +48,8 @@ #ifndef WEBP_WEBP_DEMUX_H_ #define WEBP_WEBP_DEMUX_H_ +#include + #include "./decode.h" // for WEBP_CSP_MODE #include "./mux_types.h" #include "./types.h" diff --git a/3rdparty/libwebp/src/webp/encode.h b/3rdparty/libwebp/src/webp/encode.h index f3d59297c8..ed49393234 100644 --- a/3rdparty/libwebp/src/webp/encode.h +++ b/3rdparty/libwebp/src/webp/encode.h @@ -14,13 +14,15 @@ #ifndef WEBP_WEBP_ENCODE_H_ #define WEBP_WEBP_ENCODE_H_ +#include + #include "./types.h" #ifdef __cplusplus extern "C" { #endif -#define WEBP_ENCODER_ABI_VERSION 0x020f // MAJOR(8b) + MINOR(8b) +#define WEBP_ENCODER_ABI_VERSION 0x0210 // MAJOR(8b) + MINOR(8b) // Note: forward declaring enumerations is not allowed in (strict) C and C++, // the types are left here for reference. @@ -145,7 +147,7 @@ struct WebPConfig { // RGB information for better compression. The default // value is 0. - int use_delta_palette; // reserved for future lossless feature + int use_delta_palette; // reserved int use_sharp_yuv; // if needed, use sharp (and slow) RGB->YUV conversion int qmin; // minimum permissible quality factor @@ -224,14 +226,15 @@ struct WebPAuxStats { 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 transform_bits; // precision bits for predictor 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 + int cross_color_transform_bits; // precision bits for cross-color transform - uint32_t pad[2]; // padding for later use + uint32_t pad[1]; // padding for later use }; // Signature for output function. Should return true if writing was successful. diff --git a/3rdparty/libwebp/src/webp/format_constants.h b/3rdparty/libwebp/src/webp/format_constants.h index 999035c5d2..9b007c8a9d 100644 --- a/3rdparty/libwebp/src/webp/format_constants.h +++ b/3rdparty/libwebp/src/webp/format_constants.h @@ -46,7 +46,12 @@ #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 NUM_HUFFMAN_BITS 3 + +// the maximum number of bits defining a transform is +// MIN_TRANSFORM_BITS + (1 << NUM_TRANSFORM_BITS) - 1 +#define MIN_TRANSFORM_BITS 2 +#define NUM_TRANSFORM_BITS 3 #define TRANSFORM_PRESENT 1 // The bit to be written when next data // to be read is a transform. diff --git a/3rdparty/libwebp/src/webp/mux_types.h b/3rdparty/libwebp/src/webp/mux_types.h index c585d2082f..c1bbad3971 100644 --- a/3rdparty/libwebp/src/webp/mux_types.h +++ b/3rdparty/libwebp/src/webp/mux_types.h @@ -15,6 +15,7 @@ #define WEBP_WEBP_MUX_TYPES_H_ #include // memset() + #include "./types.h" #ifdef __cplusplus diff --git a/3rdparty/libwebp/src/webp/types.h b/3rdparty/libwebp/src/webp/types.h index 9c17edec45..549a0a7d27 100644 --- a/3rdparty/libwebp/src/webp/types.h +++ b/3rdparty/libwebp/src/webp/types.h @@ -14,10 +14,10 @@ #ifndef WEBP_WEBP_TYPES_H_ #define WEBP_WEBP_TYPES_H_ -#include // for size_t +#include // IWYU pragma: export for size_t #ifndef _MSC_VER -#include +#include // IWYU pragma: export #if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \ (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define WEBP_INLINE inline @@ -38,11 +38,11 @@ typedef long long int int64_t; #ifndef WEBP_NODISCARD #if defined(WEBP_ENABLE_NODISCARD) && WEBP_ENABLE_NODISCARD -#if (defined(__cplusplus) && __cplusplus >= 201700L) || \ +#if (defined(__cplusplus) && __cplusplus >= 201703L) || \ (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) #define WEBP_NODISCARD [[nodiscard]] #else -// gcc's __has_attribute does not work for enums. +// gcc's __attribute__((warn_unused_result)) does not work for enums. #if defined(__clang__) && defined(__has_attribute) #if __has_attribute(warn_unused_result) #define WEBP_NODISCARD __attribute__((warn_unused_result)) diff --git a/3rdparty/openjpeg/openjp2/jp2.c b/3rdparty/openjpeg/openjp2/jp2.c index da5063186c..3d16d1075f 100644 --- a/3rdparty/openjpeg/openjp2/jp2.c +++ b/3rdparty/openjpeg/openjp2/jp2.c @@ -1987,6 +1987,12 @@ OPJ_BOOL opj_jp2_setup_encoder(opj_jp2_t *jp2, if (image->icc_profile_len) { jp2->meth = 2; jp2->enumcs = 0; + jp2->color.icc_profile_buf = (OPJ_BYTE *)opj_malloc(image->icc_profile_len); + if (jp2->color.icc_profile_buf) { + jp2->color.icc_profile_len = image->icc_profile_len; + memcpy(jp2->color.icc_profile_buf, image->icc_profile_buf, + image->icc_profile_len); + } } else { jp2->meth = 1; if (image->color_space == OPJ_CLRSPC_SRGB) { diff --git a/3rdparty/openjpeg/openjp2/libopenjp2.pc.cmake.in b/3rdparty/openjpeg/openjp2/libopenjp2.pc.cmake.in index 2ade312b29..ac33f1447e 100644 --- a/3rdparty/openjpeg/openjp2/libopenjp2.pc.cmake.in +++ b/3rdparty/openjpeg/openjp2/libopenjp2.pc.cmake.in @@ -10,6 +10,6 @@ Description: JPEG2000 library (Part 1 and 2) URL: http://www.openjpeg.org/ Version: @OPENJPEG_VERSION@ Libs: -L${libdir} -lopenjp2 -Libs.private: -lm +Libs.private: @deps@ Cflags: -I${includedir} Cflags.private: -DOPJ_STATIC diff --git a/3rdparty/openjpeg/openjp2/openjpeg.h b/3rdparty/openjpeg/openjp2/openjpeg.h index 59abd323ae..fe2f1ee0c4 100644 --- a/3rdparty/openjpeg/openjp2/openjpeg.h +++ b/3rdparty/openjpeg/openjp2/openjpeg.h @@ -635,6 +635,8 @@ typedef void * opj_codec_t; /* * Callback function prototype for read function + * @return returns The number of bytes delivered into + * \a p_buffer. -1 signals end of stream. */ typedef OPJ_SIZE_T(* opj_stream_read_fn)(void * p_buffer, OPJ_SIZE_T p_nb_bytes, void * p_user_data) ; @@ -1239,7 +1241,6 @@ OPJ_API void OPJ_CALLCONV opj_stream_set_user_data(opj_stream_t* p_stream, /** * Sets the length of the user data for the stream. - * * @param p_stream the stream to modify * @param data_length length of the user_data. */ @@ -1437,6 +1438,8 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_decoded_components(opj_codec_t *p_codec, * that is to say at the highest resolution level, even if requesting the image at lower * resolution levels. * + * Note: If p_start_x, p_start_y, p_end_x, p_end_y are all 0, then the whole image is decoded. + * * Generally opj_set_decode_area() should be followed by opj_decode(), and the * codec cannot be re-used. * In the particular case of an image made of a single tile, several sequences of diff --git a/CMakeLists.txt b/CMakeLists.txt index 49c325c346..e0a80b7a01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,7 +167,7 @@ OCV_OPTION(WITH_CAP_IOS "Enable iOS video capture" ON VERIFY HAVE_CAP_IOS) OCV_OPTION(WITH_CAROTENE "Use NVidia carotene acceleration library for ARM platform" (NOT CV_DISABLE_OPTIMIZATION) VISIBLE_IF (ARM OR AARCH64) AND NOT IOS AND NOT XROS) -OCV_OPTION(WITH_KLEIDICV "Use KleidiCV library for ARM platforms" (ANDROID AND AARCH64 AND NOT CV_DISABLE_OPTIMIZATION) +OCV_OPTION(WITH_KLEIDICV "Use KleidiCV library for ARM platforms" (NOT CV_DISABLE_OPTIMIZATION) VISIBLE_IF (AARCH64 AND (ANDROID OR UNIX))) OCV_OPTION(WITH_NDSRVP "Use Andes RVP extension" (NOT CV_DISABLE_OPTIMIZATION) VISIBLE_IF RISCV) @@ -771,12 +771,12 @@ if(BUILD_JAVA) if(ANDROID) include(cmake/android/OpenCVDetectAndroidSDK.cmake) else() - include(cmake/OpenCVDetectApacheAnt.cmake) - if(ANT_EXECUTABLE AND NOT OPENCV_JAVA_IGNORE_ANT) - ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "ANT") - elseif(NOT ANDROID) - find_package(Java) - if(Java_FOUND) + find_package(Java QUIET) + if(Java_FOUND) + include(cmake/OpenCVDetectApacheAnt.cmake) + if(ANT_EXECUTABLE AND NOT OPENCV_JAVA_IGNORE_ANT) + ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "ANT") + else() include(UseJava) ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "JAVA") endif() @@ -1473,6 +1473,8 @@ elseif(WITH_PNG OR HAVE_PNG) endif() elseif(PNG_LOONGARCH_LSX) status(" SIMD Support:" "YES (LoongArch LSX)") + elseif(PNG_RISCV_RVV) + status(" SIMD Support:" "YES (RISC-V RVV)") else() status(" SIMD Support:" "NO") endif() @@ -1635,6 +1637,12 @@ if(WITH_GPHOTO2 OR HAVE_GPHOTO2) status(" gPhoto2:" HAVE_GPHOTO2 THEN "YES" ELSE NO) endif() +if(OBSENSOR_USE_ORBBEC_SDK OR HAVE_OBSENSOR_ORBBEC_SDK) + status(" Orbbec SDK:" HAVE_OBSENSOR_ORBBEC_SDK THEN "YES (version ${ORBBEC_SDK_VERSION})" ELSE NO) +else() + status(" Orbbec:" HAVE_OBSENSOR THEN "YES" ELSE NO) +endif() + if(ANDROID) status(" MEDIANDK:" HAVE_ANDROID_MEDIANDK THEN "YES" ELSE NO) status(" NDK Camera:" HAVE_ANDROID_NATIVE_CAMERA THEN "YES" ELSE NO) @@ -1885,7 +1893,7 @@ if(BUILD_JAVA) status(" Java:" Java_FOUND THEN "YES (ver ${Java_VERSION})" ELSE NO) status(" JNI:" JNI_INCLUDE_DIRS THEN "${JNI_INCLUDE_DIRS}" ELSE NO) endif() - status(" Java wrappers:" HAVE_opencv_java THEN "YES (${OPENCV_JAVA_SDK_BUILD_TYPE})" ELSE NO) + status(" Java wrappers:" HAVE_opencv_java THEN "YES (${OPENCV_JAVA_SDK_BUILD_TYPE})" ELSE NO) status(" Java tests:" BUILD_TESTS AND (opencv_test_java_BINARY_DIR OR opencv_test_android_BINARY_DIR) THEN YES ELSE NO) endif() diff --git a/cmake/OpenCVCompilerOptimizations.cmake b/cmake/OpenCVCompilerOptimizations.cmake index 865bfd28a4..32af057124 100644 --- a/cmake/OpenCVCompilerOptimizations.cmake +++ b/cmake/OpenCVCompilerOptimizations.cmake @@ -49,7 +49,7 @@ set(CPU_ALL_OPTIMIZATIONS "SSE;SSE2;SSE3;SSSE3;SSE4_1;SSE4_2;POPCNT;AVX;FP16;AVX2;FMA3;AVX_512F") list(APPEND CPU_ALL_OPTIMIZATIONS "AVX512_COMMON;AVX512_KNL;AVX512_KNM;AVX512_SKX;AVX512_CNL;AVX512_CLX;AVX512_ICL") -list(APPEND CPU_ALL_OPTIMIZATIONS NEON VFPV3 FP16 NEON_DOTPROD NEON_FP16 NEON_BF16) +list(APPEND CPU_ALL_OPTIMIZATIONS SVE NEON VFPV3 FP16 NEON_DOTPROD NEON_FP16 NEON_BF16) list(APPEND CPU_ALL_OPTIMIZATIONS MSA) list(APPEND CPU_ALL_OPTIMIZATIONS VSX VSX3) list(APPEND CPU_ALL_OPTIMIZATIONS RVV FP16 RVV_ZVFH) @@ -104,6 +104,7 @@ ocv_optimization_process_obsolete_option(ENABLE_AVX2 AVX2 ON) ocv_optimization_process_obsolete_option(ENABLE_FMA3 FMA3 ON) ocv_optimization_process_obsolete_option(ENABLE_VFPV3 VFPV3 OFF) +ocv_optimization_process_obsolete_option(ENABLE_SVE SVE ON) ocv_optimization_process_obsolete_option(ENABLE_NEON NEON ON) ocv_optimization_process_obsolete_option(ENABLE_VSX VSX ON) @@ -352,6 +353,7 @@ if(X86 OR X86_64) endif() elseif(ARM OR AARCH64) + ocv_update(CPU_SVE_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_sve.cpp") ocv_update(CPU_NEON_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon.cpp") ocv_update(CPU_FP16_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_fp16.cpp") ocv_update(CPU_NEON_FP16_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon_fp16.cpp") @@ -368,16 +370,24 @@ elseif(ARM OR AARCH64) endif() ocv_update(CPU_FP16_IMPLIES "NEON") else() - ocv_update(CPU_KNOWN_OPTIMIZATIONS "NEON;FP16;NEON_DOTPROD;NEON_FP16;NEON_BF16") + if (UNIX AND NOT APPLE) + #Current Apple silicone M4 does not support SVE, + #but some Xcode versions reports their support. + ocv_update(CPU_KNOWN_OPTIMIZATIONS "SVE;NEON;FP16;NEON_DOTPROD;NEON_FP16;NEON_BF16") + else() + ocv_update(CPU_KNOWN_OPTIMIZATIONS "NEON;FP16;NEON_DOTPROD;NEON_FP16;NEON_BF16") + endif() ocv_update(CPU_FP16_IMPLIES "NEON") ocv_update(CPU_NEON_DOTPROD_IMPLIES "NEON") ocv_update(CPU_NEON_FP16_IMPLIES "NEON") ocv_update(CPU_NEON_BF16_IMPLIES "NEON") if(MSVC) + ocv_update(CPU_SVE_FLAGS_ON "") ocv_update(CPU_NEON_DOTPROD_FLAGS_ON "") ocv_update(CPU_NEON_FP16_FLAGS_ON "") ocv_update(CPU_NEON_BF16_FLAGS_ON "") else() + ocv_update(CPU_SVE_FLAGS_ON "-march=armv8.2-a+sve") ocv_update(CPU_NEON_DOTPROD_FLAGS_ON "-march=armv8.2-a+dotprod") ocv_update(CPU_NEON_FP16_FLAGS_ON "-march=armv8.2-a+fp16") ocv_update(CPU_NEON_BF16_FLAGS_ON "-march=armv8.2-a+bf16") diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index b47e7366c9..93e93c157c 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -1622,7 +1622,7 @@ endfunction() set(__OPENCV_EXPORTED_EXTERNAL_TARGETS "" CACHE INTERNAL "") function(ocv_install_used_external_targets) if(NOT BUILD_SHARED_LIBS) - foreach(tgt in ${ARGN}) + foreach(tgt IN LISTS ARGN) if(tgt MATCHES "^ocv\.3rdparty\.") list(FIND __OPENCV_EXPORTED_EXTERNAL_TARGETS "${tgt}" _found) if(_found EQUAL -1) # don't export target twice diff --git a/cmake/android/OpenCVDetectAndroidSDK.cmake b/cmake/android/OpenCVDetectAndroidSDK.cmake index d9853ae4de..dd005e93cf 100644 --- a/cmake/android/OpenCVDetectAndroidSDK.cmake +++ b/cmake/android/OpenCVDetectAndroidSDK.cmake @@ -220,8 +220,10 @@ else() endif() # BUILD_ANDROID_PROJECTS if(ANDROID_PROJECTS_BUILD_TYPE STREQUAL "ANT") + ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "ANT") include(${CMAKE_CURRENT_LIST_DIR}/android_ant_projects.cmake) elseif(ANDROID_PROJECTS_BUILD_TYPE STREQUAL "GRADLE") + ocv_update(OPENCV_JAVA_SDK_BUILD_TYPE "GRADLE") include(${CMAKE_CURRENT_LIST_DIR}/android_gradle_projects.cmake) elseif(BUILD_ANDROID_PROJECTS) message(FATAL_ERROR "Internal error") diff --git a/cmake/checks/cpu_sve.cpp b/cmake/checks/cpu_sve.cpp new file mode 100644 index 0000000000..889d18dfc1 --- /dev/null +++ b/cmake/checks/cpu_sve.cpp @@ -0,0 +1,24 @@ +#include + +#if defined(__ARM_FEATURE_SVE) +# include +# define CV_SVE 1 +#endif + +#if defined(CV_SVE) +int test() +{ + const float src[1024] = {0.0}; + svbool_t pg = svptrue_b32(); + svfloat32_t val = svld1(pg, src); + return (int)svlastb_f32(pg, val); +} +#else +#error "SVE is not supported" +#endif + +int main() +{ + printf("%d\n", test()); + return 0; +} diff --git a/doc/js_tutorials/js_assets/js_image_arithmetics_bitwise.html b/doc/js_tutorials/js_assets/js_image_arithmetics_bitwise.html index ccf0bbbda7..05c0ffd730 100644 --- a/doc/js_tutorials/js_assets/js_image_arithmetics_bitwise.html +++ b/doc/js_tutorials/js_assets/js_image_arithmetics_bitwise.html @@ -82,7 +82,7 @@ cv.bitwise_and(logo, logo, imgFg, mask); // Put logo in ROI and modify the main image cv.add(imgBg, imgFg, sum); -dst = src.mat_clone(); +dst = src.clone(); for (let i = 0; i < logo.rows; i++) { for (let j = 0; j < logo.cols; j++) { dst.ucharPtr(i, j)[0] = sum.ucharPtr(i, j)[0]; diff --git a/doc/js_tutorials/js_assets/js_imgproc_camera.html b/doc/js_tutorials/js_assets/js_imgproc_camera.html index 273b11ff57..2df68d7f33 100644 --- a/doc/js_tutorials/js_assets/js_imgproc_camera.html +++ b/doc/js_tutorials/js_assets/js_imgproc_camera.html @@ -248,7 +248,7 @@ function backprojection(src) { if (base instanceof cv.Mat) { base.delete(); } - base = src.mat_clone(); + base = src.clone(); cv.cvtColor(base, base, cv.COLOR_RGB2HSV, 0); } cv.cvtColor(src, dstC3, cv.COLOR_RGB2HSV, 0); diff --git a/doc/js_tutorials/js_assets/js_intelligent_scissors.html b/doc/js_tutorials/js_assets/js_intelligent_scissors.html index f48dd07e4b..1782dc6f03 100644 --- a/doc/js_tutorials/js_assets/js_intelligent_scissors.html +++ b/doc/js_tutorials/js_assets/js_intelligent_scissors.html @@ -53,7 +53,7 @@ canvas.addEventListener('click', e => { }); canvas.addEventListener('mousemove', e => { let x = e.offsetX, y = e.offsetY; //console.log(x, y); - let dst = src.mat_clone(); + let dst = src.clone(); if (hasMap && x >= 0 && x < src.cols && y >= 0 && y < src.rows) { let contour = new cv.Mat(); diff --git a/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown b/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown index 74c3b9cc86..ee110c37cb 100644 --- a/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown +++ b/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown @@ -77,14 +77,12 @@ How to copy Mat There are 2 ways to copy a Mat: @code{.js} -// 1. Clone (deep copy) -let dst = src.mat_clone(); +// 1. Clone +let dst = src.clone(); // 2. CopyTo(only entries indicated in the mask are copied) src.copyTo(dst, mask); @endcode -@note In OpenCV.js, use `mat_clone()` instead of `clone()` to ensure deep copy behavior. The `clone()` method may perform shallow copy due to Emscripten embind limitations. - How to convert the type of Mat ------------------------------ diff --git a/doc/js_tutorials/js_setup/js_setup/js_setup.markdown b/doc/js_tutorials/js_setup/js_setup/js_setup.markdown index 198cc74a24..3628285c41 100644 --- a/doc/js_tutorials/js_setup/js_setup/js_setup.markdown +++ b/doc/js_tutorials/js_setup/js_setup/js_setup.markdown @@ -84,7 +84,12 @@ Building OpenCV.js from Source @endcode @note - It requires `python` and `cmake` installed in your development environment. + - It requires `python` and `cmake` installed in your development environment. + - To build with Emscripten 4.0.20 or later, append --cmake_option="-DCMAKE_CXX_STANDARD=17" . + Embind requires C++17 or later since Emscripten 4.0.20. + @code{.bash} + emcmake python ./opencv/platforms/js/build_js.py build_js --cmake_option="-DCMAKE_CXX_STANDARD=17" + @endcode -# [Optional] To build the OpenCV.js loader, append `--build_loader`. diff --git a/doc/opencv.bib b/doc/opencv.bib index 3cd98ed53e..316baface9 100644 --- a/doc/opencv.bib +++ b/doc/opencv.bib @@ -1634,3 +1634,13 @@ year={2013}, url={https://enpc.hal.science/hal-00858703/document} } +@article{hrazdira2020iterative, + title={Iterative phase correlation algorithm for high-precision subpixel image registration}, + author={Hrazd{\'\i}ra, Zdenek and Druckm{\"u}ller, Miloslav and Habbal, Shadia}, + journal={The Astrophysical Journal Supplement Series}, + volume={247}, + number={1}, + pages={8}, + year={2020}, + publisher={IOP Publishing} +} diff --git a/doc/tutorials/app/animations.markdown b/doc/tutorials/app/animations.markdown index 2f7c5e1cc8..d11277d4db 100644 --- a/doc/tutorials/app/animations.markdown +++ b/doc/tutorials/app/animations.markdown @@ -67,6 +67,9 @@ Explanation Each frame in the `animation.frames` vector can be displayed as a standalone image. This loop iterates through each frame, displaying it in a window with a short delay to simulate the animation. +> **Note:** Frame durations in `cv::Animation` are expressed in milliseconds. +> When displaying frames manually using `cv::waitKey`, make sure to use the corresponding duration value to preserve the original animation timing. + @add_toggle_cpp @snippet cpp/tutorial_code/imgcodecs/animations.cpp show_animation @end_toggle diff --git a/doc/tutorials/core/adding_images/adding_images.markdown b/doc/tutorials/core/adding_images/adding_images.markdown index 6369cd8394..cead427843 100644 --- a/doc/tutorials/core/adding_images/adding_images.markdown +++ b/doc/tutorials/core/adding_images/adding_images.markdown @@ -27,12 +27,12 @@ Theory The explanation below belongs to the book [Computer Vision: Algorithms and Applications](http://szeliski.org/Book/) by Richard Szeliski -From our previous tutorial, we know already a bit of *Pixel operators*. An interesting dyadic +From our previous tutorial, we already know a bit of *Pixel operators*. An interesting dyadic (two-input) operator is the *linear blend operator*: \f[g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x)\f] -By varying \f$\alpha\f$ from \f$0 \rightarrow 1\f$ this operator can be used to perform a temporal +By varying \f$\alpha\f$ from \f$0 \rightarrow 1\f$, this operator can be used to perform a temporal *cross-dissolve* between two images or videos, as seen in slide shows and film productions (cool, eh?) diff --git a/doc/tutorials/core/univ_intrin/univ_intrin.markdown b/doc/tutorials/core/univ_intrin/univ_intrin.markdown index 0fc610ba9a..b38a8c3d2f 100644 --- a/doc/tutorials/core/univ_intrin/univ_intrin.markdown +++ b/doc/tutorials/core/univ_intrin/univ_intrin.markdown @@ -7,7 +7,7 @@ Vectorizing your code using Universal Intrinsics {#tutorial_univ_intrin} | | | | -: | :- | -| Compatibility | OpenCV >= 3.0 | +| Compatibility | OpenCV >= 4.11 | Goal ---- @@ -28,19 +28,16 @@ SIMD stands for **Single Instruction, Multiple Data**. SIMD Intrinsics allow the Depending on what *Instruction Sets* your CPU supports, you may be able to use the different registers. To learn more, look [here](https://en.wikipedia.org/wiki/Instruction_set_architecture) +### VLA +VLA stands for **Vector Length Agnostic** . +A mechanism where the register width is determined by the hardware at runtime rather than being fixed at compile time. +This allows a single binary to scale its performance across different CPUs within the same architecture (e.g., RVV or SVE). + Universal Intrinsics -------------------- -OpenCVs universal intrinsics provides an abstraction to SIMD vectorization methods and allows the user to use intrinsics without the need to write system specific code. - -OpenCV Universal Intrinsics support the following instruction sets: -* *128 bit* registers of various types support is implemented for a wide range of architectures including - * x86(SSE/SSE2/SSE4.2), - * ARM(NEON), - * PowerPC(VSX), - * MIPS(MSA). -* *256 bit* registers are supported on x86(AVX2) and -* *512 bit* registers are supported on x86(AVX512) +OpenCV's universal intrinsics provides an abstraction to SIMD and VLA vectorization methods and allows the user to use intrinsics without the need to write system specific code. +Supported SIMD/VLA technologies are detailed in @ref core_hal_intrin . **We will now introduce the available structures and functions:** * Register structures @@ -150,33 +147,35 @@ Now that we know how registers work, let us look at the functions used for filli The universal intrinsics set provides element wise binary and unary operations. +@note Since OpenCV 4.11, C++ operator overloading (e.g., +, ) in Universal Intrinsics has been deprecated in favor of explicit wrapper functions (e.g., v_add, v_mul) to ensure compatibility with VLA architectures. +See also: https://github.com/opencv/opencv/issues/27267 + * **Arithmetics**: We can add, subtract, multiply and divide two registers element-wise. The registers must be of the same width and hold the same type. To multiply two registers, for example: v_float32 a, b; // {a1, ..., an}, {b1, ..., bn} - v_float32 c; - c = a + b // {a1 + b1, ..., an + bn} - c = a * b; // {a1 * b1, ..., an * bn} + v_float32 c = v_add(a, b); // {a1 + b1, ..., an + bn} + v_flaot32 d = v_mul(a, b); // {a1 * b1, ..., an * bn}
-* **Bitwise Logic and Shifts**: We can left shift or right shift the bits of each element of the register. We can also apply bitwise &, |, ^ and ~ operators between two registers element-wise: +* **Bitwise Logic and Shifts**: We can left shift or right shift the bits of each element of the register. We can also apply bitwise and, or, xor and not operators between two registers element-wise: v_int32 as; // {a1, ..., an} - v_int32 al = as << 2; // {a1 << 2, ..., an << 2} - v_int32 bl = as >> 2; // {a1 >> 2, ..., an >> 2} + v_int32 al = v_shl(as, 2); // {a1 << 2, ..., an << 2} + v_int32 bl = v_shr(as, 2); // {a1 >> 2, ..., an >> 2} v_int32 a, b; - v_int32 a_and_b = a & b; // {a1 & b1, ..., an & bn} + v_int32 a_and_b = v_and(a, b); // {a1 & b1, ..., an & bn}
-* **Comparison Operators**: We can compare values between two registers using the <, >, <= , >=, == and != operators. Since each register contains multiple values, we don't get a single bool for these operations. Instead, for true values, all bits are converted to one (0xff for 8 bits, 0xffff for 16 bits, etc), while false values return bits converted to zero. +* **Comparison Operators**: We can compare values between two registers using the v_lt(<), v_gt(>), v_le(<=) , v_ge(>=), v_eq(==) and v_ne(!=). Since each register contains multiple values, we don't get a single bool for these operations. Instead, for true values, all bits are converted to one (0xff for 8 bits, 0xffff for 16 bits, etc), while false values return bits converted to zero. // let us consider the following code is run in a 128-bit register - v_uint8 a; // a = {0, 1, 2, ..., 15} - v_uint8 b; // b = {15, 14, 13, ..., 0} + v_uint8 a; // a = {0, 1, 2, ..., 13, 14, 15} + v_uint8 b; // b = {15, 14, 13, ..., 2, 1, 0} - v_uint8 c = a < b; + v_uint8 c = v_lt(a, b); // c = {255, 255, 255, ..., 0, 0, 0} /* let us look at the first 4 values in binary @@ -192,7 +191,7 @@ The universal intrinsics set provides element wise binary and unary operations. v_int32 a; // a = {1, 2, 3, 4, 5, 6, 7, 8} v_int32 b; // b = {8, 7, 6, 5, 4, 3, 2, 1} - v_int32 c = (a < b); // c = {-1, -1, -1, -1, 0, 0, 0, 0} + v_int32 c = v_lt(a, b); // c = {-1, -1, -1, -1, 0, 0, 0, 0} /* The true values are 0xffffffff, which in signed 32-bit integer representation is equal to -1. diff --git a/doc/tutorials/introduction/env_reference/env_reference.markdown b/doc/tutorials/introduction/env_reference/env_reference.markdown index 288c6d0a37..ae4832a8c6 100644 --- a/doc/tutorials/introduction/env_reference/env_reference.markdown +++ b/doc/tutorials/introduction/env_reference/env_reference.markdown @@ -114,7 +114,7 @@ Links: | OPENCV_THREAD_POOL_ACTIVE_WAIT_WORKER | num | 2000 | tune pthreads parallel_for backend | | OPENCV_THREAD_POOL_ACTIVE_WAIT_MAIN | num | 10000 | tune pthreads parallel_for backend | | OPENCV_THREAD_POOL_ACTIVE_WAIT_THREADS_LIMIT | num | 0 | tune pthreads parallel_for backend | -| OPENCV_FOR_OPENMP_DYNAMIC_DISABLE | bool | false | use single OpenMP thread | +| OPENCV_FOR_OPENMP_DYNAMIC_DISABLE | bool | false | Removed in 4.13.0. Use standard [OMP_DYNAMIC](https://www.openmp.org/spec-html/5.0/openmpsu116.html) instead | ## backends diff --git a/doc/tutorials/others/stitcher.markdown b/doc/tutorials/others/stitcher.markdown index 8c9ffa4a4d..62ad2da755 100644 --- a/doc/tutorials/others/stitcher.markdown +++ b/doc/tutorials/others/stitcher.markdown @@ -149,7 +149,7 @@ Pairwise images are matched using an homography --matcher homography and estimat Confidence for feature matching step is 0.3 : --match_conf 0.3. You can decrease this value if you have some difficulties to match images -Threshold for two images are from the same panorama confidence is 0. : --conf_thresh 0.3 You can decrease this value if you have some difficulties to match images +Threshold for two images are from the same panorama confidence is 0. : --conf_thresh 0.3 You can decrease this value if you have some difficulties to match images Bundle adjustment cost function is ray --ba ray diff --git a/hal/kleidicv/kleidicv.cmake b/hal/kleidicv/kleidicv.cmake index ebd6ea3c61..4ea691f97a 100644 --- a/hal/kleidicv/kleidicv.cmake +++ b/hal/kleidicv/kleidicv.cmake @@ -1,8 +1,8 @@ function(download_kleidicv root_var) set(${root_var} "" PARENT_SCOPE) - ocv_update(KLEIDICV_SRC_COMMIT "0.5.0") - ocv_update(KLEIDICV_SRC_HASH "ba5648f8df678548f337d19d8ac607d6") + ocv_update(KLEIDICV_SRC_COMMIT "0.7.0") + ocv_update(KLEIDICV_SRC_HASH "e8f94e427bd78a745afa5c8cd073b416") set(THE_ROOT "${OpenCV_BINARY_DIR}/3rdparty/kleidicv") ocv_download(FILENAME "kleidicv-${KLEIDICV_SRC_COMMIT}.tar.gz" diff --git a/hal/riscv-rvv/src/core/flip.cpp b/hal/riscv-rvv/src/core/flip.cpp index 42f7c8b16d..8a39e98bd1 100644 --- a/hal/riscv-rvv/src/core/flip.cpp +++ b/hal/riscv-rvv/src/core/flip.cpp @@ -46,10 +46,25 @@ CV_HAL_RVV_FLIP_C1(64UC1, uint64_t, RVV_U64M8) #define CV_HAL_RVV_FLIP_INPLACE_C1(name, _Tps, RVV) \ inline void flip_inplace_##name(uchar* data, size_t step, int width, int height, int flip_mode) { \ auto new_height = (flip_mode < 0 ? height / 2 : height); \ - auto new_width = width / 2; \ - for (int h = 0; h < new_height; h++) { \ + auto new_width = (flip_mode < 0 ? width : width / 2); \ + int h; \ + for (h = 0; h < new_height; h++) { \ _Tps* row_begin = (_Tps*)(data + step * h); \ - _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (new_height - h) : (h + 1))); \ + _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (height - h) : (h + 1))); \ + int vl; \ + for (int w = 0; w < new_width; w += vl) { \ + vl = RVV::setvl(new_width - w); \ + RVV::VecType indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ + auto v_left = RVV::vload(row_begin + w, vl); \ + auto v_right = RVV::vload(row_end - w - vl, vl); \ + RVV::vstore(row_begin + w, __riscv_vrgather(v_right, indices, vl), vl); \ + RVV::vstore(row_end - w - vl, __riscv_vrgather(v_left, indices, vl), vl); \ + } \ + } \ + if (flip_mode == -1 && new_height * 2 != height) { \ + _Tps* row_begin = (_Tps*)(data + step * h); \ + _Tps* row_end = (_Tps*)(data + step * (h + 1)); \ + new_width /= 2; \ int vl; \ for (int w = 0; w < new_width; w += vl) { \ vl = RVV::setvl(new_width - w); \ @@ -117,10 +132,27 @@ CV_HAL_RVV_FLIP_C3(64UC3, uint64_t, RVV_C3_U64M2) #define CV_HAL_RVV_FLIP_INPLACE_C3(name, _Tps, RVV) \ inline void flip_inplace_##name(uchar* data, size_t step, int width, int height, int flip_mode) { \ auto new_height = (flip_mode < 0 ? height / 2 : height); \ - auto new_width = width / 2; \ - for (int h = 0; h < new_height; h++) { \ + auto new_width = (flip_mode < 0 ? width : width / 2); \ + int h; \ + for (h = 0; h < new_height; h++) { \ _Tps* row_begin = (_Tps*)(data + step * h); \ - _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (new_height - h) : (h + 1))); \ + _Tps* row_end = (_Tps*)(data + step * (flip_mode < 0 ? (height - h) : (h + 1))); \ + int vl; \ + for (int w = 0; w < new_width; w += vl) { \ + vl = RVV::setvl(new_width - w); \ + RVV::VecType indices = __riscv_vrsub(RVV::vid(vl), vl - 1, vl); \ + auto v_left = RVV::vload3(row_begin + 3 * w, vl); \ + auto flipped_left = RVV::vflip3(v_left, indices, vl); \ + auto v_right = RVV::vload3(row_end - 3 * (w + vl), vl); \ + auto flipped_right = RVV::vflip3(v_right, indices, vl); \ + RVV::vstore3(row_begin + 3 * w, flipped_right, vl); \ + RVV::vstore3(row_end - 3 * (w + vl), flipped_left, vl); \ + } \ + } \ + if (flip_mode == -1 && new_height * 2 != height) { \ + _Tps* row_begin = (_Tps*)(data + step * h); \ + _Tps* row_end = (_Tps*)(data + step * (h + 1)); \ + new_width /= 2; \ int vl; \ for (int w = 0; w < new_width; w += vl) { \ vl = RVV::setvl(new_width - w); \ diff --git a/modules/3d/src/fundam.cpp b/modules/3d/src/fundam.cpp index 8e67cc1d89..786dda08b7 100644 --- a/modules/3d/src/fundam.cpp +++ b/modules/3d/src/fundam.cpp @@ -790,7 +790,7 @@ public: Mat F(count == 7 ? 9 : 3, 3, CV_64F, f); int n = count == 7 ? run7Point(m1, m2, F) : run8Point(m1, m2, F); - if( n == 0 ) + if( n <= 0 ) _model.release(); else F.rowRange(0, n*3).copyTo(_model); diff --git a/modules/3d/src/usac/dls_solver.cpp b/modules/3d/src/usac/dls_solver.cpp index 76e7e5d645..1e1cf6c34d 100644 --- a/modules/3d/src/usac/dls_solver.cpp +++ b/modules/3d/src/usac/dls_solver.cpp @@ -44,6 +44,10 @@ #elif defined(HAVE_LAPACK) #include "opencv_lapack.h" #endif +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif namespace cv { namespace usac { class DLSPnPImpl : public DLSPnP { @@ -890,3 +894,7 @@ Ptr DLSPnP::create(const Mat &points_, const Mat &calib_norm_pts, const return makePtr(points_, calib_norm_pts, K); } }} + +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/modules/3d/src/usac/essential_solver.cpp b/modules/3d/src/usac/essential_solver.cpp index 8f9b5b9d33..dfff4b970b 100644 --- a/modules/3d/src/usac/essential_solver.cpp +++ b/modules/3d/src/usac/essential_solver.cpp @@ -9,6 +9,10 @@ #elif defined(HAVE_LAPACK) #include "opencv_lapack.h" #endif +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif namespace cv { namespace usac { /* @@ -354,3 +358,7 @@ Ptr EssentialMinimalSolver5pts::create return makePtr(points_, use_svd, is_nister); } }} + +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic pop +#endif \ No newline at end of file diff --git a/modules/3d/src/usac/pnp_solver.cpp b/modules/3d/src/usac/pnp_solver.cpp index 37e001f097..f699e7cd4e 100644 --- a/modules/3d/src/usac/pnp_solver.cpp +++ b/modules/3d/src/usac/pnp_solver.cpp @@ -11,6 +11,10 @@ #elif defined(HAVE_LAPACK) #include "opencv_lapack.h" #endif +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif namespace cv { namespace usac { class PnPMinimalSolver6PtsImpl : public PnPMinimalSolver6Pts { @@ -408,3 +412,7 @@ Ptr P3PSolver::create(const Mat &points_, const Mat &calib_norm_pts, return makePtr(points_, calib_norm_pts, K); } }} + +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic pop +#endif \ No newline at end of file diff --git a/modules/3d/src/usac/utils.cpp b/modules/3d/src/usac/utils.cpp index de67aabd9e..886280928d 100644 --- a/modules/3d/src/usac/utils.cpp +++ b/modules/3d/src/usac/utils.cpp @@ -388,12 +388,12 @@ void Utils::densitySort (const Mat &points, int knn, Mat &sorted_points, std::ve sorted_mask[i] = i; // get neighbors - FlannNeighborhoodGraph &graph = *FlannNeighborhoodGraph::create(points, points_size, knn, + cv::Ptr graph = FlannNeighborhoodGraph::create(points, points_size, knn, true /*get distances */, 6, 1); std::vector sum_knn_distances (points_size, 0); for (int p = 0; p < points_size; p++) { - const std::vector &dists = graph.getNeighborsDistances(p); + const std::vector &dists = graph->getNeighborsDistances(p); for (int k = 0; k < knn; k++) sum_knn_distances[p] += dists[k]; } diff --git a/modules/3d/test/test_fundam.cpp b/modules/3d/test/test_fundam.cpp index 279c6d7110..01b71fc4dc 100644 --- a/modules/3d/test/test_fundam.cpp +++ b/modules/3d/test/test_fundam.cpp @@ -420,5 +420,13 @@ TEST(Calib3d_FindFundamentalMat, correctMatches) cout << np2 << endl; } +TEST(Calib3d_FindFundamentalMat, Crash) +{ + vector m1 = {{245, 128},{284, 226},{140, 60},{133, 127},{71, 218},{152, 138},{181, 106}}; + vector m2 = m1; + vector mask; + findFundamentalMat(m1, m2, mask, FM_LMEDS, 1, 0.99); +} + }} // namespace /* End of file. */ diff --git a/modules/calib/include/opencv2/calib.hpp b/modules/calib/include/opencv2/calib.hpp index 58aab94cfb..bb6475f185 100644 --- a/modules/calib/include/opencv2/calib.hpp +++ b/modules/calib/include/opencv2/calib.hpp @@ -654,7 +654,7 @@ CV_EXPORTS_W bool checkChessboard(InputArray img, Size size); - @ref CALIB_CB_LARGER The detected pattern is allowed to be larger than patternSize (see description). - @ref CALIB_CB_MARKER The detected pattern must have a marker (see description). This should be used if an accurate camera calibration is required. -@param meta Optional output arrray of detected corners (CV_8UC1 and size = cv::Size(columns,rows)). +@param meta Optional output array of detected corners (CV_8UC1 and size = cv::Size(columns,rows)). Each entry stands for one corner of the pattern and can have one of the following values: - 0 = no meta data attached - 1 = left-top corner of a black cell diff --git a/modules/calib/src/chessboard.cpp b/modules/calib/src/chessboard.cpp index 9b24148473..3c7030ffa3 100644 --- a/modules/calib/src/chessboard.cpp +++ b/modules/calib/src/chessboard.cpp @@ -2169,7 +2169,7 @@ cv::Point2f &Chessboard::Board::getCorner(int _row,int _col) } ++count; row_start = row_start->bottom; - }while(_row); + }while(row_start); } CV_Error(Error::StsInternal,"cannot find corner"); // return *top_left->top_left; // never reached diff --git a/modules/calib/src/chessboard.hpp b/modules/calib/src/chessboard.hpp index df4409fd69..a39f1f58d3 100644 --- a/modules/calib/src/chessboard.hpp +++ b/modules/calib/src/chessboard.hpp @@ -149,7 +149,7 @@ class Chessboard: public cv::Feature2D int max_tests; //!< maximal number of tested hypothesis bool super_resolution; //!< use super-repsolution for chessboard detection bool larger; //!< indicates if larger boards should be returned - bool marker; //!< indicates that valid boards must have a white and black cirlce marker used for orientation + bool marker; //!< indicates that valid boards must have a white and black circle marker used for orientation Parameters() { diff --git a/modules/core/include/opencv2/core/cv_cpu_dispatch.h b/modules/core/include/opencv2/core/cv_cpu_dispatch.h index a53641de20..345a5fa18c 100644 --- a/modules/core/include/opencv2/core/cv_cpu_dispatch.h +++ b/modules/core/include/opencv2/core/cv_cpu_dispatch.h @@ -239,6 +239,10 @@ struct VZeroUpperGuard { #elif defined(__ARM_NEON) # include # define CV_NEON 1 +#ifdef __ARM_FEATURE_SVE +# include +# define CV_SVE 1 +#endif #elif defined(__VSX__) && defined(__PPC64__) && defined(__LITTLE_ENDIAN__) # include # undef vector @@ -369,6 +373,10 @@ struct VZeroUpperGuard { # define CV_NEON 0 #endif +#ifndef CV_SVE +# define CV_SVE 0 +#endif + #ifndef CV_RVV071 # define CV_RVV071 0 #endif diff --git a/modules/core/include/opencv2/core/cv_cpu_helper.h b/modules/core/include/opencv2/core/cv_cpu_helper.h index 4e09f2a9a8..22657e4b54 100644 --- a/modules/core/include/opencv2/core/cv_cpu_helper.h +++ b/modules/core/include/opencv2/core/cv_cpu_helper.h @@ -399,6 +399,27 @@ #endif #define __CV_CPU_DISPATCH_CHAIN_AVX512_ICL(fn, args, mode, ...) CV_CPU_CALL_AVX512_ICL(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) +#if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_SVE +# define CV_TRY_SVE 1 +# define CV_CPU_FORCE_SVE 1 +# define CV_CPU_HAS_SUPPORT_SVE 1 +# define CV_CPU_CALL_SVE(fn, args) return (cpu_baseline::fn args) +# define CV_CPU_CALL_SVE_(fn, args) return (opt_SVE::fn args) +#elif !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_DISPATCH_COMPILE_SVE +# define CV_TRY_SVE 1 +# define CV_CPU_FORCE_SVE 0 +# define CV_CPU_HAS_SUPPORT_SVE (cv::checkHardwareSupport(CV_CPU_SVE)) +# define CV_CPU_CALL_SVE(fn, args) if (CV_CPU_HAS_SUPPORT_SVE) return (opt_SVE::fn args) +# define CV_CPU_CALL_SVE_(fn, args) if (CV_CPU_HAS_SUPPORT_SVE) return (opt_SVE::fn args) +#else +# define CV_TRY_SVE 0 +# define CV_CPU_FORCE_SVE 0 +# define CV_CPU_HAS_SUPPORT_SVE 0 +# define CV_CPU_CALL_SVE(fn, args) +# define CV_CPU_CALL_SVE_(fn, args) +#endif +#define __CV_CPU_DISPATCH_CHAIN_SVE(fn, args, mode, ...) CV_CPU_CALL_SVE(fn, args); __CV_EXPAND(__CV_CPU_DISPATCH_CHAIN_ ## mode(fn, args, __VA_ARGS__)) + #if !defined CV_DISABLE_OPTIMIZATION && defined CV_ENABLE_INTRINSICS && defined CV_CPU_COMPILE_NEON # define CV_TRY_NEON 1 # define CV_CPU_FORCE_NEON 1 diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index d00a78b151..9fd8c43f97 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -279,6 +279,7 @@ namespace cv { #define CV_CPU_NEON_DOTPROD 101 #define CV_CPU_NEON_FP16 102 #define CV_CPU_NEON_BF16 103 +#define CV_CPU_SVE 104 #define CV_CPU_MSA 150 @@ -342,6 +343,7 @@ enum CpuFeatures { CPU_NEON_DOTPROD = 101, CPU_NEON_FP16 = 102, CPU_NEON_BF16 = 103, + CPU_SVE = 104, CPU_MSA = 150, diff --git a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp index 35fda729dd..25f7c2bbbc 100644 --- a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp @@ -81,9 +81,26 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN "Universal intrinsics" is a types and functions set intended to simplify vectorization of code on different platforms. Currently a few different SIMD extensions on different architectures are supported. -128 bit registers of various types support is implemented for a wide range of architectures -including x86(__SSE/SSE2/SSE4.2__), ARM(__NEON__), PowerPC(__VSX__), MIPS(__MSA__). -256 bit long registers are supported on x86(__AVX2__) and 512 bit long registers are supported on x86(__AVX512__). + +OpenCV Universal Intrinsics support the following instruction sets: + +- *128 bit* registers of various types support is implemented for a wide range of architectures including + - x86(SSE/SSE2/SSE4.2), + - ARM(NEON): 64-bit float (64F) requires AArch64, + - PowerPC(VSX), + - MIPS(MSA), + - LoongArch(LSX), + - RISC-V(RVV 0.7.1): Fixed-length implementation, + - WASM: 64-bit float (64F) is not supported, +- *256 bit* registers are supported on + - x86(AVX2), + - LoongArch (LASX), +- *512 bit* registers are supported on + - x86(AVX512), +- *Vector Length Agnostic (VLA)* registers are supported on + - RISC-V(RVV 1.0) + - ARM(SVE/SVE2): Powered by Arm KleidiCV integration (OpenCV 4.11+), + In case when there is no SIMD extension available during compilation, fallback C++ implementation of intrinsics will be chosen and code will work as expected although it could be slower. diff --git a/modules/core/include/opencv2/core/hal/intrin_neon.hpp b/modules/core/include/opencv2/core/hal/intrin_neon.hpp index 364164e607..9cf59bc778 100644 --- a/modules/core/include/opencv2/core/hal/intrin_neon.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_neon.hpp @@ -988,9 +988,10 @@ inline v_uint32x4 v_dotprod_expand_fast(const v_uint8x16& a, const v_uint8x16& b inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b) { - int16x8_t prod = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val)); - prod = vmlal_s8(prod, vget_high_s8(a.val), vget_high_s8(b.val)); - return v_int32x4(vaddl_s16(vget_low_s16(prod), vget_high_s16(prod))); + int16x8_t p0 = vmull_s8(vget_low_s8(a.val), vget_low_s8(b.val)); + int16x8_t p1 = vmull_s8(vget_high_s8(a.val), vget_high_s8(b.val)); + int32x4_t s0 = vaddl_s16(vget_low_s16(p0), vget_low_s16(p1)); + return v_int32x4(vaddq_s32(s0, vaddl_s16(vget_high_s16(p0), vget_high_s16(p1)))); } inline v_int32x4 v_dotprod_expand_fast(const v_int8x16& a, const v_int8x16& b, const v_int32x4& c) { diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index e8ec4095ce..45839ad3b2 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -1549,15 +1549,15 @@ public: /** @overload * @param cn New number of channels. If the parameter is 0, the number of channels remains the same. - * @param newndims New number of dimentions. - * @param newsz Array with new matrix size by all dimentions. If some sizes are zero, + * @param newndims New number of dimensions. + * @param newsz Array with new matrix size by all dimensions. If some sizes are zero, * the original sizes in those dimensions are presumed. */ Mat reshape(int cn, int newndims, const int* newsz) const; /** @overload * @param cn New number of channels. If the parameter is 0, the number of channels remains the same. - * @param newshape Vector with new matrix size by all dimentions. If some sizes are zero, + * @param newshape Vector with new matrix size by all dimensions. If some sizes are zero, * the original sizes in those dimensions are presumed. */ Mat reshape(int cn, const std::vector& newshape) const; diff --git a/modules/core/include/opencv2/core/quaternion.hpp b/modules/core/include/opencv2/core/quaternion.hpp index e39065020c..d8882f6a54 100644 --- a/modules/core/include/opencv2/core/quaternion.hpp +++ b/modules/core/include/opencv2/core/quaternion.hpp @@ -57,7 +57,7 @@ class QuatEnum public: /** @brief Enum of Euler angles type. * - * Without considering the possibility of using two different convertions for the definition of the rotation axes , + * Without considering the possibility of using two different conversions for the definition of the rotation axes , * there exists twelve possible sequences of rotation axes, divided into two groups: * - Proper Euler angles (Z-X-Z, X-Y-X, Y-Z-Y, Z-Y-Z, X-Z-X, Y-X-Y) * - Tait–Bryan angles (X-Y-Z, Y-Z-X, Z-X-Y, X-Z-Y, Z-Y-X, Y-X-Z). @@ -273,7 +273,7 @@ public: * where \f$ q_{X, \theta_1} \f$ is created from @ref createFromXRot, \f$ q_{Y, \theta_2} \f$ is created from @ref createFromYRot, * \f$ q_{Z, \theta_3} \f$ is created from @ref createFromZRot. * @param angles the Euler angles in a vector of length 3 - * @param eulerAnglesType the convertion Euler angles type + * @param eulerAnglesType the conversion Euler angles type */ static Quat<_Tp> createFromEulerAngles(const Vec<_Tp, 3> &angles, QuatEnum::EulerAnglesType eulerAnglesType); @@ -1610,7 +1610,7 @@ public: * EXT_ZXZ| \f$ \theta_1 = \arctan2(m_{31},m_{32}) \\\theta_2 = \arccos(m_{33}) \\\theta_3 = \arctan2(-m_{13},m_{23})\f$| \f$ \theta_1=0\\ \theta_2=0\\ \theta_3=\arctan2(m_{21},m_{22}) \f$| \f$ \theta_1= 0\\ \theta_2=\pi\\ \theta_3=\arctan2(m_{21},m_{11}) \f$ * EXT_ZYZ| \f$ \theta_1 = \arctan2(m_{32},-m_{31})\\\theta_2 = \arccos(m_{33}) \\\theta_3 = \arctan2(m_{23},m_{13}) \f$| \f$ \theta_1=0\\ \theta_2=0\\ \theta_3=\arctan2(m_{21},m_{11}) \f$| \f$ \theta_1= 0\\ \theta_2=\pi\\ \theta_3=\arctan2(m_{21},m_{11}) \f$ * - * @param eulerAnglesType the convertion Euler angles type + * @param eulerAnglesType the conversion Euler angles type */ Vec<_Tp, 3> toEulerAngles(QuatEnum::EulerAnglesType eulerAnglesType); diff --git a/modules/core/misc/java/src/java/core+Mat.java b/modules/core/misc/java/src/java/core+Mat.java index 8e98284dde..dff33760cd 100644 --- a/modules/core/misc/java/src/java/core+Mat.java +++ b/modules/core/misc/java/src/java/core+Mat.java @@ -4,14 +4,10 @@ import java.nio.ByteBuffer; // C++: class Mat //javadoc: Mat -public class Mat { - - public final long nativeObj; +public class Mat extends CleanableMat { public Mat(long addr) { - if (addr == 0) - throw new UnsupportedOperationException("Native object address is NULL"); - nativeObj = addr; + super(addr); } // @@ -20,7 +16,7 @@ public class Mat { // javadoc: Mat::Mat() public Mat() { - nativeObj = n_Mat(); + super(n_Mat()); } // @@ -29,7 +25,7 @@ public class Mat { // javadoc: Mat::Mat(rows, cols, type) public Mat(int rows, int cols, int type) { - nativeObj = n_Mat(rows, cols, type); + super(n_Mat(rows, cols, type)); } // @@ -38,7 +34,7 @@ public class Mat { // javadoc: Mat::Mat(rows, cols, type, data) public Mat(int rows, int cols, int type, ByteBuffer data) { - nativeObj = n_Mat(rows, cols, type, data); + super(n_Mat(rows, cols, type, data)); } // @@ -47,7 +43,7 @@ public class Mat { // javadoc: Mat::Mat(rows, cols, type, data, step) public Mat(int rows, int cols, int type, ByteBuffer data, long step) { - nativeObj = n_Mat(rows, cols, type, data, step); + super(n_Mat(rows, cols, type, data, step)); } // @@ -56,7 +52,7 @@ public class Mat { // javadoc: Mat::Mat(size, type) public Mat(Size size, int type) { - nativeObj = n_Mat(size.width, size.height, type); + super(n_Mat(size.width, size.height, type)); } // @@ -65,7 +61,7 @@ public class Mat { // javadoc: Mat::Mat(sizes, type) public Mat(int[] sizes, int type) { - nativeObj = n_Mat(sizes.length, sizes, type); + super(n_Mat(sizes.length, sizes, type)); } // @@ -74,7 +70,7 @@ public class Mat { // javadoc: Mat::Mat(rows, cols, type, s) public Mat(int rows, int cols, int type, Scalar s) { - nativeObj = n_Mat(rows, cols, type, s.val[0], s.val[1], s.val[2], s.val[3]); + super(n_Mat(rows, cols, type, s.val[0], s.val[1], s.val[2], s.val[3])); } // @@ -83,7 +79,7 @@ public class Mat { // javadoc: Mat::Mat(size, type, s) public Mat(Size size, int type, Scalar s) { - nativeObj = n_Mat(size.width, size.height, type, s.val[0], s.val[1], s.val[2], s.val[3]); + super(n_Mat(size.width, size.height, type, s.val[0], s.val[1], s.val[2], s.val[3])); } // @@ -92,7 +88,7 @@ public class Mat { // javadoc: Mat::Mat(sizes, type, s) public Mat(int[] sizes, int type, Scalar s) { - nativeObj = n_Mat(sizes.length, sizes, type, s.val[0], s.val[1], s.val[2], s.val[3]); + super(n_Mat(sizes.length, sizes, type, s.val[0], s.val[1], s.val[2], s.val[3])); } // @@ -101,12 +97,12 @@ public class Mat { // javadoc: Mat::Mat(m, rowRange, colRange) public Mat(Mat m, Range rowRange, Range colRange) { - nativeObj = n_Mat(m.nativeObj, rowRange.start, rowRange.end, colRange.start, colRange.end); + super(n_Mat(m.nativeObj, rowRange.start, rowRange.end, colRange.start, colRange.end)); } // javadoc: Mat::Mat(m, rowRange) public Mat(Mat m, Range rowRange) { - nativeObj = n_Mat(m.nativeObj, rowRange.start, rowRange.end); + super(n_Mat(m.nativeObj, rowRange.start, rowRange.end)); } // @@ -115,7 +111,7 @@ public class Mat { // javadoc: Mat::Mat(m, ranges) public Mat(Mat m, Range[] ranges) { - nativeObj = n_Mat(m.nativeObj, ranges); + super(n_Mat(m.nativeObj, ranges)); } // @@ -124,7 +120,7 @@ public class Mat { // javadoc: Mat::Mat(m, roi) public Mat(Mat m, Rect roi) { - nativeObj = n_Mat(m.nativeObj, roi.y, roi.y + roi.height, roi.x, roi.x + roi.width); + super(n_Mat(m.nativeObj, roi.y, roi.y + roi.height, roi.x, roi.x + roi.width)); } // @@ -754,12 +750,6 @@ public class Mat { return new Mat(n_zeros(sizes.length, sizes, type)); } - @Override - protected void finalize() throws Throwable { - n_delete(nativeObj); - super.finalize(); - } - // javadoc:Mat::toString() @Override public String toString() { @@ -1834,9 +1824,6 @@ public class Mat { // C++: static Mat Mat::zeros(int ndims, const int* sizes, int type) private static native long n_zeros(int ndims, int[] sizes, int type); - // native support for java finalize() - private static native void n_delete(long nativeObj); - private static native int nPutD(long self, int row, int col, int count, double[] data); private static native int nPutDIdx(long self, int[] idx, int count, double[] data); diff --git a/modules/core/src/arithm_ipp.hpp b/modules/core/src/arithm_ipp.hpp index ed722113a7..33ade50918 100644 --- a/modules/core/src/arithm_ipp.hpp +++ b/modules/core/src/arithm_ipp.hpp @@ -283,24 +283,40 @@ inline IppCmpOp arithm_ipp_convert_cmp(int cmpop) inline int arithm_ipp_cmp8u(const uchar* src1, size_t step1, const uchar* src2, size_t step2, uchar* dst, size_t step, int width, int height, int cmpop) { + // perf regression with AVX512: https://github.com/opencv/opencv/issues/28251 + if (ippCPUID_AVX512F&cv::ipp::getIppFeatures()) + return 0; + ARITHM_IPP_CMP(ippiCompare_8u_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height)); } inline int arithm_ipp_cmp16u(const ushort* src1, size_t step1, const ushort* src2, size_t step2, uchar* dst, size_t step, int width, int height, int cmpop) { + // perf regression with AVX512: https://github.com/opencv/opencv/issues/28251 + if (ippCPUID_AVX512F&cv::ipp::getIppFeatures()) + return 0; + ARITHM_IPP_CMP(ippiCompare_16u_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height)); } inline int arithm_ipp_cmp16s(const short* src1, size_t step1, const short* src2, size_t step2, uchar* dst, size_t step, int width, int height, int cmpop) { + // perf regression with AVX512: https://github.com/opencv/opencv/issues/28251 + if (ippCPUID_AVX512F&cv::ipp::getIppFeatures()) + return 0; + ARITHM_IPP_CMP(ippiCompare_16s_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height)); } inline int arithm_ipp_cmp32f(const float* src1, size_t step1, const float* src2, size_t step2, uchar* dst, size_t step, int width, int height, int cmpop) { + // perf regression with AVX512: https://github.com/opencv/opencv/issues/28251 + if (ippCPUID_AVX512F&cv::ipp::getIppFeatures()) + return 0; + ARITHM_IPP_CMP(ippiCompare_32f_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height)); } diff --git a/modules/core/src/hal_internal.cpp b/modules/core/src/hal_internal.cpp index 38b2cdeff2..1444f91084 100644 --- a/modules/core/src/hal_internal.cpp +++ b/modules/core/src/hal_internal.cpp @@ -69,10 +69,17 @@ #include #define CV_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ __msan_unpoison(address, size) +#define CV_ANNOTATE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) #endif #endif #ifndef CV_ANNOTATE_MEMORY_IS_INITIALIZED #define CV_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) do { } while(0) +#define CV_ANNOTATE_NO_SANITIZE_MEMORY +#endif + +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif //lapack stores matrices in column-major order so transposing is needed everywhere @@ -108,8 +115,9 @@ set_value(fptype *dst, size_t dst_ld, fptype value, size_t m, size_t n) dst[i*dst_ld + j] = value; } +// MSAN can't see that the fortran LAPACK functions initialize `info` template static inline int -lapack_LU(fptype* a, size_t a_step, int m, fptype* b, size_t b_step, int n, int* info) +CV_ANNOTATE_NO_SANITIZE_MEMORY lapack_LU(fptype* a, size_t a_step, int m, fptype* b, size_t b_step, int n, int* info) { #if defined (ACCELERATE_NEW_LAPACK) && defined (ACCELERATE_LAPACK_ILP64) cv::AutoBuffer piv_buff(m); @@ -710,4 +718,8 @@ int lapack_gemm64fc(const double *src1, size_t src1_step, const double *src2, si return lapack_gemm_c(src1, src1_step, src2, src2_step, alpha, src3, src3_step, beta, dst, dst_step, m, n, k, flags); } -#endif //HAVE_LAPACK +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif //HAVE_LAPACK \ No newline at end of file diff --git a/modules/core/src/mathfuncs.cpp b/modules/core/src/mathfuncs.cpp index ae172b5b78..95a906013f 100644 --- a/modules/core/src/mathfuncs.cpp +++ b/modules/core/src/mathfuncs.cpp @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include "mathfuncs.hpp" namespace cv @@ -1423,12 +1425,21 @@ int cv::solveCubic( InputArray _coeffs, OutputArray _roots ) a3 = coeffs.at(i+3); } - if( a0 == 0 ) + // Fix for Issue #27748: Normalize coefficients to avoid overflow/underflow + // and correctly detect negligible cubic terms. + double max_coeff = std::max({std::abs(a0), std::abs(a1), std::abs(a2), std::abs(a3)}); + + // If max_coeff is effectively zero, the equation is 0=0 + if (max_coeff < std::numeric_limits::epsilon()) + return -1; + + // Check if the cubic term is negligible relative to the largest coefficient + if( std::abs(a0) < std::numeric_limits::epsilon() * max_coeff ) { - if( a1 == 0 ) + if( std::abs(a1) < std::numeric_limits::epsilon() * max_coeff ) { - if( a2 == 0 ) // constant - n = a3 == 0 ? -1 : 0; + if( std::abs(a2) < std::numeric_limits::epsilon() * max_coeff ) + n = std::abs(a3) < std::numeric_limits::epsilon() * max_coeff ? -1 : 0; else { // linear equation @@ -1445,7 +1456,7 @@ int cv::solveCubic( InputArray _coeffs, OutputArray _roots ) d = std::sqrt(d); double q1 = (-a2 + d) * 0.5; double q2 = (a2 + d) * -0.5; - if( fabs(q1) > fabs(q2) ) + if( std::abs(q1) > std::abs(q2) ) { x0 = q1 / a1; x1 = a3 / q1; @@ -1462,6 +1473,13 @@ int cv::solveCubic( InputArray _coeffs, OutputArray _roots ) else { // cubic equation + // Normalize coefficients in-place to prevent overflow/instability + double scale = 1.0 / max_coeff; + a0 *= scale; + a1 *= scale; + a2 *= scale; + a3 *= scale; + a0 = 1./a0; a1 *= a0; a2 *= a0; @@ -1470,6 +1488,7 @@ int cv::solveCubic( InputArray _coeffs, OutputArray _roots ) double Q = (a1 * a1 - 3 * a2) * (1./9); double R = (a1 * (2 * a1 * a1 - 9 * a2) + 27 * a3) * (1./54); double Qcubed = Q * Q * Q; + /* Here we expand expression `Qcubed - R * R` for `d` variable to reduce common terms `a1^6 / 729` and `-a1^4 * a2 / 81` diff --git a/modules/core/src/opencl/runtime/opencl_core.cpp b/modules/core/src/opencl/runtime/opencl_core.cpp index 37ab5f104c..65557537b4 100644 --- a/modules/core/src/opencl/runtime/opencl_core.cpp +++ b/modules/core/src/opencl/runtime/opencl_core.cpp @@ -93,7 +93,7 @@ static void* AppleCLGetProcAddress(const char* name) handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL); if (handle == NULL) { - if (path != NULL && path != defaultPath) + if (!path.empty() && path != defaultPath) fprintf(stderr, ERROR_MSG_CANT_LOAD); } else if (dlsym(handle, OPENCL_FUNC_TO_CHECK_1_1) == NULL) diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index b1abaad678..e130c1c468 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -465,10 +465,6 @@ namespace { static inline int _initMaxThreads() { int maxThreads = omp_get_max_threads(); - if (!utils::getConfigurationParameterBool("OPENCV_FOR_OPENMP_DYNAMIC_DISABLE", false)) - { - omp_set_dynamic(1); - } return maxThreads; } static int numThreadsMax = _initMaxThreads(); diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index a8582e672e..0b776d4817 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -179,6 +179,7 @@ const uint64_t AT_HWCAP = NT_GNU_HWCAP; #define _WIN32_WINNT 0x0400 // http://msdn.microsoft.com/en-us/library/ms686857(VS.85).aspx #endif #include +#include #if (_WIN32_WINNT >= 0x0602) #include #endif @@ -478,6 +479,7 @@ struct HWFeatures g_hwFeatureNames[CPU_NEON_DOTPROD] = "NEON_DOTPROD"; g_hwFeatureNames[CPU_NEON_FP16] = "NEON_FP16"; g_hwFeatureNames[CPU_NEON_BF16] = "NEON_BF16"; + g_hwFeatureNames[CPU_SVE] = "SVE"; g_hwFeatureNames[CPU_VSX] = "VSX"; g_hwFeatureNames[CPU_VSX3] = "VSX3"; @@ -641,6 +643,7 @@ struct HWFeatures { have[CV_CPU_NEON_DOTPROD] = (auxv.a_un.a_val & (1 << 20)) != 0; // HWCAP_ASIMDDP have[CV_CPU_NEON_FP16] = (auxv.a_un.a_val & (1 << 10)) != 0; // HWCAP_ASIMDHP + have[CV_CPU_SVE] = (auxv.a_un.a_val & (1 << 22)) != 0; // HWCAP_SVE } #if defined(AT_HWCAP2) else if (auxv.a_type == AT_HWCAP2) diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index b524da43f0..20f5318997 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -1037,6 +1037,14 @@ static void flip(const Mat& src, Mat& dst, int flipcode) } } +static void flip_inplace(Mat& dst, int flipcode) +{ + Mat m; + m.create(dst.size(), dst.type()); + reference::flip(dst, m, flipcode); + memcpy(dst.ptr(), m.ptr(), dst.total() * dst.elemSize()); +} + static void rotate(const Mat& src, Mat& dst, int rotateMode) { Mat tmp; @@ -1103,6 +1111,36 @@ struct FlipOp : public BaseElemWiseOp int flipcode; }; +struct FlipInplaceOp : public BaseElemWiseOp +{ + FlipInplaceOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) { flipcode = 0; } + void getRandomSize(RNG& rng, vector& size) + { + cvtest::randomSize(rng, 2, 2, ARITHM_MAX_SIZE_LOG, size); + } + void op(const vector& src, Mat& dst, const Mat&) + { + dst.create(src[0].size(), src[0].type()); + memcpy(dst.ptr(), src[0].ptr(), src[0].total() * src[0].elemSize()); + cv::flip(dst, dst, flipcode); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + dst.create(src[0].size(), src[0].type()); + memcpy(dst.ptr(), src[0].ptr(), src[0].total() * src[0].elemSize()); + reference::flip_inplace(dst, flipcode); + } + void generateScalars(int, RNG& rng) + { + flipcode = rng.uniform(0, 3) - 1; + } + double getMaxErr(int) + { + return 0; + } + int flipcode; +}; + struct RotateOp : public BaseElemWiseOp { RotateOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) { rotatecode = 0; } @@ -1789,6 +1827,7 @@ INSTANTIATE_TEST_CASE_P(Core_InRange, ElemWiseTest, ::testing::Values(ElemWiseOp INSTANTIATE_TEST_CASE_P(Core_FiniteMask, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new FiniteMaskOp))); INSTANTIATE_TEST_CASE_P(Core_Flip, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new FlipOp))); +INSTANTIATE_TEST_CASE_P(Core_FlipInplace, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new FlipInplaceOp))); INSTANTIATE_TEST_CASE_P(Core_Rotate, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new RotateOp))); INSTANTIATE_TEST_CASE_P(Core_Transpose, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new TransposeOp))); INSTANTIATE_TEST_CASE_P(Core_SetIdentity, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new SetIdentityOp))); diff --git a/modules/core/test/test_math.cpp b/modules/core/test/test_math.cpp index 9242070339..12e20397dc 100644 --- a/modules/core/test/test_math.cpp +++ b/modules/core/test/test_math.cpp @@ -1917,6 +1917,33 @@ TEST(Core_SolveCubic, regression_27323) } } +TEST(Core_SolveCubic, regression_27748) +{ + // a is extremely small relative to others (approx 1.8e-19 ratio), + // causing instability in standard cubic formula. + double a = 1.56041e-17; + double b = 84.4504; + double c = -96.795; + double d = 13.6826; + + Mat coeffs = (Mat_(1, 4) << a, b, c, d); + Mat roots; + + int n = solveCubic(coeffs, roots); + + // Expecting quadratic behavior (2 roots) + EXPECT_GE(n, 2); + + // Verify roots satisfy the FULL cubic equation + for(int i = 0; i < n; i++) + { + double x = roots.at(i); + double val = a*x*x*x + b*x*x + c*x + d; + // Check residual is small + EXPECT_LE(fabs(val), 1e-6) << "Root " << x << " does not satisfy the equation"; + } +} + TEST(Core_SolvePoly, regression_5599) { // x^4 - x^2 = 0, roots: 1, -1, 0, 0 diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index fcb2e6374e..62c769a4ab 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -4,7 +4,7 @@ endif() set(the_description "Deep neural network module. It allows to load models from different frameworks and to make forward pass") -ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX RVV LASX NEON) +ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX RVV LASX NEON SVE) ocv_add_dispatched_file_force_all("int8layers/layers_common" AVX2 AVX512_SKX RVV LASX NEON) ocv_add_dispatched_file_force_all("layers/cpu_kernels/conv_block" AVX AVX2 NEON NEON_FP16) ocv_add_dispatched_file_force_all("layers/cpu_kernels/conv_depthwise" AVX AVX2 RVV LASX) diff --git a/modules/dnn/include/opencv2/dnn/version.hpp b/modules/dnn/include/opencv2/dnn/version.hpp index 9e5a56f0c9..7dd58bb22c 100644 --- a/modules/dnn/include/opencv2/dnn/version.hpp +++ b/modules/dnn/include/opencv2/dnn/version.hpp @@ -6,7 +6,7 @@ #define OPENCV_DNN_VERSION_HPP /// Use with major OpenCV version only. -#define OPENCV_DNN_API_VERSION 20250619 +#define OPENCV_DNN_API_VERSION 20251223 #if !defined CV_DOXYGEN && !defined CV_STATIC_ANALYSIS && !defined CV_DNN_DONT_ADD_INLINE_NS #define CV__DNN_INLINE_NS __CV_CAT(dnn5_v, OPENCV_DNN_API_VERSION) diff --git a/modules/dnn/src/dnn_utils.cpp b/modules/dnn/src/dnn_utils.cpp index 87561d0e50..b6f1d3fcce 100644 --- a/modules/dnn/src/dnn_utils.cpp +++ b/modules/dnn/src/dnn_utils.cpp @@ -228,7 +228,7 @@ void blobFromImagesNCHW(const std::vector& images, Mat& blob_, const Image2 template void blobFromImagesNCHW(const std::vector& images, UMat& blob_, const Image2BlobParams& param) { - CV_Error(Error::StsNotImplemented, ""); + CV_Error(Error::StsNotImplemented, "blobFromImagesNCHW is not implemented for UMat inputs"); } template diff --git a/modules/dnn/src/layers/const_layer.cpp b/modules/dnn/src/layers/const_layer.cpp index 2eeee974b0..2f4e2113b4 100644 --- a/modules/dnn/src/layers/const_layer.cpp +++ b/modules/dnn/src/layers/const_layer.cpp @@ -116,14 +116,14 @@ public: { case CV_32F: break; case CV_32S: ge_dtype = ge::DT_INT32; break; - default: CV_Error(Error::StsNotImplemented, "Unsuppported data type"); + default: CV_Error(Error::StsNotImplemented, "Unsupported data type"); } auto size_of_type = sizeof(float); switch (blobs[0].type()) { case CV_32F: break; case CV_32S: size_of_type = sizeof(int); break; - default: CV_Error(Error::StsNotImplemented, "Unsuppported data type"); + default: CV_Error(Error::StsNotImplemented, "Unsupported data type"); } auto desc = std::make_shared(ge_shape, ge::FORMAT_NCHW, ge_dtype); diff --git a/modules/dnn/src/layers/fully_connected_layer.cpp b/modules/dnn/src/layers/fully_connected_layer.cpp index 6818d4851c..26dbf77e68 100644 --- a/modules/dnn/src/layers/fully_connected_layer.cpp +++ b/modules/dnn/src/layers/fully_connected_layer.cpp @@ -227,6 +227,7 @@ public: p.useAVX512 = CV_CPU_HAS_SUPPORT_AVX512_SKX; p.useRVV = checkHardwareSupport(CPU_RVV); p.useLASX = checkHardwareSupport(CPU_LASX); + p.useSVE = checkHardwareSupport(CPU_SVE); parallel_for_(Range(0, nstripes), p, nstripes); } @@ -276,6 +277,12 @@ public: opt_AVX::fastGEMM1T( sptr, wptr, wstep, biasptr, dptr, nw, vecsize_aligned); else #endif + #if CV_TRY_SVE + if( useSVE ) { + opt_SVE::fastGEMM1T( sptr, wptr, wstep, biasptr, dptr, nw, vecsize_aligned); + } + else + #endif #if CV_TRY_RVV && CV_RVV if( useRVV ) opt_RVV::fastGEMM1T( sptr, wptr, wstep, biasptr, dptr, nw, vecsize); @@ -341,6 +348,7 @@ public: bool useAVX512; bool useRVV; bool useLASX; + bool useSVE; }; #ifdef HAVE_OPENCL diff --git a/modules/dnn/src/layers/instance_norm_layer.cpp b/modules/dnn/src/layers/instance_norm_layer.cpp index ae61f15656..d695ca12f4 100644 --- a/modules/dnn/src/layers/instance_norm_layer.cpp +++ b/modules/dnn/src/layers/instance_norm_layer.cpp @@ -221,7 +221,7 @@ public: #ifdef HAVE_DNN_NGRAPH virtual Ptr initNgraph(const std::vector >& inputs, const std::vector >& nodes) CV_OVERRIDE { - // onnx to openvino convertion: https://github.com/openvinotoolkit/openvino/blob/2023.1.0/src/frontends/onnx/frontend/src/op/instance_norm.cpp + // onnx to openvino conversion: https://github.com/openvinotoolkit/openvino/blob/2023.1.0/src/frontends/onnx/frontend/src/op/instance_norm.cpp auto ieInpNode = nodes[0].dynamicCast()->node; const auto &input_shape = ieInpNode.get_shape(); diff --git a/modules/dnn/src/layers/layers_common.simd.hpp b/modules/dnn/src/layers/layers_common.simd.hpp index 0da90156b6..8eb89a922b 100644 --- a/modules/dnn/src/layers/layers_common.simd.hpp +++ b/modules/dnn/src/layers/layers_common.simd.hpp @@ -53,7 +53,119 @@ void fastGEMM( const float* aptr, size_t astep, const float* bptr, size_t bstep, float* cptr, size_t cstep, int ma, int na, int nb ); -#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_NEON +#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && defined(CV_CPU_COMPILE_SVE) +#include +// dst = vec * weights^t + bias + +void fastGEMM1T( const float* vec, const float* weights, + size_t wstep, const float* bias, + float* dst, int nvecs, int vecsize ) +{ + svbool_t pg_all = svptrue_b32(); + int i = 0; + int vl = svcntw(); + for( ; i <= nvecs - 15; i += 15 ) + { + const float* wrow0 = weights + i * wstep; // base pointer for row i + // we will use wrow0 + k, wrow0 + wstep + k, etc + svfloat32_t vs0 = svdup_f32(0.0f), vs1 = svdup_f32(0.0f), + vs2 = svdup_f32(0.0f), vs3 = svdup_f32(0.0f), + vs4 = svdup_f32(0.0f), vs5 = svdup_f32(0.0f), + vs6 = svdup_f32(0.0f), vs7 = svdup_f32(0.0f), + vs8 = svdup_f32(0.0f), vs9 = svdup_f32(0.0f), + vs10 = svdup_f32(0.0f), vs11 = svdup_f32(0.0f), + vs12 = svdup_f32(0.0f), vs13 = svdup_f32(0.0f), + vs14 = svdup_f32(0.0f); + int k = 0; + for( ; k <= vecsize - vl; k += vl ) + { + // load input chunk + const float* vecptr = reinterpret_cast(vec) + k; + svfloat32_t v = svld1_f32(pg_all, vecptr); + // load weights from each of 15 rows at offset k + vs0 = svmla_f32_m(pg_all, vs0, svld1_f32(pg_all, wrow0 + k), v); + vs1 = svmla_f32_m(pg_all, vs1, svld1_f32(pg_all, wrow0 + wstep + k), v); + vs2 = svmla_f32_m(pg_all, vs2, svld1_f32(pg_all, wrow0 + wstep*2 + k), v); + vs3 = svmla_f32_m(pg_all, vs3, svld1_f32(pg_all, wrow0 + wstep*3 + k), v); + vs4 = svmla_f32_m(pg_all, vs4, svld1_f32(pg_all, wrow0 + wstep*4 + k), v); + vs5 = svmla_f32_m(pg_all, vs5, svld1_f32(pg_all, wrow0 + wstep*5 + k), v); + vs6 = svmla_f32_m(pg_all, vs6, svld1_f32(pg_all, wrow0 + wstep*6 + k), v); + vs7 = svmla_f32_m(pg_all, vs7, svld1_f32(pg_all, wrow0 + wstep*7 + k), v); + vs8 = svmla_f32_m(pg_all, vs8, svld1_f32(pg_all, wrow0 + wstep*8 + k), v); + vs9 = svmla_f32_m(pg_all, vs9, svld1_f32(pg_all, wrow0 + wstep*9 + k), v); + vs10 = svmla_f32_m(pg_all, vs10, svld1_f32(pg_all, wrow0 + wstep*10 + k), v); + vs11 = svmla_f32_m(pg_all, vs11, svld1_f32(pg_all, wrow0 + wstep*11 + k), v); + vs12 = svmla_f32_m(pg_all, vs12, svld1_f32(pg_all, wrow0 + wstep*12 + k), v); + vs13 = svmla_f32_m(pg_all, vs13, svld1_f32(pg_all, wrow0 + wstep*13 + k), v); + vs14 = svmla_f32_m(pg_all, vs14, svld1_f32(pg_all, wrow0 + wstep*14 + k), v); + } + if(k < vecsize){ + svbool_t pg_tail = svwhilelt_b32(k, vecsize); + const float* vecptr = reinterpret_cast(vec) + k; + svfloat32_t v = svld1_f32(pg_tail, vecptr); + const float* wptr = wrow0 + k; + vs0 = svmla_f32_m(pg_tail, vs0, svld1_f32(pg_tail, wptr), v); + vs1 = svmla_f32_m(pg_tail, vs1, svld1_f32(pg_tail, wptr + wstep), v); + vs2 = svmla_f32_m(pg_tail, vs2, svld1_f32(pg_tail, wptr + wstep*2), v); + vs3 = svmla_f32_m(pg_tail, vs3, svld1_f32(pg_tail, wptr + wstep*3), v); + vs4 = svmla_f32_m(pg_tail, vs4, svld1_f32(pg_tail, wptr + wstep*4), v); + vs5 = svmla_f32_m(pg_tail, vs5, svld1_f32(pg_tail, wptr + wstep*5), v); + vs6 = svmla_f32_m(pg_tail, vs6, svld1_f32(pg_tail, wptr + wstep*6), v); + vs7 = svmla_f32_m(pg_tail, vs7, svld1_f32(pg_tail, wptr + wstep*7), v); + vs8 = svmla_f32_m(pg_tail, vs8, svld1_f32(pg_tail, wptr + wstep*8), v); + vs9 = svmla_f32_m(pg_tail, vs9, svld1_f32(pg_tail, wptr + wstep*9), v); + vs10 = svmla_f32_m(pg_tail, vs10, svld1_f32(pg_tail, wptr + wstep*10), v); + vs11 = svmla_f32_m(pg_tail, vs11, svld1_f32(pg_tail, wptr + wstep*11), v); + vs12 = svmla_f32_m(pg_tail, vs12, svld1_f32(pg_tail, wptr + wstep*12), v); + vs13 = svmla_f32_m(pg_tail, vs13, svld1_f32(pg_tail, wptr + wstep*13), v); + vs14 = svmla_f32_m(pg_tail, vs14, svld1_f32(pg_tail, wptr + wstep*14), v); + } + float sum[15]; + sum[0] = svaddv_f32(pg_all, vs0); + + sum[1] = svaddv_f32(pg_all, vs1); + sum[2] = svaddv_f32(pg_all, vs2); + sum[3] = svaddv_f32(pg_all, vs3); + sum[4] = svaddv_f32(pg_all, vs4); + sum[5] = svaddv_f32(pg_all, vs5); + sum[6] = svaddv_f32(pg_all, vs6); + sum[7] = svaddv_f32(pg_all, vs7); + sum[8] = svaddv_f32(pg_all, vs8); + sum[9] = svaddv_f32(pg_all, vs9); + sum[10] = svaddv_f32(pg_all, vs10); + sum[11] = svaddv_f32(pg_all, vs11); + sum[12] = svaddv_f32(pg_all, vs12); + sum[13] = svaddv_f32(pg_all, vs13); + sum[14] = svaddv_f32(pg_all, vs14); + for (int j = 0; j < 15; j += vl) { + svbool_t pg = svwhilelt_b32(j, 15); + svfloat32_t v_sum = svld1_f32(pg, sum + j); + svfloat32_t v_bias = svld1_f32(pg, bias + i + j); + svst1_f32(pg, dst + i + j, svadd_f32_z(pg, v_sum, v_bias)); + } + } + float temp = 0.f; + for( ; i < nvecs; i++ ) + { + const float* wrow = weights + i * wstep; + svfloat32_t vs0 = svdup_f32(0.0f); + int k = 0; + for( ; k <= vecsize - vl; k += vl ) + { + svfloat32_t v = svld1_f32(pg_all, reinterpret_cast(vec) + k); + vs0 = svmla_f32_m(pg_all, vs0, svld1_f32(pg_all, wrow + k), v); + } + if (k != vecsize) { + svbool_t pg_tail = svwhilelt_b32(k, vecsize); + svfloat32_t v = svld1_f32(pg_tail, reinterpret_cast(vec) + k); + vs0 = svmla_f32_m(pg_tail, vs0, svld1_f32(pg_tail, wrow + k), v); + } + temp = svaddv_f32(pg_all, vs0); + dst[i] = temp + bias[i]; + } +} +#endif +#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_NEON && !defined(CV_CPU_COMPILE_SVE) static const uint32_t tailMaskArray[7] = { 0u, 0u, 0u, 0u, diff --git a/modules/dnn/src/layers/matmul_layer.cpp b/modules/dnn/src/layers/matmul_layer.cpp index 448af27c18..4d232b7a3e 100644 --- a/modules/dnn/src/layers/matmul_layer.cpp +++ b/modules/dnn/src/layers/matmul_layer.cpp @@ -423,7 +423,7 @@ class MatMulLayerImpl CV_FINAL : public MatMulLayer { op->set_input_x1_by_name(*input_A_node, input_A_wrapper->name.c_str()); op->update_input_desc_x1(*input_A_desc); // set inputs : x2 - if (blobs.empty()) { // varaible input B + if (blobs.empty()) { // variable input B auto input_B_wrapper = inputs[1].dynamicCast(); auto input_B_desc = input_B_wrapper->getTensorDesc(); auto input_B_node = nodes[1].dynamicCast()->getOp(); diff --git a/modules/dnn/src/layers/nary_eltwise_layers.cpp b/modules/dnn/src/layers/nary_eltwise_layers.cpp index b3f95a60f6..2b317d927a 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -548,8 +548,8 @@ public: int nplanes = std::accumulate(shape.begin(), shape.end() - 1, 1, std::multiplies()); if (nplanes == 1) { // parallelize within the plane - AutoBuffer buf_ptrs(steps.size()); - auto ptrs = (char**)buf_ptrs.data(); + AutoBuffer buf_ptrs(steps.size()); + char** ptrs = buf_ptrs.data(); ptrs[0] = out; for (int i = 0; i < ninputs; i++) { ptrs[i+1] = (char*)inp[i]; @@ -594,8 +594,8 @@ public: parallel_for_(Range(0, plane_size), worker, nstripes); } else { // parallelize across the plane auto worker = [&](const Range &r) { - AutoBuffer buf_ptrs(steps.size()); - auto ptrs = (char**)buf_ptrs.data(); + AutoBuffer buf_ptrs(steps.size()); + char** ptrs = buf_ptrs.data(); for (int plane_idx = r.start; plane_idx < r.end; plane_idx++) { ptrs[0] = out; for (int i = 0; i < ninputs; i++) ptrs[i+1] = (char*)inp[i]; @@ -1318,7 +1318,7 @@ public: node = std::make_shared(inp0, inp1, inp2); } // Ideally we should do this but int32 internal blobs are converted to float32 data type in inference. - // TODO: Remove data type convertion when we have type inference. + // TODO: Remove data type conversion when we have type inference. else if (op == OPERATION::MOD) { auto inp0_i64 = std::make_shared(inp0, ov::element::i64); auto inp1_i64 = std::make_shared(inp1, ov::element::i64); diff --git a/modules/dnn/src/layers/recurrent_layers.cpp b/modules/dnn/src/layers/recurrent_layers.cpp index 0c9e51f3ab..03232edf40 100644 --- a/modules/dnn/src/layers/recurrent_layers.cpp +++ b/modules/dnn/src/layers/recurrent_layers.cpp @@ -138,6 +138,9 @@ class LSTMLayerImpl CV_FINAL : public LSTMLayer #if CV_TRY_AVX2 bool useAVX2; #endif +#if CV_TRY_SVE + bool useSVE; +#endif #if CV_TRY_NEON bool useNEON; #endif @@ -156,6 +159,9 @@ public: #if CV_TRY_AVX2 , useAVX2(checkHardwareSupport(CPU_AVX2)) #endif +#if CV_TRY_SVE + , useSVE(checkHardwareSupport(CPU_SVE)) +#endif #if CV_TRY_NEON , useNEON(checkHardwareSupport(CPU_NEON)) #endif @@ -410,7 +416,7 @@ public: if (layout == BATCH_SEQ_HID){ //swap axis 0 and 1 input x cv::Mat tmp; - // Since python input is 4 dimentional and C++ input 3 dimentinal + // Since python input is 4 dimensional and C++ input 3 dimensional // we need to process each differently if (input[0].dims == 4){ // here !!! @@ -495,6 +501,13 @@ public: && Wh.depth() == CV_32F && hInternal.depth() == CV_32F && gates.depth() == CV_32F && Wh.cols >= 8; #endif +#if CV_TRY_SVE + bool canUseSVE = gates.isContinuous() && bias.isContinuous() + && Wx.depth() == CV_32F && gates.depth() == CV_32F + && bias.depth() == CV_32F; + bool canUseSVE_hInternal = hInternal.isContinuous() && gates.isContinuous() && bias.isContinuous() + && Wh.depth() == CV_32F && hInternal.depth() == CV_32F && gates.depth() == CV_32F; +#endif #if CV_TRY_NEON bool canUseNeon = gates.isContinuous() && bias.isContinuous() && Wx.depth() == CV_32F && gates.depth() == CV_32F @@ -554,6 +567,23 @@ public: } else #endif +#if CV_TRY_SVE + if (useSVE && canUseSVE && xCurr.isContinuous()) + { + for (int n = 0; n < xCurr.rows; n++) { + opt_SVE::fastGEMM1T( + xCurr.ptr(n), + Wx.ptr(), + Wx.step1(), + bias.ptr(), + gates.ptr(n), + Wx.rows, + Wx.cols + ); + } + } + else +#endif #if CV_TRY_NEON if (useNEON && canUseNeon && xCurr.isContinuous()) { @@ -610,6 +640,23 @@ public: } else #endif +#if CV_TRY_SVE + if (useSVE && canUseSVE_hInternal) + { + for (int n = 0; n < hInternal.rows; n++) { + opt_SVE::fastGEMM1T( + hInternal.ptr(n), + Wh.ptr(), + Wh.step1(), + gates.ptr(n), + gates.ptr(n), + Wh.rows, + Wh.cols + ); + } + } + else +#endif #if CV_TRY_NEON if (useNEON && canUseNeon_hInternal) { diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 3a9a7f8a91..a56bc3831f 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -194,6 +194,7 @@ private: void parseElementWise (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseDepthSpaceOps (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseRange (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); + void parseRandomNormalLike (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseScatter (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseTile (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseLayerNorm (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); @@ -2046,6 +2047,25 @@ void ONNXImporter::parseConv(LayerParams& layerParams, const opencv_onnx::NodePr layerParams.blobs.push_back(getBlob(node_proto, j)); } } + // ONNX allows omitting 'kernel_shape' attribute for Conv. In that case, it should be inferred from weights. + // See: https://onnx.ai/onnx/operators/onnx__Conv.html + if (!layerParams.has("kernel_size")) + { + Mat weights; + if (!layerParams.blobs.empty()) + weights = layerParams.blobs[0]; + else if (constBlobs.find(node_proto.input(1)) != constBlobs.end()) + weights = getBlob(node_proto, 1); + + if (!weights.empty() && weights.dims >= 3) + { + const int kDims = weights.dims - 2; + std::vector kernel(kDims); + for (int i = 0; i < kDims; ++i) + kernel[i] = weights.size[2 + i]; + layerParams.set("kernel_size", DictValue::arrayInt(kernel.data(), static_cast(kernel.size()))); + } + } int outCn = layerParams.blobs.empty() ? outShapes[node_proto.input(1)][0] : layerParams.blobs[0].size[0]; layerParams.set("num_output", outCn); @@ -2062,6 +2082,20 @@ void ONNXImporter::parseConvTranspose(LayerParams& layerParams, const opencv_onn layerParams.set("num_output", layerParams.blobs[0].size[1] * layerParams.get("group", 1)); layerParams.set("bias_term", node_proto.input_size() == 3); + // ONNX allows omitting 'kernel_shape' attribute for ConvTranspose. Infer it from weights if needed. + if (!layerParams.has("kernel_size")) + { + const Mat& weights = layerParams.blobs[0]; + if (!weights.empty() && weights.dims >= 3) + { + const int kDims = weights.dims - 2; + std::vector kernel(kDims); + for (int i = 0; i < kDims; ++i) + kernel[i] = weights.size[2 + i]; + layerParams.set("kernel_size", DictValue::arrayInt(kernel.data(), static_cast(kernel.size()))); + } + } + if (!layerParams.has("kernel_size")) CV_Error(Error::StsNotImplemented, "Required attribute 'kernel_size' is not present."); @@ -3006,6 +3040,14 @@ void ONNXImporter::parseRange(LayerParams& layerParams, const opencv_onnx::NodeP constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), TensorInfo(1))); } +void ONNXImporter::parseRandomNormalLike(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +{ + CV_CheckEQ(node_proto.input_size(), 1, "RandomNormalLike: one input is required"); + + layerParams.type = "RandomNormalLike"; + addLayer(layerParams, node_proto); +} + void ONNXImporter::parseScatter(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { CV_CheckEQ(node_proto.input_size(), 3, "Scatter: three inputs are required."); @@ -4022,6 +4064,7 @@ void ONNXImporter::buildDispatchMap_ONNX_AI() dispatch["Sum"] = dispatch["Min"] = dispatch["Max"] = dispatch["Mean"] = &ONNXImporter::parseElementWise; dispatch["Where"] = &ONNXImporter::parseElementWise; dispatch["Range"] = &ONNXImporter::parseRange; + dispatch["RandomNormalLike"] = &ONNXImporter::parseRandomNormalLike; dispatch["Einsum"] = &ONNXImporter::parseEinsum; dispatch["Hardmax"] = &ONNXImporter::parseHardmax; dispatch["GatherND"] = &ONNXImporter::parseGatherND; diff --git a/modules/dnn/src/tflite/tflite_importer.cpp b/modules/dnn/src/tflite/tflite_importer.cpp index e7f447109f..8e495ec27e 100644 --- a/modules/dnn/src/tflite/tflite_importer.cpp +++ b/modules/dnn/src/tflite/tflite_importer.cpp @@ -334,7 +334,8 @@ TFLiteImporter::DispatchMap TFLiteImporter::buildDispatchMap() dispatch["DEPTHWISE_CONV_2D"] = &TFLiteImporter::parseDWConvolution; dispatch["ADD"] = dispatch["MUL"] = dispatch["SUB"] = dispatch["SQRT"] = dispatch["DIV"] = dispatch["NEG"] = - dispatch["RSQRT"] = dispatch["SQUARED_DIFFERENCE"] = &TFLiteImporter::parseEltwise; + dispatch["RSQRT"] = dispatch["SQUARED_DIFFERENCE"] = + dispatch["MAXIMUM"] = dispatch["MINIMUM"]= &TFLiteImporter::parseEltwise; dispatch["RELU"] = dispatch["PRELU"] = dispatch["HARD_SWISH"] = dispatch["LOGISTIC"] = dispatch["LEAKY_RELU"] = &TFLiteImporter::parseActivation; dispatch["MAX_POOL_2D"] = dispatch["AVERAGE_POOL_2D"] = &TFLiteImporter::parsePooling; @@ -681,7 +682,13 @@ void TFLiteImporter::parseEltwise(const Operator& op, const std::string& opcode, } else if (opcode == "SQRT" && !isOpInt8) { layerParams.type = "Sqrt"; - } else { + } + else if (opcode == "MAXIMUM" && !isOpInt8) { + layerParams.set("operation", "max"); + } + else if (opcode == "MINIMUM" && !isOpInt8) { + layerParams.set("operation", "min"); + }else { CV_Error(Error::StsNotImplemented, cv::format("DNN/TFLite: Unknown opcode for %s Eltwise layer '%s'", isOpInt8 ? "INT8" : "FP32", opcode.c_str())); } diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 9185ea6830..eeb0416cc7 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -3465,6 +3465,51 @@ TEST_P(Test_ONNX_layers, TopK) { test("top_k_smallest"); } +// BUG: https://github.com/opencv/opencv/issues/28532 +TEST_P(Test_ONNX_layers, DISABLED_RandomNormalLike_basic) +{ + Net net = readNetFromONNX(findDataFile("dnn/onnx/models/random_normal_like.onnx", true)); + + Mat input(2, 3, CV_32F, Scalar(0)); + net.setInput(input); + Mat out = net.forward(); + + EXPECT_EQ(out.rows, 2); + EXPECT_EQ(out.cols, 3); + EXPECT_EQ(out.type(), CV_32F); + + double minVal, maxVal; + minMaxLoc(out, &minVal, &maxVal); + EXPECT_NE(minVal, 0.0); + EXPECT_NE(maxVal, 0.0); + EXPECT_NE(minVal, maxVal); + + Mat out2 = net.forward(); + EXPECT_EQ(countNonZero(out != out2), 0); +} + +// BUG: https://github.com/opencv/opencv/issues/28532 +TEST_P(Test_ONNX_layers, DISABLED_RandomNormalLike_complex) +{ + Net net = readNetFromONNX(findDataFile("dnn/onnx/models/random_normal_like_complex.onnx", true)); + + Mat input(2, 3, CV_32F, Scalar(0)); + net.setInput(input); + Mat out = net.forward(); + + EXPECT_EQ(out.rows, 2); + EXPECT_EQ(out.cols, 3); + EXPECT_EQ(out.type(), CV_32F); + + double minVal, maxVal; + minMaxLoc(out, &minVal, &maxVal); + EXPECT_NE(minVal, maxVal); + + net.setInput(input); + Mat out2 = net.forward(); + EXPECT_EQ(countNonZero(out != out2), 0); +} + INSTANTIATE_TEST_CASE_P(/**/, Test_ONNX_nets, dnnBackendsAndTargets()); }} // namespace diff --git a/modules/dnn/test/test_tflite_importer.cpp b/modules/dnn/test/test_tflite_importer.cpp index b4e89ee46c..c6259927c1 100644 --- a/modules/dnn/test/test_tflite_importer.cpp +++ b/modules/dnn/test/test_tflite_importer.cpp @@ -289,6 +289,62 @@ TEST_P(Test_TFLite, face_blendshapes) testModel("face_blendshapes", inp); } +TEST_P(Test_TFLite, maximum) +{ + Net net = readNetFromTFLite(findDataFile("dnn/tflite/maximum.tflite")); + + net.setPreferableBackend(backend); + net.setPreferableTarget(target); + + Mat input_x = blobFromNPY(findDataFile("dnn/tflite/maximum_input_x.npy")); + Mat input_y = blobFromNPY(findDataFile("dnn/tflite/maximum_input_y.npy")); + + net.setInput(input_x, "x"); + net.setInput(input_y, "y"); + + Mat out = net.forward(); + Mat ref = blobFromNPY(findDataFile("dnn/tflite/maximum_output.npy")); + + double l1 = 1e-5; + double lInf = 1e-4; + + if (target == DNN_TARGET_CUDA_FP16 || target == DNN_TARGET_OPENCL_FP16) + { + l1 = 1e-3; + lInf = 1e-3; + } + + normAssert(ref, out, "", l1, lInf); +} + +TEST_P(Test_TFLite, minimum) +{ + Net net = readNetFromTFLite(findDataFile("dnn/tflite/minimum.tflite")); + + net.setPreferableBackend(backend); + net.setPreferableTarget(target); + + Mat input_x = blobFromNPY(findDataFile("dnn/tflite/minimum_input_x.npy")); + Mat input_y = blobFromNPY(findDataFile("dnn/tflite/minimum_input_y.npy")); + + net.setInput(input_x, "x"); + net.setInput(input_y, "y"); + + Mat out = net.forward(); + Mat ref = blobFromNPY(findDataFile("dnn/tflite/minimum_output.npy")); + + double l1 = 1e-5; + double lInf = 1e-4; + + if (target == DNN_TARGET_CUDA_FP16 || target == DNN_TARGET_OPENCL_FP16) + { + l1 = 1e-3; + lInf = 1e-3; + } + + normAssert(ref, out, "", l1, lInf); +} + INSTANTIATE_TEST_CASE_P(/**/, Test_TFLite, dnnBackendsAndTargets()); }} // namespace diff --git a/modules/features/include/opencv2/features.hpp b/modules/features/include/opencv2/features.hpp index 4dc3cbacf4..a1f4729922 100644 --- a/modules/features/include/opencv2/features.hpp +++ b/modules/features/include/opencv2/features.hpp @@ -563,7 +563,7 @@ public: CV_WRAP static Ptr create( int maxCorners=1000, double qualityLevel=0.01, double minDistance=1, int blockSize=3, bool useHarrisDetector=false, double k=0.04 ); CV_WRAP static Ptr create( int maxCorners, double qualityLevel, double minDistance, - int blockSize, int gradiantSize, bool useHarrisDetector=false, double k=0.04 ); + int blockSize, int gradientSize, bool useHarrisDetector=false, double k=0.04 ); CV_WRAP virtual void setMaxFeatures(int maxFeatures) = 0; CV_WRAP virtual int getMaxFeatures() const = 0; @@ -645,6 +645,11 @@ public: CV_PROP_RW bool filterByConvexity; CV_PROP_RW float minConvexity, maxConvexity; + /** @brief Flag to enable contour collection. + If set to true, the detector will store the contours of the detected blobs in memory, + which can be retrieved after the detect() call using getBlobContours(). + @note Default value is false. + */ CV_PROP_RW bool collectContours; void read( const FileNode& fn ); @@ -658,6 +663,11 @@ public: CV_WRAP virtual SimpleBlobDetector::Params getParams() const = 0; CV_WRAP virtual String getDefaultName() const CV_OVERRIDE; + + /** @brief Returns the contours of the blobs detected during the last call to detect(). + @note The @ref Params::collectContours parameter must be set to true before calling + detect() for this method to return any data. + */ CV_WRAP virtual const std::vector >& getBlobContours() const = 0; }; diff --git a/modules/features/src/orb.cpp b/modules/features/src/orb.cpp index 2a1da9ecd1..b9dd44fd80 100644 --- a/modules/features/src/orb.cpp +++ b/modules/features/src/orb.cpp @@ -1036,7 +1036,11 @@ void ORB_Impl::detectAndCompute( InputArray _image, InputArray _mask, bool useOCL = false; #endif - Mat image = _image.getMat(), mask = _mask.getMat(); + Mat image = _image.getMat(), mask; + if (!_mask.empty()) + { + threshold(_mask, mask, 0, 255, THRESH_BINARY); + } if( image.type() != CV_8UC1 ) cvtColor(_image, image, COLOR_BGR2GRAY); @@ -1134,7 +1138,7 @@ void ORB_Impl::detectAndCompute( InputArray _image, InputArray _mask, } copyMakeBorder(currImg, extImg, border, border, border, border, - BORDER_REFLECT_101+BORDER_ISOLATED); + BORDER_REFLECT_101 + BORDER_ISOLATED); if (!mask.empty()) copyMakeBorder(currMask, extMask, border, border, border, border, BORDER_CONSTANT+BORDER_ISOLATED); diff --git a/modules/features/test/test_orb.cpp b/modules/features/test/test_orb.cpp index 89e2f7d78b..92fe8e9838 100644 --- a/modules/features/test/test_orb.cpp +++ b/modules/features/test/test_orb.cpp @@ -168,4 +168,31 @@ BIGDATA_TEST(Features2D_ORB, regression_opencv_python_537) // memory usage: ~3 ASSERT_NO_THROW(orbPtr->detectAndCompute(img, noArray(), kps, fv)); } +TEST(Features2D_ORB, MaskValue) +{ + Mat gray = imread(cvtest::findDataFile("features2d/tsukuba.png"), IMREAD_GRAYSCALE); + ASSERT_FALSE(gray.empty()); + + cv::Rect roi(gray.cols/4, gray.rows/4, gray.cols/2, gray.rows/2); + + Mat mask255 = Mat::zeros(gray.size(), CV_8UC1); + Mat mask1 = Mat::zeros(gray.size(), CV_8UC1); + mask255(roi).setTo(255); + mask1(roi).setTo(1); + + Ptr orb = cv::ORB::create(); + + vector keypoints_mask255, keypoints_mask1; + Mat descriptors_mask255, descriptors_mask1; + + orb->detectAndCompute(gray, mask255, keypoints_mask255, descriptors_mask255, false); + orb->detectAndCompute(gray, mask1, keypoints_mask1, descriptors_mask1, false); + + ASSERT_EQ(keypoints_mask255.size(), keypoints_mask1.size()) + << "Number of keypoints differs between mask values 255 and 1"; + + Mat diff = descriptors_mask255 != descriptors_mask1; + ASSERT_EQ(countNonZero(diff), 0); +} + }} // namespace diff --git a/modules/highgui/src/window_QT.cpp b/modules/highgui/src/window_QT.cpp index 02da00aa76..acb084d044 100644 --- a/modules/highgui/src/window_QT.cpp +++ b/modules/highgui/src/window_QT.cpp @@ -790,14 +790,17 @@ GuiReceiver::GuiReceiver() : bTimeOut(false), nb_windows(0) void GuiReceiver::isLastWindow() { - if (--nb_windows <= 0) + if (qApp->quitOnLastWindowClosed()) { - delete guiMainThread;//delete global_control_panel too - guiMainThread = NULL; - - if (doesExternalQAppExist) + if (--nb_windows <= 0) { - qApp->quit(); + delete guiMainThread; // delete global_control_panel too + guiMainThread = NULL; + + if (doesExternalQAppExist) + { + qApp->quit(); + } } } } diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 2ead18f6a1..466646d7c6 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -557,6 +557,7 @@ It also demonstrates how to save multiple images in a TIFF file: @param filename Name of the file. @param img (Mat or vector of Mat) Image or Images to be saved. @param params Format-specific parameters encoded as pairs (paramId_1, paramValue_1, paramId_2, paramValue_2, ... .) see cv::ImwriteFlags +@return true if the image is successfully written to the specified file; false otherwise. */ CV_EXPORTS_W bool imwrite( const String& filename, InputArray img, const std::vector& params = std::vector()); diff --git a/modules/imgcodecs/src/grfmt_avif.cpp b/modules/imgcodecs/src/grfmt_avif.cpp index 7624515a1a..a33150e894 100644 --- a/modules/imgcodecs/src/grfmt_avif.cpp +++ b/modules/imgcodecs/src/grfmt_avif.cpp @@ -211,6 +211,10 @@ bool AvifDecoder::readHeader() { return true; decoder_ = avifDecoderCreate(); + if (!decoder_) { + CV_Error(Error::StsNoMem, "Failed to create AVIF decoder"); + return false; + } decoder_->strictFlags = AVIF_STRICT_DISABLED; if (!m_buf.empty()) { CV_Assert(m_buf.type() == CV_8UC1); @@ -226,6 +230,11 @@ bool AvifDecoder::readHeader() { decoder_); OPENCV_AVIF_CHECK_STATUS(avifDecoderParse(decoder_), decoder_); + if (!decoder_->image) { + CV_Error(Error::StsParseError, "AVIF image is null after parsing"); + return false; + } + m_width = decoder_->image->width; m_height = decoder_->image->height; m_frame_count = decoder_->imageCount; @@ -380,7 +389,6 @@ bool AvifEncoder::writeanimation(const Animation& animation, ? AVIF_ADD_IMAGE_FLAG_SINGLE : AVIF_ADD_IMAGE_FLAG_NONE; std::vector images; - std::vector imgs_scaled; for (const cv::Mat &img : animation.frames) { CV_CheckType( img.type(), @@ -388,11 +396,17 @@ bool AvifEncoder::writeanimation(const Animation& animation, ((bit_depth == 10 || bit_depth == 12) && img.depth() == CV_16U), "AVIF only supports bit depth of 8 with CV_8U input or " "bit depth of 10 or 12 with CV_16U input"); + CV_Check(img.channels(), img.channels() == 1 || img.channels() == 3 || img.channels() == 4, "AVIF only supports 1, 3, 4 channels"); - images.emplace_back(ConvertToAvif(img, do_lossless, bit_depth, m_metadata)); + auto avifImg = ConvertToAvif(img, do_lossless, bit_depth, m_metadata); + if (!avifImg) { + CV_Error(Error::StsError, "Failed to convert Mat to AVIF image"); + return false; + } + images.emplace_back(std::move(avifImg)); } for (size_t i = 0; i < images.size(); i++) diff --git a/modules/imgcodecs/src/grfmt_exr.cpp b/modules/imgcodecs/src/grfmt_exr.cpp index a727b36a2f..e6f9961ec5 100644 --- a/modules/imgcodecs/src/grfmt_exr.cpp +++ b/modules/imgcodecs/src/grfmt_exr.cpp @@ -130,9 +130,6 @@ bool ExrDecoder::readHeader() m_file = new InputFile( m_filename.c_str() ); - if( !m_file ) // probably paranoid - return false; - m_datawindow = m_file->header().dataWindow(); m_width = m_datawindow.max.x - m_datawindow.min.x + 1; m_height = m_datawindow.max.y - m_datawindow.min.y + 1; diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index a505266a8b..e1e5d9a710 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -42,6 +42,7 @@ #include "precomp.hpp" +#include #include #ifdef HAVE_PNG @@ -364,7 +365,7 @@ bool PngDecoder::readHeader() m_color_type = color_type; m_bit_depth = bit_depth; - if (m_is_fcTL_loaded && ((long long int)x0 + w0 > m_width || (long long int)y0 + h0 > m_height || dop > 2 || bop > 1)) + if (m_is_fcTL_loaded && ((int64_t)x0 + w0 > m_width || (int64_t)y0 + h0 > m_height || dop > 2 || bop > 1)) return false; png_color_16p background_color; @@ -456,7 +457,7 @@ bool PngDecoder::readData( Mat& img ) if (dop == 2) memcpy(frameNext.getPixels(), frameCur.getPixels(), imagesize); - if (x0 + w0 > frameCur.getWidth() || y0 + h0 > frameCur.getHeight()) + if ((uint64_t)x0 + w0 > frameCur.getWidth() || (uint64_t)y0 + h0 > frameCur.getHeight()) return false; compose_frame(frameCur.getRows(), frameRaw.getRows(), bop, x0, y0, w0, h0, mat_cur); @@ -508,7 +509,7 @@ bool PngDecoder::readData( Mat& img ) dop = chunk.p[32]; bop = chunk.p[33]; - if (int(x0 + w0) > img.cols || int(y0 + h0) > img.rows || dop > 2 || bop > 1) + if ((int64_t)x0 + w0 > img.cols || (int64_t)y0 + h0 > img.rows || dop > 2 || bop > 1) { return false; } diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index a9364b59c6..06a3f3c3b0 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -995,7 +995,7 @@ bool TiffDecoder::readData( Mat& img ) break; default: - CV_LOG_ONCE_ERROR(NULL, "OpenCV TIFF(line " << __LINE__ << "): Unsupported convertion :" + CV_LOG_ONCE_ERROR(NULL, "OpenCV TIFF(line " << __LINE__ << "): Unsupported conversion :" << " bpp = " << bpp << " ncn = " << (int)ncn << " wanted_channels =" << wanted_channels ); break; diff --git a/modules/imgcodecs/test/test_exr.cpp b/modules/imgcodecs/test/test_exr.cpp index 4fd9596936..bb0bd1580d 100644 --- a/modules/imgcodecs/test/test_exr.cpp +++ b/modules/imgcodecs/test/test_exr.cpp @@ -213,7 +213,7 @@ TEST(Imgcodecs_EXR, read_YA_unchanged) ASSERT_FALSE(img.empty()); ASSERT_EQ(CV_32FC2, img.type()); - // Cannot test writing, 2 channel writing not suppported by loadsave + // Cannot test writing, 2 channel writing not supported by loadsave } TEST(Imgcodecs_EXR, read_YC_changeDepth) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 703b784281..02a3a84de8 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -532,149 +532,153 @@ enum HistCompMethods { /** the color conversion codes @see @ref imgproc_color_conversions +@note The source image (src) must be of an appropriate type for the desired color conversion. +- `[8U]` means to support `CV_8U` src type. +- `[16U]` means to support `CV_16U` src type. +- `[32F]` means to support `CV_32F` src type. @ingroup imgproc_color_conversions */ enum ColorConversionCodes { - COLOR_BGR2BGRA = 0, //!< add alpha channel to RGB or BGR image - COLOR_RGB2RGBA = COLOR_BGR2BGRA, + COLOR_BGR2BGRA = 0, //!< [8U/16U/32F] add alpha channel to RGB or BGR image + COLOR_RGB2RGBA = COLOR_BGR2BGRA, //!< [8U/16U/32F] - COLOR_BGRA2BGR = 1, //!< remove alpha channel from RGB or BGR image - COLOR_RGBA2RGB = COLOR_BGRA2BGR, + COLOR_BGRA2BGR = 1, //!< [8U/16U/32F] remove alpha channel from RGB or BGR image + COLOR_RGBA2RGB = COLOR_BGRA2BGR, //!< [8U/16U/32F] - COLOR_BGR2RGBA = 2, //!< convert between RGB and BGR color spaces (with or without alpha channel) - COLOR_RGB2BGRA = COLOR_BGR2RGBA, + COLOR_BGR2RGBA = 2, //!< [8U/16U/32F] convert between RGB and BGR color spaces (with or without alpha channel) + COLOR_RGB2BGRA = COLOR_BGR2RGBA, //!< [8U/16U/32F] - COLOR_RGBA2BGR = 3, - COLOR_BGRA2RGB = COLOR_RGBA2BGR, + COLOR_RGBA2BGR = 3, //!< [8U/16U/32F] + COLOR_BGRA2RGB = COLOR_RGBA2BGR, //!< [8U/16U/32F] - COLOR_BGR2RGB = 4, - COLOR_RGB2BGR = COLOR_BGR2RGB, + COLOR_BGR2RGB = 4, //!< [8U/16U/32F] + COLOR_RGB2BGR = COLOR_BGR2RGB, //!< [8U/16U/32F] - COLOR_BGRA2RGBA = 5, - COLOR_RGBA2BGRA = COLOR_BGRA2RGBA, + COLOR_BGRA2RGBA = 5, //!< [8U/16U/32F] + COLOR_RGBA2BGRA = COLOR_BGRA2RGBA, //!< [8U/16U/32F] - COLOR_BGR2GRAY = 6, //!< convert between RGB/BGR and grayscale, @ref color_convert_rgb_gray "color conversions" - COLOR_RGB2GRAY = 7, - COLOR_GRAY2BGR = 8, - COLOR_GRAY2RGB = COLOR_GRAY2BGR, - COLOR_GRAY2BGRA = 9, - COLOR_GRAY2RGBA = COLOR_GRAY2BGRA, - COLOR_BGRA2GRAY = 10, - COLOR_RGBA2GRAY = 11, + COLOR_BGR2GRAY = 6, //!< [8U/16U/32F] convert between RGB/BGR and grayscale, @ref color_convert_rgb_gray "color conversions" + COLOR_RGB2GRAY = 7, //!< [8U/16U/32F] + COLOR_GRAY2BGR = 8, //!< [8U/16U/32F] + COLOR_GRAY2RGB = COLOR_GRAY2BGR, //!< [8U/16U/32F] + COLOR_GRAY2BGRA = 9, //!< [8U/16U/32F] + COLOR_GRAY2RGBA = COLOR_GRAY2BGRA, //!< [8U/16U/32F] + COLOR_BGRA2GRAY = 10, //!< [8U/16U/32F] + COLOR_RGBA2GRAY = 11, //!< [8U/16U/32F] - COLOR_BGR2BGR565 = 12, //!< convert between RGB/BGR and BGR565 (16-bit images) - COLOR_RGB2BGR565 = 13, - COLOR_BGR5652BGR = 14, - COLOR_BGR5652RGB = 15, - COLOR_BGRA2BGR565 = 16, - COLOR_RGBA2BGR565 = 17, - COLOR_BGR5652BGRA = 18, - COLOR_BGR5652RGBA = 19, + COLOR_BGR2BGR565 = 12, //!< [8U] convert between RGB/BGR and BGR565 (16-bit images) + COLOR_RGB2BGR565 = 13, //!< [8U] + COLOR_BGR5652BGR = 14, //!< [8U] + COLOR_BGR5652RGB = 15, //!< [8U] + COLOR_BGRA2BGR565 = 16, //!< [8U] + COLOR_RGBA2BGR565 = 17, //!< [8U] + COLOR_BGR5652BGRA = 18, //!< [8U] + COLOR_BGR5652RGBA = 19, //!< [8U] - COLOR_GRAY2BGR565 = 20, //!< convert between grayscale to BGR565 (16-bit images) - COLOR_BGR5652GRAY = 21, + COLOR_GRAY2BGR565 = 20, //!< [8U] convert between grayscale to BGR565 (16-bit images) + COLOR_BGR5652GRAY = 21, //!< [8U] - COLOR_BGR2BGR555 = 22, //!< convert between RGB/BGR and BGR555 (16-bit images) - COLOR_RGB2BGR555 = 23, - COLOR_BGR5552BGR = 24, - COLOR_BGR5552RGB = 25, - COLOR_BGRA2BGR555 = 26, - COLOR_RGBA2BGR555 = 27, - COLOR_BGR5552BGRA = 28, - COLOR_BGR5552RGBA = 29, + COLOR_BGR2BGR555 = 22, //!< [8U] convert between RGB/BGR and BGR555 (16-bit images) + COLOR_RGB2BGR555 = 23, //!< [8U] + COLOR_BGR5552BGR = 24, //!< [8U] + COLOR_BGR5552RGB = 25, //!< [8U] + COLOR_BGRA2BGR555 = 26, //!< [8U] + COLOR_RGBA2BGR555 = 27, //!< [8U] + COLOR_BGR5552BGRA = 28, //!< [8U] + COLOR_BGR5552RGBA = 29, //!< [8U] - COLOR_GRAY2BGR555 = 30, //!< convert between grayscale and BGR555 (16-bit images) - COLOR_BGR5552GRAY = 31, + COLOR_GRAY2BGR555 = 30, //!< [8U] convert between grayscale and BGR555 (16-bit images) + COLOR_BGR5552GRAY = 31, //!< [8U] - COLOR_BGR2XYZ = 32, //!< convert RGB/BGR to CIE XYZ, @ref color_convert_rgb_xyz "color conversions" - COLOR_RGB2XYZ = 33, - COLOR_XYZ2BGR = 34, - COLOR_XYZ2RGB = 35, + COLOR_BGR2XYZ = 32, //!< [8U/16U/32F] convert RGB/BGR to CIE XYZ, @ref color_convert_rgb_xyz "color conversions" + COLOR_RGB2XYZ = 33, //!< [8U/16U/32F] + COLOR_XYZ2BGR = 34, //!< [8U/16U/32F] + COLOR_XYZ2RGB = 35, //!< [8U/16U/32F] - COLOR_BGR2YCrCb = 36, //!< convert RGB/BGR to luma-chroma (aka YCC), @ref color_convert_rgb_ycrcb "color conversions" - COLOR_RGB2YCrCb = 37, - COLOR_YCrCb2BGR = 38, - COLOR_YCrCb2RGB = 39, + COLOR_BGR2YCrCb = 36, //!< [8U/16U/32F] convert RGB/BGR to luma-chroma (aka YCC), @ref color_convert_rgb_ycrcb "color conversions" + COLOR_RGB2YCrCb = 37, //!< [8U/16U/32F] + COLOR_YCrCb2BGR = 38, //!< [8U/16U/32F] + COLOR_YCrCb2RGB = 39, //!< [8U/16U/32F] - COLOR_BGR2HSV = 40, //!< convert RGB/BGR to HSV (hue saturation value) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hsv "color conversions" - COLOR_RGB2HSV = 41, + COLOR_BGR2HSV = 40, //!< [8U/32F] convert RGB/BGR to HSV (hue saturation value) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hsv "color conversions" + COLOR_RGB2HSV = 41, //!< [8U/32F] - COLOR_BGR2Lab = 44, //!< convert RGB/BGR to CIE Lab, @ref color_convert_rgb_lab "color conversions" - COLOR_RGB2Lab = 45, + COLOR_BGR2Lab = 44, //!< [8U/32F] convert RGB/BGR to CIE Lab, @ref color_convert_rgb_lab "color conversions" + COLOR_RGB2Lab = 45, //!< [8U/32F] - COLOR_BGR2Luv = 50, //!< convert RGB/BGR to CIE Luv, @ref color_convert_rgb_luv "color conversions" - COLOR_RGB2Luv = 51, - COLOR_BGR2HLS = 52, //!< convert RGB/BGR to HLS (hue lightness saturation) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hls "color conversions" - COLOR_RGB2HLS = 53, + COLOR_BGR2Luv = 50, //!< [8U/32F] convert RGB/BGR to CIE Luv, @ref color_convert_rgb_luv "color conversions" + COLOR_RGB2Luv = 51, //!< [8U/32F] + COLOR_BGR2HLS = 52, //!< [8U/32F] convert RGB/BGR to HLS (hue lightness saturation) with H range 0..180 if 8 bit image, @ref color_convert_rgb_hls "color conversions" + COLOR_RGB2HLS = 53, //!< [8U/32F] - COLOR_HSV2BGR = 54, //!< backward conversions HSV to RGB/BGR with H range 0..180 if 8 bit image - COLOR_HSV2RGB = 55, + COLOR_HSV2BGR = 54, //!< [8U/32F] backward conversions HSV to RGB/BGR with H range 0..180 if 8 bit image + COLOR_HSV2RGB = 55, //!< [8U/32F] - COLOR_Lab2BGR = 56, - COLOR_Lab2RGB = 57, - COLOR_Luv2BGR = 58, - COLOR_Luv2RGB = 59, - COLOR_HLS2BGR = 60, //!< backward conversions HLS to RGB/BGR with H range 0..180 if 8 bit image - COLOR_HLS2RGB = 61, + COLOR_Lab2BGR = 56, //!< [8U/32F] + COLOR_Lab2RGB = 57, //!< [8U/32F] + COLOR_Luv2BGR = 58, //!< [8U/32F] + COLOR_Luv2RGB = 59, //!< [8U/32F] + COLOR_HLS2BGR = 60, //!< [8U/32F] backward conversions HLS to RGB/BGR with H range 0..180 if 8 bit image + COLOR_HLS2RGB = 61, //!< [8U/32F] - COLOR_BGR2HSV_FULL = 66, //!< convert RGB/BGR to HSV (hue saturation value) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hsv "color conversions" - COLOR_RGB2HSV_FULL = 67, - COLOR_BGR2HLS_FULL = 68, //!< convert RGB/BGR to HLS (hue lightness saturation) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hls "color conversions" - COLOR_RGB2HLS_FULL = 69, + COLOR_BGR2HSV_FULL = 66, //!< [8U/32F] convert RGB/BGR to HSV (hue saturation value) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hsv "color conversions" + COLOR_RGB2HSV_FULL = 67, //!< [8U/32F] + COLOR_BGR2HLS_FULL = 68, //!< [8U/32F] convert RGB/BGR to HLS (hue lightness saturation) with H range 0..255 if 8 bit image, @ref color_convert_rgb_hls "color conversions" + COLOR_RGB2HLS_FULL = 69, //!< [8U/32F] - COLOR_HSV2BGR_FULL = 70, //!< backward conversions HSV to RGB/BGR with H range 0..255 if 8 bit image - COLOR_HSV2RGB_FULL = 71, - COLOR_HLS2BGR_FULL = 72, //!< backward conversions HLS to RGB/BGR with H range 0..255 if 8 bit image - COLOR_HLS2RGB_FULL = 73, + COLOR_HSV2BGR_FULL = 70, //!< [8U/32F] backward conversions HSV to RGB/BGR with H range 0..255 if 8 bit image + COLOR_HSV2RGB_FULL = 71, //!< [8U/32F] + COLOR_HLS2BGR_FULL = 72, //!< [8U/32F] backward conversions HLS to RGB/BGR with H range 0..255 if 8 bit image + COLOR_HLS2RGB_FULL = 73, //!< [8U/32F] - COLOR_LBGR2Lab = 74, - COLOR_LRGB2Lab = 75, - COLOR_LBGR2Luv = 76, - COLOR_LRGB2Luv = 77, + COLOR_LBGR2Lab = 74, //!< [8U/32F] + COLOR_LRGB2Lab = 75, //!< [8U/32F] + COLOR_LBGR2Luv = 76, //!< [8U/32F] + COLOR_LRGB2Luv = 77, //!< [8U/32F] - COLOR_Lab2LBGR = 78, - COLOR_Lab2LRGB = 79, - COLOR_Luv2LBGR = 80, - COLOR_Luv2LRGB = 81, + COLOR_Lab2LBGR = 78, //!< [8U/32F] + COLOR_Lab2LRGB = 79, //!< [8U/32F] + COLOR_Luv2LBGR = 80, //!< [8U/32F] + COLOR_Luv2LRGB = 81, //!< [8U/32F] - COLOR_BGR2YUV = 82, //!< convert between RGB/BGR and YUV - COLOR_RGB2YUV = 83, - COLOR_YUV2BGR = 84, - COLOR_YUV2RGB = 85, + COLOR_BGR2YUV = 82, //!< [8U/16U/32F] convert between RGB/BGR and YUV + COLOR_RGB2YUV = 83, //!< [8U/16U/32F] + COLOR_YUV2BGR = 84, //!< [8U/16U/32F] + COLOR_YUV2RGB = 85, //!< [8U/16U/32F] - COLOR_YUV2RGB_NV12 = 90, //!< convert between 4:2:0-subsampled YUV NV12 and RGB, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGR_NV12 = 91, //!< convert between 4:2:0-subsampled YUV NV12 and BGR, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2RGB_NV21 = 92, //!< convert between 4:2:0-subsampled YUV NV21 and RGB, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGR_NV21 = 93, //!< convert between 4:2:0-subsampled YUV NV21 and BGR, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGB_NV12 = 90, //!< [8U] convert between 4:2:0-subsampled YUV NV12 and RGB, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGR_NV12 = 91, //!< [8U] convert between 4:2:0-subsampled YUV NV12 and BGR, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGB_NV21 = 92, //!< [8U] convert between 4:2:0-subsampled YUV NV21 and RGB, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGR_NV21 = 93, //!< [8U] convert between 4:2:0-subsampled YUV NV21 and BGR, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x COLOR_YUV420sp2RGB = COLOR_YUV2RGB_NV21, //!< synonym to NV21 COLOR_YUV420sp2BGR = COLOR_YUV2BGR_NV21, //!< synonym to NV21 - COLOR_YUV2RGBA_NV12 = 94, //!< convert between 4:2:0-subsampled YUV NV12 and RGBA, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGRA_NV12 = 95, //!< convert between 4:2:0-subsampled YUV NV12 and BGRA, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2RGBA_NV21 = 96, //!< convert between 4:2:0-subsampled YUV NV21 and RGBA, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGRA_NV21 = 97, //!< convert between 4:2:0-subsampled YUV NV21 and BGRA, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGBA_NV12 = 94, //!< [8U] convert between 4:2:0-subsampled YUV NV12 and RGBA, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGRA_NV12 = 95, //!< [8U] convert between 4:2:0-subsampled YUV NV12 and BGRA, two planes (in one or separate arrays): Y and U/V interleaved, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGBA_NV21 = 96, //!< [8U] convert between 4:2:0-subsampled YUV NV21 and RGBA, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGRA_NV21 = 97, //!< [8U] convert between 4:2:0-subsampled YUV NV21 and BGRA, two planes (in one or separate arrays): Y and V/U interleaved, see @ref color_convert_rgb_yuv_42x COLOR_YUV420sp2RGBA = COLOR_YUV2RGBA_NV21, //!< synonym to NV21 COLOR_YUV420sp2BGRA = COLOR_YUV2BGRA_NV21, //!< synonym to NV21 - COLOR_YUV2RGB_YV12 = 98, //!< convert between 4:2:0-subsampled YUV YV12 and RGB, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGR_YV12 = 99, //!< convert between 4:2:0-subsampled YUV YV12 and BGR, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2RGB_IYUV = 100, //!< convert between 4:2:0-subsampled YUV IYUV and RGB, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGR_IYUV = 101, //!< convert between 4:2:0-subsampled YUV IYUV and BGR, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGB_YV12 = 98, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and RGB, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGR_YV12 = 99, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and BGR, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGB_IYUV = 100, //!< [8U] convert between 4:2:0-subsampled YUV IYUV and RGB, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGR_IYUV = 101, //!< [8U] convert between 4:2:0-subsampled YUV IYUV and BGR, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x COLOR_YUV2RGB_I420 = COLOR_YUV2RGB_IYUV, //!< synonym to IYUV COLOR_YUV2BGR_I420 = COLOR_YUV2BGR_IYUV, //!< synonym to IYUV COLOR_YUV420p2RGB = COLOR_YUV2RGB_YV12, //!< synonym to YV12 COLOR_YUV420p2BGR = COLOR_YUV2BGR_YV12, //!< synonym to YV12 - COLOR_YUV2RGBA_YV12 = 102, //!< convert between 4:2:0-subsampled YUV YV12 and RGBA, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGRA_YV12 = 103, //!< convert between 4:2:0-subsampled YUV YV12 and BGRA, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2RGBA_IYUV = 104, //!< convert between 4:2:0-subsampled YUV YV12 and RGBA, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGRA_IYUV = 105, //!< convert between 4:2:0-subsampled YUV YV12 and BGRA, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGBA_YV12 = 102, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and RGBA, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGRA_YV12 = 103, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and BGRA, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGBA_IYUV = 104, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and RGBA, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGRA_IYUV = 105, //!< [8U] convert between 4:2:0-subsampled YUV YV12 and BGRA, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x COLOR_YUV2RGBA_I420 = COLOR_YUV2RGBA_IYUV, //!< synonym to IYUV COLOR_YUV2BGRA_I420 = COLOR_YUV2BGRA_IYUV, //!< synonym to IYUV COLOR_YUV420p2RGBA = COLOR_YUV2RGBA_YV12, //!< synonym to YV12 COLOR_YUV420p2BGRA = COLOR_YUV2BGRA_YV12, //!< synonym to YV12 - COLOR_YUV2GRAY_420 = 106, //!< extract Y channel from YUV 4:2:0 image + COLOR_YUV2GRAY_420 = 106, //!< [8U] extract Y channel from YUV 4:2:0 image COLOR_YUV2GRAY_NV21 = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420 COLOR_YUV2GRAY_NV12 = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420 COLOR_YUV2GRAY_YV12 = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420 @@ -683,8 +687,8 @@ enum ColorConversionCodes { COLOR_YUV420sp2GRAY = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420 COLOR_YUV420p2GRAY = COLOR_YUV2GRAY_420, //!< synonym to COLOR_YUV2GRAY_420 - COLOR_YUV2RGB_UYVY = 107, //!< convert between YUV UYVY and RGB, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGR_UYVY = 108, //!< convert between YUV UYVY and BGR, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGB_UYVY = 107, //!< [8U] convert between YUV UYVY and RGB, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGR_UYVY = 108, //!< [8U] convert between YUV UYVY and BGR, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x //COLOR_YUV2RGB_VYUY = 109, //!< convert between YUV VYUY and RGB, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x //COLOR_YUV2BGR_VYUY = 110, //!< convert between YUV VYUY and BGR, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x COLOR_YUV2RGB_Y422 = COLOR_YUV2RGB_UYVY, //!< synonym to UYVY @@ -692,180 +696,181 @@ enum ColorConversionCodes { COLOR_YUV2RGB_UYNV = COLOR_YUV2RGB_UYVY, //!< synonym to UYVY COLOR_YUV2BGR_UYNV = COLOR_YUV2BGR_UYVY, //!< synonym to UYVY - COLOR_YUV2RGBA_UYVY = 111, //!< convert between YUV UYVY and RGBA, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGRA_UYVY = 112, //!< convert between YUV UYVY and BGRA, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x - //COLOR_YUV2RGBA_VYUY = 113, //!< convert between YUV VYUY and RGBA, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x - //COLOR_YUV2BGRA_VYUY = 114, //!< convert between YUV VYUY and BGRA, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGBA_UYVY = 111, //!< [8U] convert between YUV UYVY and RGBA, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGRA_UYVY = 112, //!< [8U] convert between YUV UYVY and BGRA, YUV is 4:2:2-subsampled and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x + //COLOR_YUV2RGBA_VYUY = 113, //!< [8U] convert between YUV VYUY and RGBA, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x + //COLOR_YUV2BGRA_VYUY = 114, //!< [8U] convert between YUV VYUY and BGRA, YUV is 4:2:2-subsampled and interleaved as V/Y1/U/Y2, see @ref color_convert_rgb_yuv_42x COLOR_YUV2RGBA_Y422 = COLOR_YUV2RGBA_UYVY, //!< synonym to UYVY COLOR_YUV2BGRA_Y422 = COLOR_YUV2BGRA_UYVY, //!< synonym to UYVY COLOR_YUV2RGBA_UYNV = COLOR_YUV2RGBA_UYVY, //!< synonym to UYVY COLOR_YUV2BGRA_UYNV = COLOR_YUV2BGRA_UYVY, //!< synonym to UYVY - COLOR_YUV2RGB_YUY2 = 115, //!< convert between YUV YUY2 and RGB, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGR_YUY2 = 116, //!< convert between YUV YUY2 and BGR, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2RGB_YVYU = 117, //!< convert between YUV YVYU and RGB, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGR_YVYU = 118, //!< convert between YUV YVYU and BGR, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGB_YUY2 = 115, //!< [8U] convert between YUV YUY2 and RGB, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGR_YUY2 = 116, //!< [8U] convert between YUV YUY2 and BGR, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGB_YVYU = 117, //!< [8U] convert between YUV YVYU and RGB, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGR_YVYU = 118, //!< [8U] convert between YUV YVYU and BGR, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x COLOR_YUV2RGB_YUYV = COLOR_YUV2RGB_YUY2, //!< synonym to YUY2 COLOR_YUV2BGR_YUYV = COLOR_YUV2BGR_YUY2, //!< synonym to YUY2 COLOR_YUV2RGB_YUNV = COLOR_YUV2RGB_YUY2, //!< synonym to YUY2 COLOR_YUV2BGR_YUNV = COLOR_YUV2BGR_YUY2, //!< synonym to YUY2 - COLOR_YUV2RGBA_YUY2 = 119, //!< convert between YUV YUY2 and RGBA, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGRA_YUY2 = 120, //!< convert between YUV YUY2 and BGRA, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2RGBA_YVYU = 121, //!< convert between YUV YVYU and RGBA, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x - COLOR_YUV2BGRA_YVYU = 122, //!< convert between YUV YVYU and BGRA, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGBA_YUY2 = 119, //!< [8U] convert between YUV YUY2 and RGBA, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGRA_YUY2 = 120, //!< [8U] convert between YUV YUY2 and BGRA, YUV is 4:2:2-subsampled and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2RGBA_YVYU = 121, //!< [8U] convert between YUV YVYU and RGBA, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x + COLOR_YUV2BGRA_YVYU = 122, //!< [8U] convert between YUV YVYU and BGRA, YUV is 4:2:2-subsampled and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x COLOR_YUV2RGBA_YUYV = COLOR_YUV2RGBA_YUY2, //!< synonym to YUY2 COLOR_YUV2BGRA_YUYV = COLOR_YUV2BGRA_YUY2, //!< synonym to YUY2 COLOR_YUV2RGBA_YUNV = COLOR_YUV2RGBA_YUY2, //!< synonym to YUY2 COLOR_YUV2BGRA_YUNV = COLOR_YUV2BGRA_YUY2, //!< synonym to YUY2 - COLOR_YUV2GRAY_UYVY = 123, //!< extract Y channel from YUV 4:2:2 image - COLOR_YUV2GRAY_YUY2 = 124, //!< extract Y channel from YUV 4:2:2 image - //COLOR_YUV2GRAY_VYUY = COLOR_YUV2GRAY_UYVY, + COLOR_YUV2GRAY_UYVY = 123, //!< [8U] extract Y channel from YUV 4:2:2 image + COLOR_YUV2GRAY_YUY2 = 124, //!< [8U] extract Y channel from YUV 4:2:2 image + //CV_YUV2GRAY_VYUY = CV_YUV2GRAY_UYVY, //!< synonym to COLOR_YUV2GRAY_UYVY + COLOR_YUV2GRAY_Y422 = COLOR_YUV2GRAY_UYVY, //!< synonym to COLOR_YUV2GRAY_UYVY COLOR_YUV2GRAY_UYNV = COLOR_YUV2GRAY_UYVY, //!< synonym to COLOR_YUV2GRAY_UYVY COLOR_YUV2GRAY_YVYU = COLOR_YUV2GRAY_YUY2, //!< synonym to COLOR_YUV2GRAY_YUY2 COLOR_YUV2GRAY_YUYV = COLOR_YUV2GRAY_YUY2, //!< synonym to COLOR_YUV2GRAY_YUY2 COLOR_YUV2GRAY_YUNV = COLOR_YUV2GRAY_YUY2, //!< synonym to COLOR_YUV2GRAY_YUY2 //! alpha premultiplication - COLOR_RGBA2mRGBA = 125, - COLOR_mRGBA2RGBA = 126, + COLOR_RGBA2mRGBA = 125, //!< [8U] + COLOR_mRGBA2RGBA = 126, //!< [8U] - COLOR_RGB2YUV_I420 = 127, //!< convert between RGB and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x - COLOR_BGR2YUV_I420 = 128, //!< convert between BGR and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x + COLOR_RGB2YUV_I420 = 127, //!< [8U] convert between RGB and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x + COLOR_BGR2YUV_I420 = 128, //!< [8U] convert between BGR and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x COLOR_RGB2YUV_IYUV = COLOR_RGB2YUV_I420, //!< synonym to I420 COLOR_BGR2YUV_IYUV = COLOR_BGR2YUV_I420, //!< synonym to I420 - COLOR_RGBA2YUV_I420 = 129, //!< convert between RGBA and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x - COLOR_BGRA2YUV_I420 = 130, //!< convert between BGRA and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x + COLOR_RGBA2YUV_I420 = 129, //!< [8U] convert between RGBA and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x + COLOR_BGRA2YUV_I420 = 130, //!< [8U] convert between BGRA and 4:2:0-subsampled YUV I420, three planes in one array: Y, U and V, see @ref color_convert_rgb_yuv_42x COLOR_RGBA2YUV_IYUV = COLOR_RGBA2YUV_I420, //!< synonym to I420 COLOR_BGRA2YUV_IYUV = COLOR_BGRA2YUV_I420, //!< synonym to I420 - COLOR_RGB2YUV_YV12 = 131, //!< convert between RGB and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x - COLOR_BGR2YUV_YV12 = 132, //!< convert between BGR and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x - COLOR_RGBA2YUV_YV12 = 133, //!< convert between RGBA and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x - COLOR_BGRA2YUV_YV12 = 134, //!< convert between BGRA and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x + COLOR_RGB2YUV_YV12 = 131, //!< [8U] convert between RGB and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x + COLOR_BGR2YUV_YV12 = 132, //!< [8U] convert between BGR and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x + COLOR_RGBA2YUV_YV12 = 133, //!< [8U] convert between RGBA and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x + COLOR_BGRA2YUV_YV12 = 134, //!< [8U] convert between BGRA and 4:2:0-subsampled YUV YV12, three planes in one array: Y, V and U, see @ref color_convert_rgb_yuv_42x //! Demosaicing, see @ref color_convert_bayer "color conversions" for additional information - COLOR_BayerBG2BGR = 46, //!< equivalent to RGGB Bayer pattern - COLOR_BayerGB2BGR = 47, //!< equivalent to GRBG Bayer pattern - COLOR_BayerRG2BGR = 48, //!< equivalent to BGGR Bayer pattern - COLOR_BayerGR2BGR = 49, //!< equivalent to GBRG Bayer pattern + COLOR_BayerBG2BGR = 46, //!< [8U/16U] equivalent to RGGB Bayer pattern + COLOR_BayerGB2BGR = 47, //!< [8U/16U] equivalent to GRBG Bayer pattern + COLOR_BayerRG2BGR = 48, //!< [8U/16U] equivalent to BGGR Bayer pattern + COLOR_BayerGR2BGR = 49, //!< [8U/16U] equivalent to GBRG Bayer pattern - COLOR_BayerRGGB2BGR = COLOR_BayerBG2BGR, - COLOR_BayerGRBG2BGR = COLOR_BayerGB2BGR, - COLOR_BayerBGGR2BGR = COLOR_BayerRG2BGR, - COLOR_BayerGBRG2BGR = COLOR_BayerGR2BGR, + COLOR_BayerRGGB2BGR = COLOR_BayerBG2BGR, //!< [8U/16U] + COLOR_BayerGRBG2BGR = COLOR_BayerGB2BGR, //!< [8U/16U] + COLOR_BayerBGGR2BGR = COLOR_BayerRG2BGR, //!< [8U/16U] + COLOR_BayerGBRG2BGR = COLOR_BayerGR2BGR, //!< [8U/16U] - COLOR_BayerRGGB2RGB = COLOR_BayerBGGR2BGR, - COLOR_BayerGRBG2RGB = COLOR_BayerGBRG2BGR, - COLOR_BayerBGGR2RGB = COLOR_BayerRGGB2BGR, - COLOR_BayerGBRG2RGB = COLOR_BayerGRBG2BGR, + COLOR_BayerRGGB2RGB = COLOR_BayerBGGR2BGR, //!< [8U/16U] + COLOR_BayerGRBG2RGB = COLOR_BayerGBRG2BGR, //!< [8U/16U] + COLOR_BayerBGGR2RGB = COLOR_BayerRGGB2BGR, //!< [8U/16U] + COLOR_BayerGBRG2RGB = COLOR_BayerGRBG2BGR, //!< [8U/16U] - COLOR_BayerBG2RGB = COLOR_BayerRG2BGR, //!< equivalent to RGGB Bayer pattern - COLOR_BayerGB2RGB = COLOR_BayerGR2BGR, //!< equivalent to GRBG Bayer pattern - COLOR_BayerRG2RGB = COLOR_BayerBG2BGR, //!< equivalent to BGGR Bayer pattern - COLOR_BayerGR2RGB = COLOR_BayerGB2BGR, //!< equivalent to GBRG Bayer pattern + COLOR_BayerBG2RGB = COLOR_BayerRG2BGR, //!< [8U/16U] equivalent to RGGB Bayer pattern + COLOR_BayerGB2RGB = COLOR_BayerGR2BGR, //!< [8U/16U] equivalent to GRBG Bayer pattern + COLOR_BayerRG2RGB = COLOR_BayerBG2BGR, //!< [8U/16U] equivalent to BGGR Bayer pattern + COLOR_BayerGR2RGB = COLOR_BayerGB2BGR, //!< [8U/16U] equivalent to GBRG Bayer pattern - COLOR_BayerBG2GRAY = 86, //!< equivalent to RGGB Bayer pattern - COLOR_BayerGB2GRAY = 87, //!< equivalent to GRBG Bayer pattern - COLOR_BayerRG2GRAY = 88, //!< equivalent to BGGR Bayer pattern - COLOR_BayerGR2GRAY = 89, //!< equivalent to GBRG Bayer pattern + COLOR_BayerBG2GRAY = 86, //!< [8U/16U] equivalent to RGGB Bayer pattern + COLOR_BayerGB2GRAY = 87, //!< [8U/16U] equivalent to GRBG Bayer pattern + COLOR_BayerRG2GRAY = 88, //!< [8U/16U] equivalent to BGGR Bayer pattern + COLOR_BayerGR2GRAY = 89, //!< [8U/16U] equivalent to GBRG Bayer pattern - COLOR_BayerRGGB2GRAY = COLOR_BayerBG2GRAY, - COLOR_BayerGRBG2GRAY = COLOR_BayerGB2GRAY, - COLOR_BayerBGGR2GRAY = COLOR_BayerRG2GRAY, - COLOR_BayerGBRG2GRAY = COLOR_BayerGR2GRAY, + COLOR_BayerRGGB2GRAY = COLOR_BayerBG2GRAY, //!< [8U/16U] + COLOR_BayerGRBG2GRAY = COLOR_BayerGB2GRAY, //!< [8U/16U] + COLOR_BayerBGGR2GRAY = COLOR_BayerRG2GRAY, //!< [8U/16U] + COLOR_BayerGBRG2GRAY = COLOR_BayerGR2GRAY, //!< [8U/16U] //! Demosaicing using Variable Number of Gradients - COLOR_BayerBG2BGR_VNG = 62, //!< equivalent to RGGB Bayer pattern - COLOR_BayerGB2BGR_VNG = 63, //!< equivalent to GRBG Bayer pattern - COLOR_BayerRG2BGR_VNG = 64, //!< equivalent to BGGR Bayer pattern - COLOR_BayerGR2BGR_VNG = 65, //!< equivalent to GBRG Bayer pattern + COLOR_BayerBG2BGR_VNG = 62, //!< [8U] equivalent to RGGB Bayer pattern + COLOR_BayerGB2BGR_VNG = 63, //!< [8U] equivalent to GRBG Bayer pattern + COLOR_BayerRG2BGR_VNG = 64, //!< [8U] equivalent to BGGR Bayer pattern + COLOR_BayerGR2BGR_VNG = 65, //!< [8U] equivalent to GBRG Bayer pattern - COLOR_BayerRGGB2BGR_VNG = COLOR_BayerBG2BGR_VNG, - COLOR_BayerGRBG2BGR_VNG = COLOR_BayerGB2BGR_VNG, - COLOR_BayerBGGR2BGR_VNG = COLOR_BayerRG2BGR_VNG, - COLOR_BayerGBRG2BGR_VNG = COLOR_BayerGR2BGR_VNG, + COLOR_BayerRGGB2BGR_VNG = COLOR_BayerBG2BGR_VNG, //!< [8U] + COLOR_BayerGRBG2BGR_VNG = COLOR_BayerGB2BGR_VNG, //!< [8U] + COLOR_BayerBGGR2BGR_VNG = COLOR_BayerRG2BGR_VNG, //!< [8U] + COLOR_BayerGBRG2BGR_VNG = COLOR_BayerGR2BGR_VNG, //!< [8U] - COLOR_BayerRGGB2RGB_VNG = COLOR_BayerBGGR2BGR_VNG, - COLOR_BayerGRBG2RGB_VNG = COLOR_BayerGBRG2BGR_VNG, - COLOR_BayerBGGR2RGB_VNG = COLOR_BayerRGGB2BGR_VNG, - COLOR_BayerGBRG2RGB_VNG = COLOR_BayerGRBG2BGR_VNG, + COLOR_BayerRGGB2RGB_VNG = COLOR_BayerBGGR2BGR_VNG, //!< [8U] + COLOR_BayerGRBG2RGB_VNG = COLOR_BayerGBRG2BGR_VNG, //!< [8U] + COLOR_BayerBGGR2RGB_VNG = COLOR_BayerRGGB2BGR_VNG, //!< [8U] + COLOR_BayerGBRG2RGB_VNG = COLOR_BayerGRBG2BGR_VNG, //!< [8U] - COLOR_BayerBG2RGB_VNG = COLOR_BayerRG2BGR_VNG, //!< equivalent to RGGB Bayer pattern - COLOR_BayerGB2RGB_VNG = COLOR_BayerGR2BGR_VNG, //!< equivalent to GRBG Bayer pattern - COLOR_BayerRG2RGB_VNG = COLOR_BayerBG2BGR_VNG, //!< equivalent to BGGR Bayer pattern - COLOR_BayerGR2RGB_VNG = COLOR_BayerGB2BGR_VNG, //!< equivalent to GBRG Bayer pattern + COLOR_BayerBG2RGB_VNG = COLOR_BayerRG2BGR_VNG, //!< [8U] equivalent to RGGB Bayer pattern + COLOR_BayerGB2RGB_VNG = COLOR_BayerGR2BGR_VNG, //!< [8U] equivalent to GRBG Bayer pattern + COLOR_BayerRG2RGB_VNG = COLOR_BayerBG2BGR_VNG, //!< [8U] equivalent to BGGR Bayer pattern + COLOR_BayerGR2RGB_VNG = COLOR_BayerGB2BGR_VNG, //!< [8U] equivalent to GBRG Bayer pattern //! Edge-Aware Demosaicing - COLOR_BayerBG2BGR_EA = 135, //!< equivalent to RGGB Bayer pattern - COLOR_BayerGB2BGR_EA = 136, //!< equivalent to GRBG Bayer pattern - COLOR_BayerRG2BGR_EA = 137, //!< equivalent to BGGR Bayer pattern - COLOR_BayerGR2BGR_EA = 138, //!< equivalent to GBRG Bayer pattern + COLOR_BayerBG2BGR_EA = 135, //!< [8U/16U] equivalent to RGGB Bayer pattern + COLOR_BayerGB2BGR_EA = 136, //!< [8U/16U] equivalent to GRBG Bayer pattern + COLOR_BayerRG2BGR_EA = 137, //!< [8U/16U] equivalent to BGGR Bayer pattern + COLOR_BayerGR2BGR_EA = 138, //!< [8U/16U] equivalent to GBRG Bayer pattern - COLOR_BayerRGGB2BGR_EA = COLOR_BayerBG2BGR_EA, - COLOR_BayerGRBG2BGR_EA = COLOR_BayerGB2BGR_EA, - COLOR_BayerBGGR2BGR_EA = COLOR_BayerRG2BGR_EA, - COLOR_BayerGBRG2BGR_EA = COLOR_BayerGR2BGR_EA, + COLOR_BayerRGGB2BGR_EA = COLOR_BayerBG2BGR_EA, //!< [8U/16U] + COLOR_BayerGRBG2BGR_EA = COLOR_BayerGB2BGR_EA, //!< [8U/16U] + COLOR_BayerBGGR2BGR_EA = COLOR_BayerRG2BGR_EA, //!< [8U/16U] + COLOR_BayerGBRG2BGR_EA = COLOR_BayerGR2BGR_EA, //!< [8U/16U] - COLOR_BayerRGGB2RGB_EA = COLOR_BayerBGGR2BGR_EA, - COLOR_BayerGRBG2RGB_EA = COLOR_BayerGBRG2BGR_EA, - COLOR_BayerBGGR2RGB_EA = COLOR_BayerRGGB2BGR_EA, - COLOR_BayerGBRG2RGB_EA = COLOR_BayerGRBG2BGR_EA, + COLOR_BayerRGGB2RGB_EA = COLOR_BayerBGGR2BGR_EA, //!< [8U/16U] + COLOR_BayerGRBG2RGB_EA = COLOR_BayerGBRG2BGR_EA, //!< [8U/16U] + COLOR_BayerBGGR2RGB_EA = COLOR_BayerRGGB2BGR_EA, //!< [8U/16U] + COLOR_BayerGBRG2RGB_EA = COLOR_BayerGRBG2BGR_EA, //!< [8U/16U] - COLOR_BayerBG2RGB_EA = COLOR_BayerRG2BGR_EA, //!< equivalent to RGGB Bayer pattern - COLOR_BayerGB2RGB_EA = COLOR_BayerGR2BGR_EA, //!< equivalent to GRBG Bayer pattern - COLOR_BayerRG2RGB_EA = COLOR_BayerBG2BGR_EA, //!< equivalent to BGGR Bayer pattern - COLOR_BayerGR2RGB_EA = COLOR_BayerGB2BGR_EA, //!< equivalent to GBRG Bayer pattern + COLOR_BayerBG2RGB_EA = COLOR_BayerRG2BGR_EA, //!< [8U/16U] equivalent to RGGB Bayer pattern + COLOR_BayerGB2RGB_EA = COLOR_BayerGR2BGR_EA, //!< [8U/16U] equivalent to GRBG Bayer pattern + COLOR_BayerRG2RGB_EA = COLOR_BayerBG2BGR_EA, //!< [8U/16U] equivalent to BGGR Bayer pattern + COLOR_BayerGR2RGB_EA = COLOR_BayerGB2BGR_EA, //!< [8U/16U] equivalent to GBRG Bayer pattern //! Demosaicing with alpha channel - COLOR_BayerBG2BGRA = 139, //!< equivalent to RGGB Bayer pattern - COLOR_BayerGB2BGRA = 140, //!< equivalent to GRBG Bayer pattern - COLOR_BayerRG2BGRA = 141, //!< equivalent to BGGR Bayer pattern - COLOR_BayerGR2BGRA = 142, //!< equivalent to GBRG Bayer pattern + COLOR_BayerBG2BGRA = 139, //!< [8U/16U] equivalent to RGGB Bayer pattern + COLOR_BayerGB2BGRA = 140, //!< [8U/16U] equivalent to GRBG Bayer pattern + COLOR_BayerRG2BGRA = 141, //!< [8U/16U] equivalent to BGGR Bayer pattern + COLOR_BayerGR2BGRA = 142, //!< [8U/16U] equivalent to GBRG Bayer pattern - COLOR_BayerRGGB2BGRA = COLOR_BayerBG2BGRA, - COLOR_BayerGRBG2BGRA = COLOR_BayerGB2BGRA, - COLOR_BayerBGGR2BGRA = COLOR_BayerRG2BGRA, - COLOR_BayerGBRG2BGRA = COLOR_BayerGR2BGRA, + COLOR_BayerRGGB2BGRA = COLOR_BayerBG2BGRA, //!< [8U/16U] + COLOR_BayerGRBG2BGRA = COLOR_BayerGB2BGRA, //!< [8U/16U] + COLOR_BayerBGGR2BGRA = COLOR_BayerRG2BGRA, //!< [8U/16U] + COLOR_BayerGBRG2BGRA = COLOR_BayerGR2BGRA, //!< [8U/16U] - COLOR_BayerRGGB2RGBA = COLOR_BayerBGGR2BGRA, - COLOR_BayerGRBG2RGBA = COLOR_BayerGBRG2BGRA, - COLOR_BayerBGGR2RGBA = COLOR_BayerRGGB2BGRA, - COLOR_BayerGBRG2RGBA = COLOR_BayerGRBG2BGRA, + COLOR_BayerRGGB2RGBA = COLOR_BayerBGGR2BGRA, //!< [8U/16U] + COLOR_BayerGRBG2RGBA = COLOR_BayerGBRG2BGRA, //!< [8U/16U] + COLOR_BayerBGGR2RGBA = COLOR_BayerRGGB2BGRA, //!< [8U/16U] + COLOR_BayerGBRG2RGBA = COLOR_BayerGRBG2BGRA, //!< [8U/16U] - COLOR_BayerBG2RGBA = COLOR_BayerRG2BGRA, //!< equivalent to RGGB Bayer pattern - COLOR_BayerGB2RGBA = COLOR_BayerGR2BGRA, //!< equivalent to GRBG Bayer pattern - COLOR_BayerRG2RGBA = COLOR_BayerBG2BGRA, //!< equivalent to BGGR Bayer pattern - COLOR_BayerGR2RGBA = COLOR_BayerGB2BGRA, //!< equivalent to GBRG Bayer pattern + COLOR_BayerBG2RGBA = COLOR_BayerRG2BGRA, //!< [8U/16U] equivalent to RGGB Bayer pattern + COLOR_BayerGB2RGBA = COLOR_BayerGR2BGRA, //!< [8U/16U] equivalent to GRBG Bayer pattern + COLOR_BayerRG2RGBA = COLOR_BayerBG2BGRA, //!< [8U/16U] equivalent to BGGR Bayer pattern + COLOR_BayerGR2RGBA = COLOR_BayerGB2BGRA, //!< [8U/16U] equivalent to GBRG Bayer pattern - COLOR_RGB2YUV_UYVY = 143, //!< convert between RGB and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x - COLOR_BGR2YUV_UYVY = 144, //!< convert between BGR and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x + COLOR_RGB2YUV_UYVY = 143, //!< [8U] convert between RGB and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x + COLOR_BGR2YUV_UYVY = 144, //!< [8U] convert between BGR and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x COLOR_RGB2YUV_Y422 = COLOR_RGB2YUV_UYVY, //!< synonym to UYVY COLOR_BGR2YUV_Y422 = COLOR_BGR2YUV_UYVY, //!< synonym to UYVY COLOR_RGB2YUV_UYNV = COLOR_RGB2YUV_UYVY, //!< synonym to UYVY COLOR_BGR2YUV_UYNV = COLOR_BGR2YUV_UYVY, //!< synonym to UYVY - COLOR_RGBA2YUV_UYVY = 145, //!< convert between RGBA and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x - COLOR_BGRA2YUV_UYVY = 146, //!< convert between BGRA and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x + COLOR_RGBA2YUV_UYVY = 145, //!< [8U] convert between RGBA and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x + COLOR_BGRA2YUV_UYVY = 146, //!< [8U] convert between BGRA and YUV UYVU, YUV is 4:2:2 and interleaved as U/Y1/V/Y2, see @ref color_convert_rgb_yuv_42x COLOR_RGBA2YUV_Y422 = COLOR_RGBA2YUV_UYVY, //!< synonym to UYVY COLOR_BGRA2YUV_Y422 = COLOR_BGRA2YUV_UYVY, //!< synonym to UYVY COLOR_RGBA2YUV_UYNV = COLOR_RGBA2YUV_UYVY, //!< synonym to UYVY COLOR_BGRA2YUV_UYNV = COLOR_BGRA2YUV_UYVY, //!< synonym to UYVY - COLOR_RGB2YUV_YUY2 = 147, //!< convert between RGB and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x - COLOR_BGR2YUV_YUY2 = 148, //!< convert between BGR and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x - COLOR_RGB2YUV_YVYU = 149, //!< convert between RGB and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x - COLOR_BGR2YUV_YVYU = 150, //!< convert between BGR and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x + COLOR_RGB2YUV_YUY2 = 147, //!< [8U] convert between RGB and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x + COLOR_BGR2YUV_YUY2 = 148, //!< [8U] convert between BGR and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x + COLOR_RGB2YUV_YVYU = 149, //!< [8U] convert between RGB and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x + COLOR_BGR2YUV_YVYU = 150, //!< [8U] convert between BGR and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x COLOR_RGB2YUV_YUYV = COLOR_RGB2YUV_YUY2, //!< synonym to YUY2 COLOR_BGR2YUV_YUYV = COLOR_BGR2YUV_YUY2, //!< synonym to YUY2 COLOR_RGB2YUV_YUNV = COLOR_RGB2YUV_YUY2, //!< synonym to YUY2 COLOR_BGR2YUV_YUNV = COLOR_BGR2YUV_YUY2, //!< synonym to YUY2 - COLOR_RGBA2YUV_YUY2 = 151, //!< convert between RGBA and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x - COLOR_BGRA2YUV_YUY2 = 152, //!< convert between BGRA and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x - COLOR_RGBA2YUV_YVYU = 153, //!< convert between RGBA and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x - COLOR_BGRA2YUV_YVYU = 154, //!< convert between BGRA and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x + COLOR_RGBA2YUV_YUY2 = 151, //!< [8U] convert between RGBA and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x + COLOR_BGRA2YUV_YUY2 = 152, //!< [8U] convert between BGRA and YUV YUY2, YUV is 4:2:2 and interleaved as Y1/U/Y2/V, see @ref color_convert_rgb_yuv_42x + COLOR_RGBA2YUV_YVYU = 153, //!< [8U] convert between RGBA and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x + COLOR_BGRA2YUV_YVYU = 154, //!< [8U] convert between BGRA and YUV YVYU, YUV is 4:2:2 and interleaved as Y1/V/Y2/U, see @ref color_convert_rgb_yuv_42x COLOR_RGBA2YUV_YUYV = COLOR_RGBA2YUV_YUY2, //!< synonym to YUY2 COLOR_BGRA2YUV_YUYV = COLOR_BGRA2YUV_YUY2, //!< synonym to YUY2 COLOR_RGBA2YUV_YUNV = COLOR_RGBA2YUV_YUY2, //!< synonym to YUY2 @@ -2999,6 +3004,22 @@ peak) and will be smaller when there are multiple peaks. CV_EXPORTS_W Point2d phaseCorrelate(InputArray src1, InputArray src2, InputArray window = noArray(), CV_OUT double* response = 0); +/** @brief Detects translational shifts between two images. + +This function extends the standard @ref phaseCorrelate method by improving sub-pixel accuracy +through iterative shift refinement in the phase-correlation space, as described in +@cite hrazdira2020iterative. + +@param src1 Source floating point array (CV_32FC1 or CV_64FC1) +@param src2 Source floating point array (CV_32FC1 or CV_64FC1) +@param L2size The size of the correlation neighborhood used by the iterative shift refinement algorithm. +@param maxIters The maximum number of iterations the iterative refinement algorithm will run. +@returns detected sub-pixel shift between the two arrays. + +@sa phaseCorrelate, dft, idft, createHanningWindow + */ +CV_EXPORTS_W Point2d phaseCorrelateIterative(InputArray src1, InputArray src2, int L2size = 7, int maxIters = 10); + /** @brief This function computes a Hanning window coefficients in two dimensions. See (https://en.wikipedia.org/wiki/Hann_function) and (https://en.wikipedia.org/wiki/Window_function) @@ -3736,6 +3757,7 @@ floating-point. channels is derived automatically from src and code. @param hint Implementation modification flags. See #AlgorithmHint +@note The source image (src) must be of an appropriate type for the desired color conversion. see ColorConversionCodes @see @ref imgproc_color_conversions */ CV_EXPORTS_W void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0, AlgorithmHint hint = cv::ALGO_HINT_DEFAULT ); @@ -3789,6 +3811,7 @@ The function can do the following transformations: #COLOR_BayerBG2BGRA , #COLOR_BayerGB2BGRA , #COLOR_BayerRG2BGRA , #COLOR_BayerGR2BGRA +@note The source image (src) must be of an appropriate type for the desired color conversion. see ColorConversionCodes @sa cvtColor */ CV_EXPORTS_W void demosaicing(InputArray src, OutputArray dst, int code, int dstCn = 0); @@ -3812,6 +3835,18 @@ used for images only. @note Only applicable to contour moments calculations from Python bindings: Note that the numpy type for the input array should be either np.int32 or np.float32. +@note For contour-based moments, the zeroth-order moment \c m00 represents +the contour area. + +If the input contour is degenerate (for example, a single point or all points +are collinear), the area is zero and therefore \c m00 == 0. + +In this case, the centroid coordinates (\c m10/m00, \c m01/m00) are undefined +and must be handled explicitly by the caller. + +A common workaround is to compute the center using cv::boundingRect() or by +averaging the input points. + @sa contourArea, arcLength */ CV_EXPORTS_W Moments moments( InputArray array, bool binaryImage = false ); @@ -4219,7 +4254,7 @@ area. It takes the set of points and the parameter k as input and returns the ar enclosing polygon. The Implementation is based on a paper by Aggarwal, Chang and Yap @cite Aggarwal1985. They -provide a \f$\theta(n²log(n)log(k))\f$ algorighm for finding the minimal convex polygon with k +provide a \f$\theta(n²log(n)log(k))\f$ algorithm for finding the minimal convex polygon with k vertices enclosing a 2D convex polygon with n vertices (k < n). Since the #minEnclosingConvexPolygon function takes a 2D point set as input, an additional preprocessing step of computing the convex hull of the 2D point set is required. The complexity of the #convexHull function is \f$O(n log(n))\f$ which diff --git a/modules/imgproc/src/approx.cpp b/modules/imgproc/src/approx.cpp index ffc45c2624..2c30750a38 100644 --- a/modules/imgproc/src/approx.cpp +++ b/modules/imgproc/src/approx.cpp @@ -162,26 +162,40 @@ approxPolyDP_( const Point_* src_contour, int count0, Point_* dst_contour, if( pos != slice.end ) { - double dx, dy, dist, max_dist = 0; + double dx, dy, max_dist_2_mul_segment_len_2 = 0; dx = end_pt.x - start_pt.x; dy = end_pt.y - start_pt.y; + double segment_len_2 = dx * dx + dy * dy; CV_Assert( dx != 0 || dy != 0 ); while( pos != slice.end ) { READ_PT(pt, pos); - dist = fabs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy); + double projection = ((pt.x - start_pt.x) * dx + (pt.y - start_pt.y) * dy); - if( dist > max_dist ) + double dist_2_mul_segment_len_2; + if ( projection < 0 ) { - max_dist = dist; + dist_2_mul_segment_len_2 = ((pt.x - start_pt.x) * (pt.x - start_pt.x) + (pt.y - start_pt.y) * (pt.y - start_pt.y)) * segment_len_2; + } else if ( projection > segment_len_2 ) + { + dist_2_mul_segment_len_2 = ((pt.x - end_pt.x) * (pt.x - end_pt.x) + (pt.y - end_pt.y) * (pt.y - end_pt.y)) * segment_len_2; + } else + { + double dist = ((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy); + dist_2_mul_segment_len_2 = dist * dist; + } + + if( dist_2_mul_segment_len_2 > max_dist_2_mul_segment_len_2 ) + { + max_dist_2_mul_segment_len_2 = dist_2_mul_segment_len_2; right_slice.start = (pos+count-1)%count; } } - le_eps = max_dist * max_dist <= eps * (dx * dx + dy * dy); + le_eps = max_dist_2_mul_segment_len_2 <= eps * segment_len_2; } else { diff --git a/modules/imgproc/src/bilateral_filter.dispatch.cpp b/modules/imgproc/src/bilateral_filter.dispatch.cpp index 8e0f1f5a95..1cc21521f6 100644 --- a/modules/imgproc/src/bilateral_filter.dispatch.cpp +++ b/modules/imgproc/src/bilateral_filter.dispatch.cpp @@ -321,7 +321,7 @@ bilateralFilter_32f( const Mat& src, Mat& dst, int d, } // parallel_for usage - CV_CPU_DISPATCH(bilateralFilterInvoker_32f, (cn, radius, maxk, space_ofs, temp, dst, scale_index, space_weight, expLUT), + CV_CPU_DISPATCH(bilateralFilterInvoker_32f, (cn, radius, maxk, space_ofs, temp, dst, scale_index, space_weight, expLUT, kExpNumBins), CV_CPU_DISPATCH_MODES_ALL); } diff --git a/modules/imgproc/src/bilateral_filter.simd.hpp b/modules/imgproc/src/bilateral_filter.simd.hpp index ab718eac91..d03ef01f59 100644 --- a/modules/imgproc/src/bilateral_filter.simd.hpp +++ b/modules/imgproc/src/bilateral_filter.simd.hpp @@ -58,7 +58,7 @@ void bilateralFilterInvoker_8u( int* space_ofs, float *space_weight, float *color_weight); void bilateralFilterInvoker_32f( int cn, int radius, int maxk, int *space_ofs, - const Mat& temp, Mat& dst, float scale_index, float *space_weight, float *expLUT); + const Mat& temp, Mat& dst, float scale_index, float *space_weight, float *expLUT, int kExpNumBins); #ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY @@ -495,9 +495,9 @@ class BilateralFilter_32f_Invoker : public: BilateralFilter_32f_Invoker(int _cn, int _radius, int _maxk, int *_space_ofs, - const Mat& _temp, Mat& _dest, float _scale_index, float *_space_weight, float *_expLUT) : + const Mat& _temp, Mat& _dest, float _scale_index, float *_space_weight, float *_expLUT, int _kExpNumBins) : cn(_cn), radius(_radius), maxk(_maxk), space_ofs(_space_ofs), - temp(&_temp), dest(&_dest), scale_index(_scale_index), space_weight(_space_weight), expLUT(_expLUT) + temp(&_temp), dest(&_dest), scale_index(_scale_index), space_weight(_space_weight), expLUT(_expLUT), kExpNumBins(_kExpNumBins) { } @@ -550,7 +550,7 @@ public: v_float32 val0 = vx_load(ksptr); v_float32 knan0 = v_not_nan(val0); v_float32 alpha0 = v_and(v_and(v_mul(v_absdiff(val0, rval0), sindex), v_not_nan(rval0)), knan0); - v_int32 idx0 = v_trunc(alpha0); + v_int32 idx0 = v_min(v_trunc(alpha0), vx_setall_s32(kExpNumBins)); alpha0 = v_sub(alpha0, v_cvt_f32(idx0)); v_float32 w0 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx0), alpha0, v_mul(v_lut(this->expLUT, idx0), v_sub(v_one, alpha0)))), knan0); v_wsum0 = v_add(v_wsum0, w0); @@ -560,7 +560,7 @@ public: v_float32 val1 = vx_load(ksptr + nlanes); v_float32 knan1 = v_not_nan(val1); v_float32 alpha1 = v_and(v_and(v_mul(v_absdiff(val1, rval1), sindex), v_not_nan(rval1)), knan1); - v_int32 idx1 = v_trunc(alpha1); + v_int32 idx1 = v_min(v_trunc(alpha1), vx_setall_s32(kExpNumBins)); alpha1 = v_sub(alpha1, v_cvt_f32(idx1)); v_float32 w1 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx1), alpha1, v_mul(v_lut(this->expLUT, idx1), v_sub(v_one, alpha1)))), knan1); v_wsum1 = v_add(v_wsum1, w1); @@ -570,7 +570,7 @@ public: v_float32 val2 = vx_load(ksptr + nlanes_2); v_float32 knan2 = v_not_nan(val2); v_float32 alpha2 = v_and(v_and(v_mul(v_absdiff(val2, rval2), sindex), v_not_nan(rval2)), knan2); - v_int32 idx2 = v_trunc(alpha2); + v_int32 idx2 = v_min(v_trunc(alpha2), vx_setall_s32(kExpNumBins)); alpha2 = v_sub(alpha2, v_cvt_f32(idx2)); v_float32 w2 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx2), alpha2, v_mul(v_lut(this->expLUT, idx2), v_sub(v_one, alpha2)))), knan2); v_wsum2 = v_add(v_wsum2, w2); @@ -580,7 +580,7 @@ public: v_float32 val3 = vx_load(ksptr + nlanes_3); v_float32 knan3 = v_not_nan(val3); v_float32 alpha3 = v_and(v_and(v_mul(v_absdiff(val3, rval3), sindex), v_not_nan(rval3)), knan3); - v_int32 idx3 = v_trunc(alpha3); + v_int32 idx3 = v_min(v_trunc(alpha3), vx_setall_s32(kExpNumBins)); alpha3 = v_sub(alpha3, v_cvt_f32(idx3)); v_float32 w3 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx3), alpha3, v_mul(v_lut(this->expLUT, idx3), v_sub(v_one, alpha3)))), knan3); v_wsum3 = v_add(v_wsum3, w3); @@ -611,7 +611,7 @@ public: v_float32 val0 = vx_load(ksptr); v_float32 knan0 = v_not_nan(val0); v_float32 alpha0 = v_and(v_and(v_mul(v_absdiff(val0, rval0), sindex), rval0_not_nan), knan0); - v_int32 idx0 = v_trunc(alpha0); + v_int32 idx0 = v_min(v_trunc(alpha0), vx_setall_s32(kExpNumBins)); alpha0 = v_sub(alpha0, v_cvt_f32(idx0)); v_float32 w0 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx0), alpha0, v_mul(v_lut(this->expLUT, idx0), v_sub(v_one, alpha0)))), knan0); v_wsum0 = v_add(v_wsum0, w0); @@ -621,7 +621,7 @@ public: v_float32 val1 = vx_load(ksptr + nlanes); v_float32 knan1 = v_not_nan(val1); v_float32 alpha1 = v_and(v_and(v_mul(v_absdiff(val1, rval1), sindex), rval1_not_nan), knan1); - v_int32 idx1 = v_trunc(alpha1); + v_int32 idx1 = v_min(v_trunc(alpha1), vx_setall_s32(kExpNumBins)); alpha1 = v_sub(alpha1, v_cvt_f32(idx1)); v_float32 w1 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx1), alpha1, v_mul(v_lut(this->expLUT, idx1), v_sub(v_one, alpha1)))), knan1); v_wsum1 = v_add(v_wsum1, w1); @@ -640,7 +640,7 @@ public: { const float* ksptr = sptr_j + space_ofs[k]; float val = *ksptr; - float alpha = std::abs(val - rval) * scale_index; + float alpha = std::min(std::abs(val - rval) * scale_index, (float)kExpNumBins); int idx = cvFloor(alpha); alpha -= idx; if (!cvIsNaN(val)) @@ -681,7 +681,7 @@ public: v_float32 knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); v_float32 alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - v_int32 idx = v_trunc(alpha); + v_int32 idx = v_min(v_trunc(alpha), vx_setall_s32(kExpNumBins)); alpha = v_sub(alpha, v_cvt_f32(idx)); v_float32 w = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); @@ -709,7 +709,7 @@ public: bool v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); float rb = rsptr[0], rg = rsptr[1], rr = rsptr[2]; bool r_NAN = cvIsNaN(rb) || cvIsNaN(rg) || cvIsNaN(rr); - float alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index; + float alpha = std::min((std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index, (float)kExpNumBins); int idx = cvFloor(alpha); alpha -= idx; if (!v_NAN) @@ -753,17 +753,18 @@ private: const Mat* temp; Mat *dest; float scale_index, *space_weight, *expLUT; + int kExpNumBins; }; } // namespace anon void bilateralFilterInvoker_32f( int cn, int radius, int maxk, int *space_ofs, - const Mat& temp, Mat& dst, float scale_index, float *space_weight, float *expLUT) + const Mat& temp, Mat& dst, float scale_index, float *space_weight, float *expLUT, int kExpNumBins) { CV_INSTRUMENT_REGION(); - BilateralFilter_32f_Invoker body(cn, radius, maxk, space_ofs, temp, dst, scale_index, space_weight, expLUT); + BilateralFilter_32f_Invoker body(cn, radius, maxk, space_ofs, temp, dst, scale_index, space_weight, expLUT, kExpNumBins); parallel_for_(Range(0, dst.rows), body, dst.total()/(double)(1<<16)); } diff --git a/modules/imgproc/src/convhull.cpp b/modules/imgproc/src/convhull.cpp index 632f1da131..d68f64dd5c 100644 --- a/modules/imgproc/src/convhull.cpp +++ b/modules/imgproc/src/convhull.cpp @@ -219,9 +219,9 @@ void convexHull( InputArray _points, OutputArray _hull, bool clockwise, bool ret } for( i = 0; i < tl_count-1; i++ ) - hullbuf[nout++] = int(pointer[tl_stack[i]] - data0); + hullbuf[nout++] = tl_stack[i]; for( i = tr_count - 1; i > 0; i-- ) - hullbuf[nout++] = int(pointer[tr_stack[i]] - data0); + hullbuf[nout++] = tr_stack[i]; int stop_idx = tr_count > 2 ? tr_stack[1] : tl_count > 2 ? tl_stack[tl_count - 2] : -1; // lower half @@ -257,9 +257,43 @@ void convexHull( InputArray _points, OutputArray _hull, bool clockwise, bool ret } for( i = 0; i < bl_count-1; i++ ) - hullbuf[nout++] = int(pointer[bl_stack[i]] - data0); + hullbuf[nout++] = bl_stack[i]; for( i = br_count-1; i > 0; i-- ) - hullbuf[nout++] = int(pointer[br_stack[i]] - data0); + hullbuf[nout++] = br_stack[i]; + + if (!returnPoints) + { + // Try keep monotonous indices in case of self-intersection. + for (i = 0; i < nout; ++i) + { + auto prev = pointer[hullbuf[(i == 0 ? nout : i) - 1]]; + auto next = pointer[hullbuf[(i + 1) % nout]]; + auto cur = pointer[hullbuf[i]]; + if ((prev < cur && cur < next) || (prev > cur && cur > next)) + { + continue; + } + for (int j = hullbuf[i] + 1; j < total; ++j) + { + cur = pointer[j]; + if (*pointer[hullbuf[i]] == *cur) + { + if ((prev < cur && cur < next) || (prev > cur && cur > next)) + { + hullbuf[i] = j; + break; + } + } + else + break; + } + } + + } + for (i = 0; i < nout; ++i) + { + hullbuf[i] = int(pointer[hullbuf[i]] - data0); + } // try to make the convex hull indices form // an ascending or descending sequence by the cyclic diff --git a/modules/imgproc/src/drawing.cpp b/modules/imgproc/src/drawing.cpp index 89898e2445..5badcdb70f 100644 --- a/modules/imgproc/src/drawing.cpp +++ b/modules/imgproc/src/drawing.cpp @@ -1668,7 +1668,7 @@ ThickLine( Mat& img, Point2l p0, Point2l p1, const void* color, { if( line_type < cv::LINE_AA ) { - if( line_type == 1 || line_type == 4 || shift == 0 ) + if( line_type == 1 || line_type == 8 || shift == 0 ) { p0.x = (p0.x + (XY_ONE>>1)) >> XY_SHIFT; p0.y = (p0.y + (XY_ONE>>1)) >> XY_SHIFT; diff --git a/modules/imgproc/src/filter.dispatch.cpp b/modules/imgproc/src/filter.dispatch.cpp index baece7da2b..95541253e6 100644 --- a/modules/imgproc/src/filter.dispatch.cpp +++ b/modules/imgproc/src/filter.dispatch.cpp @@ -1171,8 +1171,24 @@ static bool replacementFilter2D(int stype, int dtype, int kernel_type, int anchor_x, int anchor_y, double delta, int borderType, bool isSubmatrix) { + // Prioritize stateless implementation + int res = cv_hal_filter_stateless(src_data, src_step, stype, + dst_data, dst_step, dtype, width, height, + full_width, full_height, offset_x, offset_y, + kernel_data, kernel_step, kernel_type, + kernel_width, kernel_height, anchor_x, anchor_y, + delta, borderType, isSubmatrix, src_data == dst_data); + if (res == CV_HAL_ERROR_OK) + { + return true; + } else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation filter_stateless ==> " CVAUX_STR(cv_hal_filter_stateless) " returned %d (0x%08x)", res, res)); + } + cvhalFilter2D* ctx; - int res = cv_hal_filterInit(&ctx, kernel_data, kernel_step, kernel_type, kernel_width, kernel_height, width, height, + res = cv_hal_filterInit(&ctx, kernel_data, kernel_step, kernel_type, kernel_width, kernel_height, width, height, stype, dtype, borderType, delta, anchor_x, anchor_y, isSubmatrix, src_data == dst_data); if (res == CV_HAL_ERROR_NOT_IMPLEMENTED) { @@ -1385,8 +1401,23 @@ static bool replacementSepFilter(int stype, int dtype, int ktype, uchar * kernely_data, int kernely_len, int anchor_x, int anchor_y, double delta, int borderType) { + // Prioritize stateless implementation + int res = cv_hal_sepFilter_stateless(src_data, src_step, stype, + dst_data, dst_step, dtype, + width, height, full_width, full_height, offset_x, offset_y, + kernelx_data, kernelx_len, kernely_data, kernely_len, ktype, + anchor_x, anchor_y, delta, borderType); + if (res == CV_HAL_ERROR_OK) + { + return true; + } else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation sepFilter_stateless ==> " CVAUX_STR(cv_hal_sepFilter_stateless) " returned %d (0x%08x)", res, res)); + } + cvhalFilter2D *ctx; - int res = cv_hal_sepFilterInit(&ctx, stype, dtype, ktype, + res = cv_hal_sepFilterInit(&ctx, stype, dtype, ktype, kernelx_data, kernelx_len, kernely_data, kernely_len, anchor_x, anchor_y, delta, borderType); diff --git a/modules/imgproc/src/hal_replacement.hpp b/modules/imgproc/src/hal_replacement.hpp index 8bc20fbefd..6df3b77c9b 100644 --- a/modules/imgproc/src/hal_replacement.hpp +++ b/modules/imgproc/src/hal_replacement.hpp @@ -86,6 +86,40 @@ int my_hal_filterFree(cvhalFilter2D *context) { */ struct cvhalFilter2D {}; +/** + @brief 2D filtering in a stateless manner + @param src_data source image data + @param src_step source image step + @param src_type source image type + @param dst_data destination image data + @param dst_step destination image step + @param dst_type destination image type + @param width images width + @param height images height + @param full_width full width of source image (outside the ROI) + @param full_height full height of source image (outside the ROI) + @param offset_x source image ROI offset X + @param offset_y source image ROI offset Y + @param kernel_data pointer to kernel data + @param kernel_step kernel step + @param kernel_type kernel type (CV_8U, ...) + @param kernel_width kernel width + @param kernel_height kernel height + @param anchor_x relative X position of center point within the kernel + @param anchor_y relative Y position of center point within the kernel + @param delta added to pixel values + @param borderType border processing mode (CV_HAL_BORDER_REFLECT, ...) + @param isSubmatrix indicates whether the submatrices will be allowed as source image + @param allowInplace indicates whether the inplace operation will be possible + @sa cv::filter2D, cv::hal::Filter2D + */ +inline int hal_ni_filter_stateless(const uchar * src_data, size_t src_step, int src_type, + uchar * dst_data, size_t dst_step, int dst_type, + int width, int height, int full_width, int full_height, int offset_x, int offset_y, + const uchar * kernel_data, size_t kernel_step, int kernel_type, int kernel_width, int kernel_height, + int anchor_x, int anchor_y, double delta, int borderType, bool isSubmatrix, bool allowInplace) +{ return CV_HAL_ERROR_NOT_IMPLEMENTED; } + /** @brief hal_filterInit @param context double pointer to user-defined context @@ -131,11 +165,45 @@ inline int hal_ni_filter(cvhalFilter2D *context, uchar *src_data, size_t src_ste inline int hal_ni_filterFree(cvhalFilter2D *context) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } //! @cond IGNORED +#define cv_hal_filter_stateless hal_ni_filter_stateless #define cv_hal_filterInit hal_ni_filterInit #define cv_hal_filter hal_ni_filter #define cv_hal_filterFree hal_ni_filterFree //! @endcond +/** + @brief separable filtering in a stateless manner + @param src_data source image data + @param src_step source image step + @param src_type source image type + @param dst_data destination image data + @param dst_step destination image step + @param dst_type destination image type + @param width images width + @param height images height + @param full_width full width of source image (outside the ROI) + @param full_height full height of source image (outside the ROI) + @param offset_x source image ROI offset X + @param offset_y source image ROI offset Y + @param kernelx_data pointer to x-kernel data + @param kernelx_len x-kernel vector length + @param kernely_data pointer to y-kernel data + @param kernely_len y-kernel vector length + @param kernel_type kernel type (CV_8U, ...) + @param anchor_x relative X position of center point within the kernel + @param anchor_y relative Y position of center point within the kernel + @param delta added to pixel values + @param borderType border processing mode (CV_HAL_BORDER_REFLECT, ...) + @sa cv::sepFilter2D, cv::hal::SepFilter2D + */ +inline int hal_ni_sepFilter_stateless(const uchar * src_data, size_t src_step, int src_type, + uchar * dst_data, size_t dst_step, int dst_type, + int width, int height, int full_width, int full_height, int offset_x, int offset_y, + const uchar * kernelx_data, int kernelx_len, + const uchar * kernely_data, int kernely_len, + int kernel_type, int anchor_x, int anchor_y, double delta, int borderType) +{ return CV_HAL_ERROR_NOT_IMPLEMENTED; } + /** @brief hal_sepFilterInit @param context double pointer to user-defined context @@ -177,11 +245,54 @@ inline int hal_ni_sepFilter(cvhalFilter2D *context, uchar *src_data, size_t src_ inline int hal_ni_sepFilterFree(cvhalFilter2D *context) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } //! @cond IGNORED +#define cv_hal_sepFilter_stateless hal_ni_sepFilter_stateless #define cv_hal_sepFilterInit hal_ni_sepFilterInit #define cv_hal_sepFilter hal_ni_sepFilter #define cv_hal_sepFilterFree hal_ni_sepFilterFree //! @endcond +/** + @brief morphology in a stateless manner + @param operation morphology operation CV_HAL_MORPH_ERODE or CV_HAL_MORPH_DILATE + @param src_data source image data + @param src_step source image step + @param src_type source image type + @param dst_data destination image data + @param dst_step destination image step + @param dst_type destination image type + @param width images width + @param height images height + @param src_full_width full width of source image (outside the ROI) + @param src_full_height full height of source image (outside the ROI) + @param src_roi_x source image ROI X offset + @param src_roi_y source image ROI Y offset + @param dst_full_width full width of destination image + @param dst_full_height full height of destination image + @param dst_roi_x destination image ROI X offset + @param dst_roi_y destination image ROI Y offset + @param kernel_data pointer to kernel data + @param kernel_step kernel step + @param kernel_type kernel type (CV_8U, ...) + @param kernel_width kernel width + @param kernel_height kernel height + @param anchor_x relative X position of center point within the kernel + @param anchor_y relative Y position of center point within the kernel + @param borderType border processing mode (CV_HAL_BORDER_REFLECT, ...) + @param borderValue values to use for CV_HAL_BORDER_CONSTANT mode + @param iterations number of iterations + @param allowSubmatrix indicates whether the submatrices will be allowed as source image + @param allowInplace indicates whether the inplace operation will be possible + @sa cv::erode, cv::dilate, cv::morphologyEx, cv::hal::Morph + */ +inline int hal_ni_morph_stateless(int operation, const uchar * src_data, size_t src_step, int src_type, + uchar * dst_data, size_t dst_step, int dst_type, + int width, int height, int src_full_width, int src_full_height, int src_roi_x, int src_roi_y, + int dst_full_width, int dst_full_height, int dst_roi_x, int dst_roi_y, + const uchar * kernel_data, size_t kernel_step, int kernel_type, int kernel_width, int kernel_height, + int anchor_x, int anchor_y, int borderType, const double borderValue[4], + int iterations, bool allowSubmatrix, bool allowInplace) +{ return CV_HAL_ERROR_NOT_IMPLEMENTED; } + /** @brief hal_morphInit @param context double pointer to user-defined context @@ -233,6 +344,7 @@ inline int hal_ni_morph(cvhalFilter2D *context, uchar *src_data, size_t src_step inline int hal_ni_morphFree(cvhalFilter2D *context) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } //! @cond IGNORED +#define cv_hal_morph_stateless hal_ni_morph_stateless #define cv_hal_morphInit hal_ni_morphInit #define cv_hal_morph hal_ni_morph #define cv_hal_morphFree hal_ni_morphFree diff --git a/modules/imgproc/src/hough.cpp b/modules/imgproc/src/hough.cpp index 9e81b396da..644b9af283 100644 --- a/modules/imgproc/src/hough.cpp +++ b/modules/imgproc/src/hough.cpp @@ -1024,7 +1024,7 @@ void HoughLinesPointSet( InputArray _point, OutputArray _lines, int lines_max, i int r = idx - (n+1)*(numrho+2) - 1; line.rho = static_cast(min_rho) + r * (float)rho_step; line.angle = static_cast(min_theta) + n * (float)theta_step; - lines.push_back(Vec3d((double)accum[idx], (double)line.rho, (double)line.angle)); + lines.emplace_back((double)accum[idx], (double)line.rho, (double)line.angle); } Mat(lines).copyTo(_lines); @@ -1125,7 +1125,7 @@ public: { if (ptr[x]) { - list.push_back(Point(x, y)); + list.emplace_back(x, y); } } } @@ -1489,7 +1489,7 @@ protected: // Check if the circle has enough support if(maxCount > accThreshold) { - circlesLocal.push_back(EstimatedCircle(Vec3f(curCenter.x, curCenter.y, rBest), maxCount)); + circlesLocal.emplace_back(Vec3f(curCenter.x, curCenter.y, rBest), maxCount); } } @@ -1847,7 +1847,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector& circl continue; mdata[y*mstep + x] = (uchar)1; - stack.push_back(Point(x, y)); + stack.emplace_back(x, y); bool backtrace_mode = false; do @@ -1858,7 +1858,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector& circl int vy = dyData[p.y*dxystep + p.x]; float mag = std::sqrt((float)vx*vx+(float)vy*vy); - nz.push_back(Vec4f((float)p.x, (float)p.y, (float)vx, (float)vy)); + nz.emplace_back((float)p.x, (float)p.y, (float)vx, (float)vy); CV_Assert(mdata[p.y*mstep + p.x] == 1); int sx = cvRound(vx * RAY_FP_SCALE / mag); @@ -1903,7 +1903,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector& circl if( mdata[y_*mstep + x_] || !edgeData[y_*estep + x_]) continue; mdata[y_*mstep + x_] = (uchar)1; - stack.push_back(Point(x_, y_)); + stack.emplace_back(x_, y_); neighbors++; } @@ -1919,7 +1919,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector& circl // insert a special "stop marker" in the end of each // connected component to make sure we // finalize and analyze the arc segment - nz.push_back(Vec4f(0.f, 0.f, 0.f, 0.f)); + nz.emplace_back(0.f, 0.f, 0.f, 0.f); } } @@ -1951,7 +1951,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector& circl { float cx = (float)((left + x - 1)*dp*0.5f); float cy = (float)(y*dp); - centers.push_back(Point2f(cx, cy)); + centers.emplace_back(cx, cy); left = -1; } } @@ -2223,7 +2223,7 @@ static void HoughCirclesAlt( const Mat& img, std::vector& circl // (accepted ? '+' : '-'), cx, cy, rk, cjk.weight, count, max_runlen, cjk.mask); if( accepted ) - local_circles.push_back(EstimatedCircle(Vec3f(cx, cy, (float)rk), cjk.weight)); + local_circles.emplace_back(Vec3f(cx, cy, (float)rk), cjk.weight); } } } diff --git a/modules/imgproc/src/morph.dispatch.cpp b/modules/imgproc/src/morph.dispatch.cpp index 1d59fa787b..94326b545d 100644 --- a/modules/imgproc/src/morph.dispatch.cpp +++ b/modules/imgproc/src/morph.dispatch.cpp @@ -212,8 +212,22 @@ static bool halMorph(int op, int src_type, int dst_type, int kernel_width, int kernel_height, int anchor_x, int anchor_y, int borderType, const double borderValue[4], int iterations, bool isSubmatrix) { + // Prioritize stateless implementation + int res = cv_hal_morph_stateless(op, src_data, src_step, src_type, dst_data, dst_step, dst_type, width, height, + roi_width, roi_height, roi_x, roi_y, roi_width2, roi_height2, roi_x2, roi_y2, + kernel_data, kernel_step, kernel_type, kernel_width, kernel_height, anchor_x, anchor_y, + borderType, borderValue, iterations, isSubmatrix, src_data == dst_data); + if (res == CV_HAL_ERROR_OK) + { + return true; + } else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation morph_stateless ==> " CVAUX_STR(cv_hal_morph_stateless) " returned %d (0x%08x)", res, res)); + } + cvhalFilter2D * ctx; - int res = cv_hal_morphInit(&ctx, op, src_type, dst_type, width, height, + res = cv_hal_morphInit(&ctx, op, src_type, dst_type, width, height, kernel_type, kernel_data, kernel_step, kernel_width, kernel_height, anchor_x, anchor_y, borderType, borderValue, diff --git a/modules/imgproc/src/phasecorr_iterative.cpp b/modules/imgproc/src/phasecorr_iterative.cpp new file mode 100644 index 0000000000..ced9339c87 --- /dev/null +++ b/modules/imgproc/src/phasecorr_iterative.cpp @@ -0,0 +1,191 @@ +#include "precomp.hpp" +#include + +namespace { + +template +void calculateCrossPowerSpectrum(const cv::Mat& dft1, const cv::Mat& dft2, cv::Mat& cps) +{ + for (int row = 0; row < dft1.rows; ++row) + { + auto* cpsp = cps.ptr>(row); + const auto* dft1p = dft1.ptr>(row); + const auto* dft2p = dft2.ptr>(row); + for (int col = 0; col < dft1.cols; ++col) + { + const T re = dft1p[col][0] * dft2p[col][0] + dft1p[col][1] * dft2p[col][1]; + const T im = dft1p[col][0] * dft2p[col][1] - dft1p[col][1] * dft2p[col][0]; + const T mag = std::sqrt(re * re + im * im); + cpsp[col][0] = re / mag; + cpsp[col][1] = im / mag; + } + } +} + +cv::Mat calculateCrossPowerSpectrum(const cv::Mat& dft1, const cv::Mat& dft2) +{ + cv::Mat cps(dft1.rows, dft1.cols, dft1.type()); + if (dft1.type() == CV_32FC2) + calculateCrossPowerSpectrum(dft1, dft2, cps); + else if (dft1.type() == CV_64FC2) + calculateCrossPowerSpectrum(dft1, dft2, cps); + else + CV_Error(cv::Error::StsNotImplemented, "Only CV_32FC2 and CV_64FC2 types are supported"); + return cps; +} + +void fftshift(cv::Mat& out) +{ + int cx = out.cols / 2; + int cy = out.rows / 2; + cv::Mat q0(out, cv::Rect(0, 0, cx, cy)); + cv::Mat q1(out, cv::Rect(cx, 0, cx, cy)); + cv::Mat q2(out, cv::Rect(0, cy, cx, cy)); + cv::Mat q3(out, cv::Rect(cx, cy, cx, cy)); + + cv::Mat tmp; + q0.copyTo(tmp); + q3.copyTo(q0); + tmp.copyTo(q3); + q1.copyTo(tmp); + q2.copyTo(q1); + tmp.copyTo(q2); +} + +bool isOutOfBounds(const cv::Point2i& peak, const cv::Mat& mat, int size) +{ + return peak.x - size / 2 < 0 || peak.y - size / 2 < 0 || peak.x + size / 2 >= mat.cols || + peak.y + size / 2 >= mat.rows; +} + +bool reduceL2size(int& L2size) +{ + L2size -= 2; + return L2size >= 3; +} + +int getL1size(int L2Usize, double L1ratio) +{ + int L1size = static_cast(std::floor(L1ratio * L2Usize)); + return (L1size % 2) ? L1size : L1size + 1; +} + +cv::Point2d getPeakSubpixel(const cv::Mat& mat) +{ + const auto m = moments(mat); + return cv::Point2d(m.m10 / m.m00, m.m01 / m.m00); +} + +bool accuracyReached(const cv::Point2d& L1peak, const cv::Point2d& L1mid) +{ + return std::abs(L1peak.x - L1mid.x) < 0.5 && std::abs(L1peak.y - L1mid.y) < 0.5; +} + +cv::Point2d getSubpixelShift(const cv::Mat& L3, + const cv::Point2d& L3peak, + const cv::Point2d& L3mid, + int L2size) +{ + while (isOutOfBounds(L3peak, L3, L2size)) + if (!reduceL2size(L2size)) + return L3peak - L3mid; + + cv::Mat L2 = L3(cv::Rect(static_cast(L3peak.x - L2size / 2), + static_cast(L3peak.y - L2size / 2), + L2size, + L2size)); + cv::Point2d L2peak = getPeakSubpixel(L2); + cv::Point2d L2mid(L2.cols / 2, L2.rows / 2); + return L3peak - L3mid + L2peak - L2mid; +} + +} // namespace + +cv::Point2d + cv::phaseCorrelateIterative(InputArray _src1, InputArray _src2, int L2size, int maxIters) +{ + CV_INSTRUMENT_REGION(); + + Mat src1 = _src1.getMat(); + Mat src2 = _src2.getMat(); + + CV_Assert(src1.type() == src2.type()); + CV_Assert(src1.size() == src2.size()); + CV_Assert(src1.type() == CV_32FC1 || src1.type() == CV_64FC1); + + // apply DFT window to input images + Mat window; + createHanningWindow(window, src1.size(), _src1.type()); + Mat image1, image2; + multiply(_src1, window, image1); + multiply(_src2, window, image2); + + // compute the DFTs of input images + dft(image1, image1, DFT_COMPLEX_OUTPUT); + dft(image2, image2, DFT_COMPLEX_OUTPUT); + + // compute the phase correlation landscape L3 + Mat L3 = calculateCrossPowerSpectrum(image1, image2); + dft(L3, L3, DFT_INVERSE | DFT_SCALE | DFT_REAL_OUTPUT); + fftshift(L3); + Point2d L3mid(L3.cols / 2, L3.rows / 2); + + // calculate the maximum correlation location + Point2i L3peak; + minMaxLoc(L3, nullptr, nullptr, nullptr, &L3peak); + + // reduce the L2size as long as L2 is out of bounds of L3 + while (isOutOfBounds(L3peak, L3, L2size)) + if (!reduceL2size(L2size)) + return Point2d(L3peak) - L3mid; + + // extract the L2 maximum correlation neighborhood from L3 + Mat L2 = L3(Rect(L3peak.x - L2size / 2, L3peak.y - L2size / 2, L2size, L2size)); + + // upsample L2 maximum correlation neighborhood to get L2U + Mat L2U; + const int L2Usize = 223; // empirically determined optimal constant + resize(L2, L2U, {L2Usize, L2Usize}, 0, 0, INTER_LINEAR); + const Point2d L2Umid(L2U.cols / 2, L2U.rows / 2); + + // run the iterative refinement algorithm using the specified L1 ratio + // gradually decrease L1 ratio if convergence is not achieved + const double L1ratioBase = 0.45; // empirically determined optimal constant + const double L1ratioStep = 0.025; + for (double L1ratio = L1ratioBase; getL1size(L2U.cols, L1ratio) > 0; L1ratio -= L1ratioStep) + { + Point2d L2Upeak = L2Umid; // reset the accumulated L2U peak position + const int L1size = getL1size(L2U.cols, L1ratio); // calculate the current L1 size + const Point2d L1mid(L1size / 2, L1size / 2); // update the L1 mid position + + // perform the iterative refinement algorithm + for (int iter = 0; iter < maxIters; ++iter) + { + // verify that the L1 region is within the L2U region + if (isOutOfBounds(L2Upeak, L2U, L1size)) + break; + + // extract the L1 region from L2U + const Mat L1 = L2U(Rect(static_cast(L2Upeak.x - L1size / 2), + static_cast(L2Upeak.y - L1size / 2), + L1size, + L1size)); + + // calculate the centroid location + const Point2d L1peak = getPeakSubpixel(L1); + + // add the contribution of the current iteration to the accumulated L2U peak location + L2Upeak += Point2d(std::round(L1peak.x - L1mid.x), std::round(L1peak.y - L1mid.y)); + + // check for convergence + if (accuracyReached(L1peak, L1mid)) + // return the refined subpixel image shift + return Point2d(L3peak) - L3mid + + (L2Upeak - L2Umid + L1peak - L1mid) / + (static_cast(L2Usize) / L2size); + } + } + + // iterative refinement failed to converge, return non-iterative subpixel shift + return getSubpixelShift(L3, Point2d(L3peak), L3mid, L2size); +} diff --git a/modules/imgproc/src/rotcalipers.cpp b/modules/imgproc/src/rotcalipers.cpp index 211d254d74..79bd77b684 100644 --- a/modules/imgproc/src/rotcalipers.cpp +++ b/modules/imgproc/src/rotcalipers.cpp @@ -365,7 +365,7 @@ cv::RotatedRect cv::minAreaRect( InputArray _points ) Mat hull; Point2f out[3]; RotatedRect box; - box.angle = -(float)CV_PI / 2; // default angle for box without rotation and single point + double angle = -CV_PI / 2; // default angle for box without rotation and single point static const bool clockwise = false; convexHull(_points, hull, clockwise, true); @@ -390,7 +390,7 @@ cv::RotatedRect cv::minAreaRect( InputArray _points ) if (out[1].x == 0.f && out[1].y > 0.f) std::swap(box.size.width, box.size.height); else - box.angle += (float)atan2( (double)out[1].y, (double)out[1].x ); + angle = -atan2( (double)out[1].x, (double)out[1].y ); } else if( n == 2 ) { @@ -406,12 +406,12 @@ cv::RotatedRect cv::minAreaRect( InputArray _points ) } else if (dy < 0) { - box.angle = (float)atan2( dy, dx ); + angle = atan2( dy, dx ); std::swap(box.size.width, box.size.height); } else if (dy > 0) { - box.angle += (float)atan2( dy, dx ); + angle = -atan2( dx, dy ); } } else @@ -420,7 +420,7 @@ cv::RotatedRect cv::minAreaRect( InputArray _points ) box.center = hpoints[0]; } - box.angle = (float)(box.angle*180/CV_PI); + box.angle = (float)(angle*180/CV_PI); CV_DbgCheckGE(box.angle, -90.0f, ""); CV_DbgCheckLT(box.angle, 0.0f, ""); return box; diff --git a/modules/imgproc/src/stackblur.cpp b/modules/imgproc/src/stackblur.cpp index a69e4b4101..da34d3249d 100644 --- a/modules/imgproc/src/stackblur.cpp +++ b/modules/imgproc/src/stackblur.cpp @@ -41,6 +41,7 @@ OTHER DEALINGS IN THE SOFTWARE. #include "opencv2/core/hal/intrin.hpp" #include +#include using namespace std; @@ -1199,12 +1200,17 @@ void stackBlur(InputArray _src, OutputArray _dst, Size ksize) CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 && ksize.height > 0 && ksize.height % 2 == 1 ); - int radiusH = ksize.height / 2; - int radiusW = ksize.width / 2; - int stype = _src.type(), sdepth = _src.depth(); Mat src = _src.getMat(); + if (ksize.width > src.cols) + ksize.width = (src.cols % 2 == 0) ? std::max(1, src.cols - 1) : src.cols; + if (ksize.height > src.rows) + ksize.height = (src.rows % 2 == 0) ? std::max(1, src.rows - 1) : src.rows; + + int radiusH = ksize.height / 2; + int radiusW = ksize.width / 2; + if (ksize.width == 1) { _src.copyTo(_dst); diff --git a/modules/imgproc/src/subdivision2d.cpp b/modules/imgproc/src/subdivision2d.cpp index 671430ffb5..78432f456d 100644 --- a/modules/imgproc/src/subdivision2d.cpp +++ b/modules/imgproc/src/subdivision2d.cpp @@ -265,7 +265,7 @@ int Subdiv2D::newPoint(Point2f pt, bool isvirtual, int firstEdge) { if( freePoint == 0 ) { - vtx.push_back(Vertex()); + vtx.emplace_back(); freePoint = (int)(vtx.size()-1); } int vidx = freePoint; @@ -520,8 +520,8 @@ void Subdiv2D::initDelaunay( Rect rect ) Point2f ppB( rx, ry + big_coord ); Point2f ppC( rx - big_coord, ry - big_coord ); - vtx.push_back(Vertex()); - qedges.push_back(QuadEdge()); + vtx.emplace_back(); + qedges.emplace_back(); freeQEdge = 0; freePoint = 0; @@ -566,8 +566,8 @@ void Subdiv2D::initDelaunay( Rect2f rect ) Point2f ppB( rx, ry + big_coord ); Point2f ppC( rx - big_coord, ry - big_coord ); - vtx.push_back(Vertex()); - qedges.push_back(QuadEdge()); + vtx.emplace_back(); + qedges.emplace_back(); freeQEdge = 0; freePoint = 0; @@ -784,7 +784,7 @@ void Subdiv2D::getEdgeList(std::vector& edgeList) const { Point2f org = vtx[qedges[i].pt[0]].pt; Point2f dst = vtx[qedges[i].pt[2]].pt; - edgeList.push_back(Vec4f(org.x, org.y, dst.x, dst.y)); + edgeList.emplace_back(org.x, org.y, dst.x, dst.y); } } } @@ -836,7 +836,7 @@ void Subdiv2D::getTriangleList(std::vector& triangleList) const edgemask[edge_a] = true; edgemask[edge_b] = true; edgemask[edge_c] = true; - triangleList.push_back(Vec6f(a.x, a.y, b.x, b.y, c.x, c.y)); + triangleList.emplace_back(a.x, a.y, b.x, b.y, c.x, c.y); } } diff --git a/modules/imgproc/test/test_approxpoly.cpp b/modules/imgproc/test/test_approxpoly.cpp index 410673ad53..9a70d46408 100644 --- a/modules/imgproc/test/test_approxpoly.cpp +++ b/modules/imgproc/test/test_approxpoly.cpp @@ -76,6 +76,19 @@ TEST(Imgproc_ApproxPoly, bad_epsilon) ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false)); } +TEST(Imgproc_ApproxPoly, distace_between_point_and_segment) +{ + vector inputPoints = { + { {0.f, 0.f}, {4.f, 2.f}, {11.f, 1.f}, {8.f, 0.f} } + }; + std::vector result; + approxPolyDP(inputPoints, result, 1.9, false); + vector expectedResult = { + { {0.f, 0.f}, {11.f, 1.f}, {8.f, 0.f} } + }; + ASSERT_EQ(result, expectedResult); +} + struct ApproxPolyN: public testing::Test { void SetUp() diff --git a/modules/imgproc/test/test_bilateral_filter.cpp b/modules/imgproc/test/test_bilateral_filter.cpp index 8f800a1215..36d43c6795 100644 --- a/modules/imgproc/test/test_bilateral_filter.cpp +++ b/modules/imgproc/test/test_bilateral_filter.cpp @@ -291,4 +291,33 @@ namespace opencv_test { namespace { test.safe_run(); } + // Regression test for issue #28254 + // Out-of-bounds read in AVX2 bilateralFilter 32f path with BORDER_CONSTANT + TEST(Imgproc_BilateralFilter, regression_28254_oob_read) + { + // Create a 64x64 CV_32FC1 image with values in range [100, 200] + // Image must be large enough (width >= 32) to trigger SIMD/AVX2 code path. + // Values are set so BORDER_CONSTANT padding (default 0) is outside the range, + // which triggers the out-of-bounds condition in the LUT access. + cv::Mat src(64, 64, CV_32FC1); + cv::randu(src, 100.0f, 200.0f); + cv::Mat dst; + + // Parameters that trigger the bug + int d = -1; + double sigmaColor = 2.7; + double sigmaSpace = 44.5; + int borderType = cv::BORDER_CONSTANT; + + // This should not crash or trigger AddressSanitizer + EXPECT_NO_THROW( + cv::bilateralFilter(src, dst, d, sigmaColor, sigmaSpace, borderType) + ); + + // Verify output is valid + EXPECT_FALSE(dst.empty()); + EXPECT_EQ(dst.size(), src.size()); + EXPECT_EQ(dst.type(), src.type()); + } + }} // namespace diff --git a/modules/imgproc/test/test_contours.cpp b/modules/imgproc/test/test_contours.cpp index 2bcd642a17..3fd3df8468 100644 --- a/modules/imgproc/test/test_contours.cpp +++ b/modules/imgproc/test/test_contours.cpp @@ -233,5 +233,20 @@ TEST(Imgproc_DrawContours, MatListOfMatIntScalarInt) EXPECT_EQ(nz, 0); } +TEST(Imgproc_Moments, degenerateContours) +{ + std::vector c1; + c1.push_back(cv::Point(10,10)); + cv::Moments m1 = cv::moments(c1, false); + EXPECT_EQ(m1.m00, 0); + + std::vector c2; + c2.push_back(cv::Point(0,0)); + c2.push_back(cv::Point(5,5)); + c2.push_back(cv::Point(10,10)); + cv::Moments m2 = cv::moments(c2, false); + EXPECT_EQ(m2.m00, 0); +} + }} // namespace /* End of file. */ diff --git a/modules/imgproc/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp index 774bee7af6..714ed75ba0 100644 --- a/modules/imgproc/test/test_convhull.cpp +++ b/modules/imgproc/test/test_convhull.cpp @@ -304,9 +304,11 @@ TEST(Imgproc_ConvexityDefects, ordering_4539) vector hull_ind; vector defects; +#if 0 // deprecated behavior // first, check the original contour as-is, without intermediate fillPoly/drawContours. convexHull(contour_, hull_ind, false, false); EXPECT_THROW( convexityDefects(contour_, hull_ind, defects), cv::Exception ); +#endif int scale = 20; contour_ *= (double)scale; @@ -319,10 +321,12 @@ TEST(Imgproc_ConvexityDefects, ordering_4539) findContours(canvas_gray, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE); convexHull(contours[0], hull_ind, false, false); +#if 0 // deprecated behavior // the original contour contains self-intersections, // therefore convexHull does not return a monotonous sequence of points // and therefore convexityDefects throws an exception EXPECT_THROW( convexityDefects(contours[0], hull_ind, defects), cv::Exception ); +#endif #if 1 // one way to eliminate the contour self-intersection in this particular case is to apply dilate(), @@ -1049,7 +1053,7 @@ TEST_P(minEnclosingTriangle_Modes, accuracy) const Mat midPoint = (cur + next) / 2; EXPECT_TRUE(isPointOnHull(hull, midPoint)); - // at least one of hull edges must be on tirangle edge + // at least one of hull edges must be on triangle edge hasEdgeOnHull = hasEdgeOnHull || isEdgeOnHull(hull, cur, next); } EXPECT_TRUE(hasEdgeOnHull); @@ -1279,10 +1283,60 @@ INSTANTIATE_TEST_CASE_P(Imgproc, minAreaRect_of_line, testing::Values( std::make_tuple(Point2f(10, 15), Point2f(10, 25), Point2f(10, 20), Size2f(10, 0), -90.f), std::make_tuple(Point2f(450, 500), Point2f(508, 500), Point2f(479, 500), Size2f(0, 58), -90.f), - std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301002f), + std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301041f), std::make_tuple(Point2f(9, 19), Point2f(4, 7), Point2f(6.5, 13), Size2f(0, 13), -22.6198654f) )); +typedef testing::TestWithParam, Mat>, bool> > convexHull_monotonous; +TEST_P(convexHull_monotonous, self_intersecting_contour) +{ + std::vector contour = get<0>(get<0>(GetParam())); + Mat ref = get<1>(get<0>(GetParam())).clone(); + bool clockwise = get<1>(GetParam()); + if (!clockwise) + { + std::reverse(ref.begin(), ref.end()); + } + + Mat indices; + convexHull(contour, indices, clockwise, false); + + Point minLoc; + minMaxLoc(indices, nullptr, nullptr, &minLoc); + std::rotate(indices.begin(), indices.begin() + minLoc.y, indices.end()); + + minMaxLoc(ref, nullptr, nullptr, &minLoc); + std::rotate(ref.begin(), ref.begin() + minLoc.y, ref.end()); + + ASSERT_EQ( cvtest::norm(indices, ref, NORM_INF), 0) << indices; +} +INSTANTIATE_TEST_CASE_P(Imgproc, convexHull_monotonous, + testing::Combine( + testing::Values( + std::make_tuple( + std::vector{ + Point(3, 2), Point(3, 4), Point(2, 5), Point(1, 5), + Point(2, 5), Point(3, 4), Point(6, 4), Point(6, 2) + }, + (Mat_(5, 1) << 0, 3, 4, 6, 7) + ), + std::make_tuple( + std::vector{ + Point(3, -2), Point(3, -4), Point(2, -5), Point(1, -5), + Point(2, -5), Point(3, -4), Point(6, -4), Point(6, -2) + }, + (Mat_(5, 1) << 3, 0, 7, 6, 4) + ), + std::make_tuple( + std::vector{ + Point(1, 1), Point(1, 0), Point(0, 0), Point(1, 0), Point(0, 1) + }, + (Mat_(4, 1) << 0, 1, 2, 4) + ) + ), + testing::Bool() +)); + }} // namespace /* End of file. */ diff --git a/modules/imgproc/test/test_drawing.cpp b/modules/imgproc/test/test_drawing.cpp index 9424a38133..b2889cd4da 100755 --- a/modules/imgproc/test/test_drawing.cpp +++ b/modules/imgproc/test/test_drawing.cpp @@ -1281,4 +1281,28 @@ TEST(Drawing, contours_filled) } } +// Test for LINE_4 vs LINE_8 connectivity behavior +// Regression test for issue #26413 +TEST(Drawing, line_connectivity_regression_26413) +{ + Mat img4(10, 10, CV_8UC1, Scalar(0)); + Mat img8(10, 10, CV_8UC1, Scalar(0)); + + // Draw a diagonal line from (0,0) to (9,9) + // LINE_4 (4-connected) should produce staircase pattern (no diagonals) + // LINE_8 (8-connected) should produce diagonal steps + line(img4, Point(0, 0), Point(9, 9), Scalar(255), 1, LINE_4); + line(img8, Point(0, 0), Point(9, 9), Scalar(255), 1, LINE_8); + + int count4 = countNonZero(img4); + int count8 = countNonZero(img8); + + // LINE_8 for a 10-pixel diagonal should have exactly 10 pixels + EXPECT_EQ(10, count8) << "LINE_8 diagonal from (0,0) to (9,9) should have 10 pixels"; + + // LINE_4 for a 10-pixel diagonal should have approximately 19 pixels + // (needs both horizontal and vertical steps) + EXPECT_GT(count4, 15) << "LINE_4 diagonal should have significantly more pixels due to staircase"; +} + }} // namespace diff --git a/modules/imgproc/test/test_goodfeaturetotrack.cpp b/modules/imgproc/test/test_goodfeaturetotrack.cpp index b1c5ee9200..9110e7c2a2 100644 --- a/modules/imgproc/test/test_goodfeaturetotrack.cpp +++ b/modules/imgproc/test/test_goodfeaturetotrack.cpp @@ -509,7 +509,8 @@ int CV_GoodFeatureToTTest::validate_test_results( int test_case_idx ) EXPECT_LE(e, eps); // never true ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - for(int i = 0; i < (int)std::min((unsigned int)(cornersQuality.size()), (unsigned int)(cornersQuality.size())); i++) { + int min_size = (int)std::min(cornersQuality.size(), RefcornersQuality.size()); + for(int i = 0; i < min_size; i++) { if (std::abs(cornersQuality[i] - RefcornersQuality[i]) > eps * std::max(cornersQuality[i], RefcornersQuality[i])) printf("i = %i Quality %2.6f Quality ref %2.6f\n", i, cornersQuality[i], RefcornersQuality[i]); } diff --git a/modules/imgproc/test/test_ipc.cpp b/modules/imgproc/test/test_ipc.cpp new file mode 100644 index 0000000000..321b1f2fe3 --- /dev/null +++ b/modules/imgproc/test/test_ipc.cpp @@ -0,0 +1,117 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "test_precomp.hpp" +#include + +namespace opencv_test { namespace { + +Mat CropMid(InputArray src, int w, int h) +{ + Mat mat = src.getMat(); + return mat(Rect(mat.cols / 2 - w / 2, mat.rows / 2 - h / 2, w, h)); +} + +Mat GenerateTestImage(Size size) +{ + Mat image = Mat::zeros(size.height * 2, size.width * 2, CV_32F); + rectangle(image, + Point(static_cast(size.width * 0.1), static_cast(size.height * 0.1)), + Point(static_cast(size.width * 0.9), static_cast(size.height * 0.9)), + Scalar(1), + -1); + return image; +} + +void TestPhaseCorrelationIterative(const Size& size, const double maxShift) +{ + const auto iters = std::max(201., maxShift * 10 + 1); + const Point2d shiftOffset(-maxShift * 0.5, -maxShift * 0.5); + Mat image1 = GenerateTestImage(size); + Mat crop1 = CropMid(image1, size.width, size.height); + Mat image2 = image1.clone(); + + std::vector pcErrors; + std::vector ipcErrors; + + for (int i = 0; i < iters; ++i) + { + const auto shift = + Point2d(maxShift * i / (iters - 1), maxShift * i / (iters - 1)) + shiftOffset; + const Mat Tmat = (Mat_(2, 3) << 1., 0., shift.x, 0., 1., shift.y); + warpAffine(image1, image2, Tmat, image2.size()); + Mat crop2 = CropMid(image2, size.width, size.height); + const auto ipcshift = phaseCorrelateIterative(crop1, crop2); + const auto pcshift = phaseCorrelate(crop1, crop2); + + pcErrors.push_back( + 0.5 * std::abs(pcshift.x - shift.y) + 0.5 * std::abs(pcshift.y - shift.x)); + ipcErrors.push_back( + 0.5 * std::abs(ipcshift.x - shift.y) + 0.5 * std::abs(ipcshift.y - shift.x)); + + // error should be low + EXPECT_NEAR(ipcshift.x - shift.x, 0.0, 0.1); + EXPECT_NEAR(ipcshift.y - shift.y, 0.0, 0.1); + } + + cv::Scalar pcMean, pcStddev, ipcMean, ipcStddev; + meanStdDev(ipcErrors, ipcMean, ipcStddev); + meanStdDev(pcErrors, pcMean, pcStddev); + + // average error should be low + ASSERT_LT(ipcMean[0], 0.03); + // average error should be less than non-iterative average error + ASSERT_LT(ipcMean[0], pcMean[0]); + // error stddev should be less than non-iterative error stddev + ASSERT_LT(ipcStddev[0], pcStddev[0]); +} + + +TEST(Imgproc_PhaseCorrelationIterative, 256x128_accuracy) +{ + TestPhaseCorrelationIterative(Size(256, 128), 1); +} + +TEST(Imgproc_PhaseCorrelationIterative, 64x64_accuracy_shift_1) +{ + TestPhaseCorrelationIterative(Size(64, 64), 1); +} + +TEST(Imgproc_PhaseCorrelationIterative, 64x64_accuracy_shift_16) +{ + TestPhaseCorrelationIterative(Size(64, 64), 16); +} + +TEST(Imgproc_PhaseCorrelationIterative, 0x0_image) +{ + ASSERT_ANY_THROW(TestPhaseCorrelationIterative(Size(0, 0), 1)); +} + +TEST(Imgproc_PhaseCorrelationIterative, 1x1_image) +{ + ASSERT_ANY_THROW(TestPhaseCorrelationIterative(Size(1, 1), 1)); +} + +TEST(Imgproc_PhaseCorrelationIterative, accuracy_real_img) +{ + Mat img = imread(cvtest::TS::ptr()->get_data_path() + "shared/airplane.png", IMREAD_GRAYSCALE); + if (img.empty()) + return; + img.convertTo(img, CV_64FC1); + + const int xLen = 256; + const int yLen = 256; + const int xShift = 40; + const int yShift = 14; + + Mat roi1 = img(Rect(xShift, yShift, xLen, yLen)); + Mat roi2 = img(Rect(0, 0, xLen, yLen)); + + const Point2d ipcShift = phaseCorrelateIterative(roi1, roi2); + + ASSERT_NEAR(ipcShift.x, (double)xShift, 1.); + ASSERT_NEAR(ipcShift.y, (double)yShift, 1.); +} + +}} // namespace opencv_test diff --git a/modules/imgproc/test/test_stackblur.cpp b/modules/imgproc/test/test_stackblur.cpp index b7525467b3..6c6dd42a67 100644 --- a/modules/imgproc/test/test_stackblur.cpp +++ b/modules/imgproc/test/test_stackblur.cpp @@ -311,5 +311,18 @@ TEST_P(StackBlur_GaussianBlur, compare) } INSTANTIATE_TEST_CASE_P(Imgproc, StackBlur_GaussianBlur, testing::Values(CV_8U, CV_16S, CV_16U, CV_32F)); + +TEST(Imgproc_StackBlur, regression_28233) +{ + Mat src1(1, 1, CV_8UC1, Scalar(123)); + Mat dst1; + EXPECT_NO_THROW(stackBlur(src1, dst1, Size(9, 1))); + EXPECT_EQ(dst1.at(0, 0), 123); + + Mat src2(3, 3, CV_8UC1, Scalar(50)); + Mat dst2; + EXPECT_NO_THROW(stackBlur(src2, dst2, Size(11, 11))); + EXPECT_EQ(dst2.at(1, 1), 50); +} } } diff --git a/modules/java/generator/CMakeLists.txt b/modules/java/generator/CMakeLists.txt index 130e6d5fec..fb1f159e71 100644 --- a/modules/java/generator/CMakeLists.txt +++ b/modules/java/generator/CMakeLists.txt @@ -62,6 +62,24 @@ ocv_bindings_generator_populate_preprocessor_definitions( opencv_preprocessor_defs ) +if(OPENCV_JAVA_CLEANING_API) + if(OPENCV_JAVA_CLEANING_API STREQUAL "finalize") + set(opencv_supported_cleaners "false") + elseif(OPENCV_JAVA_CLEANING_API STREQUAL "cleaner") + set(opencv_supported_cleaners "true") + else() + message(FATAL_ERROR "OPENCV_JAVA_CLEANING_API should be one of \"finalize\" or \"cleaner\"") + endif() +else() + if(ANDROID OR (Java_VERSION VERSION_LESS 9)) + message(STATUS "Set Cleaners to False") + set(opencv_supported_cleaners "false") + else() + message(STATUS "Set Cleaners to True") + set(opencv_supported_cleaners "true") + endif() +endif(OPENCV_JAVA_CLEANING_API) + set(CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/gen_java.json") set(__config_str "{ @@ -74,7 +92,8 @@ ${opencv_preprocessor_defs} }, \"files_remap\": [ ${__remap_config} - ] + ], + \"support_cleaners\": ${opencv_supported_cleaners} } ") if(EXISTS "${CONFIG_FILE}") diff --git a/modules/java/generator/gen_java.py b/modules/java/generator/gen_java.py index d4c44104a7..9ffe6701ad 100755 --- a/modules/java/generator/gen_java.py +++ b/modules/java/generator/gen_java.py @@ -23,6 +23,7 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) # list of modules + files remap config = None ROOT_DIR = None +USE_CLEANERS = True FILES_REMAP = {} def checkFileRemap(path): path = os.path.realpath(path) @@ -377,6 +378,7 @@ class ClassInfo(GeneralInfo): jmodule = make_jmodule(m), name = self.name, jname = self.jname, + jcleaner = "long nativeObjCopy = nativeObj;\n org.opencv.core.Mat.cleaner.register(this, () -> delete(nativeObjCopy));" if USE_CLEANERS else "", imports = "\n".join(self.getAllImports(M)), docs = self.docstring, annotation = "\n" + "\n".join(self.annotation) if self.annotation else "", @@ -961,6 +963,7 @@ class JavaWrapperGenerator(object): tail = ")" else: ret_val = "nativeObj = " + tail = ";\n long nativeObjCopy = nativeObj;\n org.opencv.core.Mat.cleaner.register(this, () -> delete(nativeObjCopy))" if USE_CLEANERS else "" ret = "" elif self.isWrapped(ret_type): # wrapped class constructor = self.getClass(ret_type).jname + "(" @@ -1228,8 +1231,9 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${jmodule}_${clazz}_$fname ci.cpp_code.write("\n".join(fn["cpp_code"])) if ci.name != self.Module or ci.base: - # finalize() - ci.j_code.write( + # finalize() for old Java + if not USE_CLEANERS: + ci.j_code.write( """ @Override protected void finalize() throws Throwable { @@ -1239,7 +1243,7 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${jmodule}_${clazz}_$fname ci.jn_code.write( """ - // native support for java finalize() + // native support for java finalize() or cleaner private static native void delete(long nativeObj); """ ) @@ -1247,7 +1251,7 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${jmodule}_${clazz}_$fname ci.cpp_code.write( """ // -// native support for java finalize() +// native support for java finalize() or cleaner // static void %(cls)s::delete( __int64 self ) // JNIEXPORT void JNICALL Java_org_opencv_%(module)s_%(j_cls)s_delete(JNIEnv*, jclass, jlong); @@ -1468,6 +1472,12 @@ if __name__ == "__main__": FILES_REMAP = { os.path.realpath(os.path.join(ROOT_DIR, f['src'])): f['target'] for f in config['files_remap'] } logging.info("\nRemapped configured files (%d):\n%s", len(FILES_REMAP), pformat(FILES_REMAP)) + USE_CLEANERS = config['support_cleaners'] + if (USE_CLEANERS): + logging.info("\nUse Java 9+ cleaners\n") + else: + logging.info("\nUse old style Java finalize()\n") + dstdir = "./gen" jni_path = os.path.join(dstdir, 'cpp'); mkdir_p(jni_path) java_base_path = os.path.join(dstdir, 'java'); mkdir_p(java_base_path) @@ -1557,6 +1567,17 @@ if __name__ == "__main__": preprocessor_definitions) else: logging.info("No generated code for module: %s", module) + + # Copy Cleaner / finalize() related files + if USE_CLEANERS: + cleaner_src = os.path.join(SCRIPT_DIR, "src", "java9", "CleanableMat.java") + else: + cleaner_src = os.path.join(SCRIPT_DIR, "src", "java_classic", "CleanableMat.java") + + cleaner_dst = os.path.join(java_base_path, "org", "opencv", "core", "CleanableMat.java") + print("cleaner_dst: ", cleaner_dst) + copyfile(cleaner_src, cleaner_dst) + generator.finalize(jni_path) print('Generated files: %d (updated %d)' % (total_files, updated_files)) diff --git a/modules/java/generator/src/cpp/CleanableMat.cpp b/modules/java/generator/src/cpp/CleanableMat.cpp new file mode 100644 index 0000000000..a1110cd391 --- /dev/null +++ b/modules/java/generator/src/cpp/CleanableMat.cpp @@ -0,0 +1,28 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#include "opencv2/core.hpp" + +#define LOG_TAG "org.opencv.core.CleanableMat" +#include "common.h" +#include + +using namespace cv; + +extern "C" { + // + // native support for java finalize() or cleaners + // static void CleanableMat::n_delete( __int64 self ) + // + + JNIEXPORT void JNICALL Java_org_opencv_core_CleanableMat_n_1delete + (JNIEnv*, jclass, jlong self); + + JNIEXPORT void JNICALL Java_org_opencv_core_CleanableMat_n_1delete + (JNIEnv*, jclass, jlong self) + { + // LOGD("CleanableMat.n_delete() called\n"); + delete (Mat*) self; + } +} diff --git a/modules/java/generator/src/cpp/Mat.cpp b/modules/java/generator/src/cpp/Mat.cpp index f43a4c6ed5..128a4fc08a 100644 --- a/modules/java/generator/src/cpp/Mat.cpp +++ b/modules/java/generator/src/cpp/Mat.cpp @@ -2114,22 +2114,6 @@ JNIEXPORT jlong JNICALL Java_org_opencv_core_Mat_n_1zeros__I_3II return 0; } - - -// -// native support for java finalize() -// static void Mat::n_delete( __int64 self ) -// - -JNIEXPORT void JNICALL Java_org_opencv_core_Mat_n_1delete - (JNIEnv*, jclass, jlong self); - -JNIEXPORT void JNICALL Java_org_opencv_core_Mat_n_1delete - (JNIEnv*, jclass, jlong self) -{ - delete (Mat*) self; -} - } // extern "C" namespace { diff --git a/modules/java/generator/src/java9/CleanableMat.java b/modules/java/generator/src/java9/CleanableMat.java new file mode 100644 index 0000000000..29677ffa3c --- /dev/null +++ b/modules/java/generator/src/java9/CleanableMat.java @@ -0,0 +1,23 @@ +package org.opencv.core; + +import java.lang.ref.Cleaner; + +public abstract class CleanableMat { + // A native memory cleaner for the OpenCV library + public static Cleaner cleaner = Cleaner.create(); + + protected CleanableMat(long obj) { + if (obj == 0) + throw new UnsupportedOperationException("Native object address is NULL"); + + nativeObj = obj; + + // The n_delete action must not refer to the object being registered. So, do not use nativeObj directly. + long nativeObjCopy = nativeObj; + cleaner.register(this, () -> n_delete(nativeObjCopy)); + } + + private static native void n_delete(long nativeObj); + + public final long nativeObj; +} diff --git a/modules/java/generator/src/java_classic/CleanableMat.java b/modules/java/generator/src/java_classic/CleanableMat.java new file mode 100644 index 0000000000..1909483e7c --- /dev/null +++ b/modules/java/generator/src/java_classic/CleanableMat.java @@ -0,0 +1,21 @@ +package org.opencv.core; + +public abstract class CleanableMat { + + protected CleanableMat(long obj) { + if (obj == 0) + throw new UnsupportedOperationException("Native object address is NULL"); + + nativeObj = obj; + } + + @Override + protected void finalize() throws Throwable { + n_delete(nativeObj); + super.finalize(); + } + + private static native void n_delete(long nativeObj); + + public final long nativeObj; +} diff --git a/modules/java/generator/templates/java_class.prolog b/modules/java/generator/templates/java_class.prolog index d9134fe9c0..a3f7e04344 100644 --- a/modules/java/generator/templates/java_class.prolog +++ b/modules/java/generator/templates/java_class.prolog @@ -9,7 +9,10 @@ $docs$annotation public class $jname { protected final long nativeObj; - protected $jname(long addr) { nativeObj = addr; } + protected $jname(long addr) { + nativeObj = addr; + $jcleaner + } public long getNativeObjAddr() { return nativeObj; } diff --git a/modules/js/CMakeLists.txt b/modules/js/CMakeLists.txt index cc26d039fc..dfb7fb76c0 100644 --- a/modules/js/CMakeLists.txt +++ b/modules/js/CMakeLists.txt @@ -74,6 +74,18 @@ else() endif() endif() +# Embind requires C++17 or newer from Emscripten 4.0.20. +# See https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md#4020---111825 +if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "4.0.20") + if(NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD STREQUAL "98" + OR CMAKE_CXX_STANDARD STREQUAL "11" OR CMAKE_CXX_STANDARD STREQUAL "14") + + message(FATAL_ERROR "[OpenCV.js Build Error] " + "Emscripten ${EMSCRIPTEN_VERSION} requires C++17 or newer for Embind, " + "but CMAKE_CXX_STANDARD is set to '${CMAKE_CXX_STANDARD}'.\n" + "Please re-run emcmake with --cmake_option=\"-DCMAKE_CXX_STANDARD=17\"\n") + endif() +endif() ocv_add_module(js BINDINGS PRIVATE_REQUIRED opencv_js_bindings_generator) diff --git a/modules/js/generator/CMakeLists.txt b/modules/js/generator/CMakeLists.txt index 48e3e2f92e..9fec48f950 100644 --- a/modules/js/generator/CMakeLists.txt +++ b/modules/js/generator/CMakeLists.txt @@ -50,7 +50,7 @@ if(DEFINED ENV{OPENCV_JS_WHITELIST}) else() #generate white list from modules//misc/js/whitelist.json set(OPENCV_JS_WHITELIST_FILE "${CMAKE_CURRENT_BINARY_DIR}/whitelist.json") - foreach(m in ${OPENCV_JS_MODULES}) + foreach(m IN LISTS OPENCV_JS_MODULES) set(js_whitelist "${OPENCV_MODULE_${m}_LOCATION}/misc/js/gen_dict.json") if (EXISTS "${js_whitelist}") file(READ "${js_whitelist}" whitelist_content) diff --git a/modules/js/generator/embindgen.py b/modules/js/generator/embindgen.py index 430baf99ef..7c456fce3d 100644 --- a/modules/js/generator/embindgen.py +++ b/modules/js/generator/embindgen.py @@ -523,13 +523,20 @@ class JSWrapperGenerator(object): # Return type ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype - if ret_type.startswith('Ptr'): #smart pointer + # FIX: Ensure namespaced smart-pointer return types in factory methods, e.g.: + # Ptr → Ptr + if factory and class_info is not None and ret_type.startswith('Ptr<'): + inner = ret_type[len('Ptr<'):-1].strip() + if '::' not in inner and inner == class_info.name: + ret_type = 'Ptr<%s>' % class_info.cname + + if ret_type.startswith('Ptr'): # smart pointer ptr_type = ret_type.replace('Ptr<', '').replace('>', '') if ptr_type in type_dict: ret_type = type_dict[ptr_type] - for key in type_dict: - if key in ret_type: - ret_type = re.sub(r"\b" + key + r"\b", type_dict[key], ret_type) + for key in type_dict: + if key in ret_type: + ret_type = re.sub(r"\b" + key + r"\b", type_dict[key], ret_type) arg_types = [] unwrapped_arg_types = [] for arg in variant.args: @@ -708,6 +715,12 @@ class JSWrapperGenerator(object): ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype ret_type = ret_type.strip() + # Same namespace fix for factory methods: Ptr -> Ptr + if factory and class_info is not None and ret_type.startswith('Ptr<'): + inner = ret_type[len('Ptr<'):-1].strip() + if '::' not in inner and inner == class_info.name: + ret_type = 'Ptr<%s>' % class_info.cname + if ret_type.startswith('Ptr'): #smart pointer ptr_type = ret_type.replace('Ptr<', '').replace('>', '') if ptr_type in type_dict: diff --git a/modules/js/src/helpers.js b/modules/js/src/helpers.js index ab88d18eec..315f802eb1 100644 --- a/modules/js/src/helpers.js +++ b/modules/js/src/helpers.js @@ -410,3 +410,19 @@ if ( if (cv.UMat) cv.UMat.prototype[Symbol.dispose] = cv.UMat.prototype.delete; // Add more as OpenCV gains new manual-cleanup classes } + +// Override Emscripten's shallow clone() with OpenCV's deep copy mat_clone() +// This restores the expected behavior where clone() performs a deep copy. +// Background: Emscripten 3.1.71+ added ClassHandle.clone() which only does shallow copy. +// See: https://github.com/opencv/opencv/pull/26643 +// See: https://github.com/opencv/opencv/issues/27572 +var _opencv_onRuntimeInitialized_backup = Module['onRuntimeInitialized']; +Module['onRuntimeInitialized'] = function() { + if (_opencv_onRuntimeInitialized_backup) { + _opencv_onRuntimeInitialized_backup(); + } + if (typeof cv !== 'undefined' && cv.Mat && + typeof cv.Mat.prototype.mat_clone === 'function') { + cv.Mat.prototype.clone = cv.Mat.prototype.mat_clone; + } +}; \ No newline at end of file diff --git a/modules/js/test/test_imgproc.js b/modules/js/test/test_imgproc.js index 79e2eb9d42..8d4d3b45c6 100644 --- a/modules/js/test/test_imgproc.js +++ b/modules/js/test/test_imgproc.js @@ -552,7 +552,7 @@ QUnit.test('test_filter', function(assert) { cv.stackBlur(src, src, new cv.Size(3, 3)); // Verify result. - let expected = new Uint8Array([22,29,36,38,43,50]); + let expected = new Uint8Array([14,22,29,46,51,58]); assert.deepEqual(src.data, expected); src.delete(); diff --git a/modules/js/test/test_mat.js b/modules/js/test/test_mat.js index f0c5211568..a1d5c98b17 100644 --- a/modules/js/test/test_mat.js +++ b/modules/js/test/test_mat.js @@ -173,7 +173,7 @@ QUnit.test('test_mat_creation', function(assert) { // clone { let mat = cv.Mat.ones(5, 5, cv.CV_8UC1); - let mat2 = mat.mat_clone(); + let mat2 = mat.clone(); assert.equal(mat.channels, mat2.channels); assert.equal(mat.size().height, mat2.size().height); diff --git a/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp b/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp index c081989645..c6ae25b718 100644 --- a/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp +++ b/modules/objdetect/include/opencv2/objdetect/aruco_detector.hpp @@ -318,6 +318,33 @@ public: CV_WRAP void detectMarkers(InputArray image, OutputArrayOfArrays corners, OutputArray ids, OutputArrayOfArrays rejectedImgPoints = noArray()) const; + /** @brief Marker detection with confidence computation + * + * @param image input image + * @param corners vector of detected marker corners. For each marker, its four corners + * are provided, (e.g std::vector > ). For N detected markers, + * the dimensions of this array is Nx4. The order of the corners is clockwise. + * @param ids vector of identifiers of the detected markers. The identifier is of type int + * (e.g. std::vector). For N detected markers, the size of ids is also N. + * The identifiers have the same order than the markers in the imgPoints array. + * @param markersConfidence contains the normalized confidence [0;1] of the markers' detection, + * defined as 1 minus the normalized uncertainty (percentage of incorrect pixel detections), + * with 1 describing a pixel perfect detection. The confidence values are of type float + * (e.g. std::vector) + * @param rejectedImgPoints contains the imgPoints of those squares whose inner code has not a + * correct codification. Useful for debugging purposes. + * + * Performs marker detection in the input image. Only markers included in the first specified dictionary + * are searched. For each detected marker, it returns the 2D position of its corner in the image + * and its corresponding identifier. + * Note that this function does not perform pose estimation. + * @note The function does not correct lens distortion or takes it into account. It's recommended to undistort + * input image with corresponding camera model, if camera parameters are known + * @sa undistort, estimatePoseSingleMarkers, estimatePoseBoard + */ + CV_WRAP void detectMarkersWithConfidence(InputArray image, OutputArrayOfArrays corners, OutputArray ids, OutputArray markersConfidence, + OutputArrayOfArrays rejectedImgPoints = noArray()) const; + /** @brief Refine not detected markers based on the already detected and the board layout * * @param image input image diff --git a/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp b/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp index bc7b934b2a..6a90876bf9 100644 --- a/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp +++ b/modules/objdetect/include/opencv2/objdetect/aruco_dictionary.hpp @@ -71,7 +71,6 @@ class CV_EXPORTS_W_SIMPLE Dictionary { */ CV_WRAP int getDistanceToId(InputArray bits, int id, bool allRotations = true) const; - /** @brief Generate a canonical marker image */ CV_WRAP void generateImageMarker(int id, int sidePixels, OutputArray _img, int borderBits = 1) const; @@ -84,7 +83,7 @@ class CV_EXPORTS_W_SIMPLE Dictionary { /** @brief Transform list of bytes to matrix of bits */ - CV_WRAP static Mat getBitsFromByteList(const Mat &byteList, int markerSize); + CV_WRAP static Mat getBitsFromByteList(const Mat &byteList, int markerSize, int rotationId = 0); }; diff --git a/modules/objdetect/include/opencv2/objdetect/charuco_detector.hpp b/modules/objdetect/include/opencv2/objdetect/charuco_detector.hpp index e9681521d3..999874c12e 100644 --- a/modules/objdetect/include/opencv2/objdetect/charuco_detector.hpp +++ b/modules/objdetect/include/opencv2/objdetect/charuco_detector.hpp @@ -62,7 +62,7 @@ public: /** * @brief detect aruco markers and interpolate position of ChArUco board corners - * @param image input image necesary for corner refinement. Note that markers are not detected and + * @param image input image necessary for corner refinement. Note that markers are not detected and * should be sent in corners and ids parameters. * @param charucoCorners interpolated chessboard corners. * @param charucoIds interpolated chessboard corners identifiers. diff --git a/modules/objdetect/src/aruco/apriltag/apriltag_quad_thresh.cpp b/modules/objdetect/src/aruco/apriltag/apriltag_quad_thresh.cpp index 96b7fe517e..41e6852609 100644 --- a/modules/objdetect/src/aruco/apriltag/apriltag_quad_thresh.cpp +++ b/modules/objdetect/src/aruco/apriltag/apriltag_quad_thresh.cpp @@ -18,6 +18,7 @@ #include "../../precomp.hpp" #include "apriltag_quad_thresh.hpp" +#include "unionfind.hpp" //#define APRIL_DEBUG #ifdef APRIL_DEBUG @@ -93,7 +94,7 @@ void ptsort_(struct pt *pts, int sz) // a merge sort with temp storage. // Use stack storage if it's not too big. - cv::AutoBuffer _tmp_stack(sz); + AutoBuffer _tmp_stack(sz); memcpy(_tmp_stack.data(), pts, sizeof(struct pt) * sz); int asz = sz/2; @@ -571,31 +572,6 @@ int quad_segment_agg(int sz, struct line_fit_pt *lfps, int indices[4]){ return 1; } -#define DO_UNIONFIND(dx, dy) if (im.data[y*s + dy*s + x + dx] == v) unionfind_connect(uf, y*w + x, y*w + dy*w + x + dx); -static void do_unionfind_line(unionfind_t *uf, Mat &im, int w, int s, int y){ - CV_Assert(y+1 < im.rows); - CV_Assert(!im.empty()); - - for (int x = 1; x < w - 1; x++) { - uint8_t v = im.data[y*s + x]; - - if (v == 127) - continue; - - // (dx,dy) pairs for 8 connectivity: - // (REFERENCE) (1, 0) - // (-1, 1) (0, 1) (1, 1) - // - DO_UNIONFIND(1, 0); - DO_UNIONFIND(0, 1); - if (v == 255) { - DO_UNIONFIND(-1, 1); - DO_UNIONFIND(1, 1); - } - } -} -#undef DO_UNIONFIND - /** * return 1 if the quad looks okay, 0 if it should be discarded * quad @@ -1309,11 +1285,11 @@ zarray_t *apriltag_quad_thresh(const DetectorParameters ¶meters, const Mat & //////////////////////////////////////////////////////// // step 2. find connected components. - unionfind_t *uf = unionfind_create(w * h); + UnionFind uf(w * h); // TODO PARALLELIZE for (int y = 0; y < h - 1; y++) { - do_unionfind_line(uf, thold, w, ts, y); + uf.do_line(thold, w, ts, y); } // XXX sizing?? @@ -1329,7 +1305,7 @@ zarray_t *apriltag_quad_thresh(const DetectorParameters ¶meters, const Mat & continue; // XXX don't query this until we know we need it? - uint64_t rep0 = unionfind_get_representative(uf, y*w + x); + uint64_t rep0 = uf.get_representative(y*w + x); // whenever we find two adjacent pixels such that one is // white and the other black, we add the point half-way @@ -1356,7 +1332,7 @@ zarray_t *apriltag_quad_thresh(const DetectorParameters ¶meters, const Mat & uint8_t v1 = thold.data[y*ts + dy*ts + x + dx]; \ \ if (v0 + v1 == 255) { \ - uint64_t rep1 = unionfind_get_representative(uf, y*w + dy*w + x + dx); \ + uint64_t rep1 = uf.get_representative(y*w + dy*w + x + dx); \ uint64_t clusterid; \ if (rep0 < rep1) \ clusterid = (rep1 << 32) + rep0; \ @@ -1405,9 +1381,9 @@ uint32_t *colors = (uint32_t*) calloc(w*h, sizeof(*colors)); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { - uint32_t v = unionfind_get_representative(uf, y*w+x); + uint32_t v = uf.get_representative(y*w+x); - if (unionfind_get_set_size(uf, v) < parameters->aprilTagMinClusterPixels) + if (uf.get_set_size(v) < parameters->aprilTagMinClusterPixels) continue; uint32_t color = colors[v]; @@ -1533,8 +1509,6 @@ for (int i = 0; i < _zarray_size(quads); i++) { imwrite("2.5 debug_lines.pnm", out); #endif - unionfind_destroy(uf); - for (int i = 0; i < _zarray_size(clusters); i++) { zarray_t *cluster; _zarray_get(clusters, i, &cluster); diff --git a/modules/objdetect/src/aruco/apriltag/apriltag_quad_thresh.hpp b/modules/objdetect/src/aruco/apriltag/apriltag_quad_thresh.hpp index bc1334a12d..aede1bbe0e 100644 --- a/modules/objdetect/src/aruco/apriltag/apriltag_quad_thresh.hpp +++ b/modules/objdetect/src/aruco/apriltag/apriltag_quad_thresh.hpp @@ -19,7 +19,6 @@ #ifndef _OPENCV_APRIL_QUAD_THRESH_HPP_ #define _OPENCV_APRIL_QUAD_THRESH_HPP_ -#include "unionfind.hpp" #include "zmaxheap.hpp" #include "zarray.hpp" diff --git a/modules/objdetect/src/aruco/apriltag/unionfind.hpp b/modules/objdetect/src/aruco/apriltag/unionfind.hpp index 6ecc58a407..f7a2d6a55c 100644 --- a/modules/objdetect/src/aruco/apriltag/unionfind.hpp +++ b/modules/objdetect/src/aruco/apriltag/unionfind.hpp @@ -17,115 +17,115 @@ namespace cv { namespace aruco { -typedef struct unionfind unionfind_t; -struct unionfind{ +struct UnionFind { + UnionFind(uint32_t _maxid) + { + maxid = _maxid; + data.resize(maxid+1); + for (unsigned int i = 0; i <= maxid; i++) { + data[i].size = 1; + data[i].parent = i; + } + }; + + inline uint32_t get_representative(uint32_t id) { + uint32_t root = id; + + // chase down the root + while (data[root].parent != root) { + root = data[root].parent; + } + + // go back and collapse the tree. + // + // XXX: on some of our workloads that have very shallow trees + // (e.g. image segmentation), we are actually faster not doing + // this... + while (data[id].parent != root) { + uint32_t tmp = data[id].parent; + data[id].parent = root; + id = tmp; + } + + return root; + } + + inline uint32_t get_set_size(uint32_t id) { + uint32_t repid = get_representative(id); + return data[repid].size; + } + + inline uint32_t connect(uint32_t aid, uint32_t bid) { + uint32_t aroot = get_representative(aid); + uint32_t broot = get_representative(bid); + + if (aroot == broot) + return aroot; + + // we don't perform "union by rank", but we perform a similar + // operation (but probably without the same asymptotic guarantee): + // We join trees based on the number of *elements* (as opposed to + // rank) contained within each tree. I.e., we use size as a proxy + // for rank. In my testing, it's often *faster* to use size than + // rank, perhaps because the rank of the tree isn't that critical + // if there are very few nodes in it. + uint32_t asize = data[aroot].size; + uint32_t bsize = data[broot].size; + + // optimization idea: We could shortcut some or all of the tree + // that is grafted onto the other tree. Pro: those nodes were just + // read and so are probably in cache. Con: it might end up being + // wasted effort -- the tree might be grafted onto another tree in + // a moment! + if (asize > bsize) { + data[broot].parent = aroot; + data[aroot].size += bsize; + return aroot; + } else { + data[aroot].parent = broot; + data[broot].size += asize; + return broot; + } + } + + #define DO_UNIONFIND(dx, dy) if (im.data[y*s + dy*s + x + dx] == v) connect(y*w + x, y*w + dy*w + x + dx); + void do_line(Mat &im, int w, int s, int y) { + CV_Assert(y+1 < im.rows); + CV_Assert(!im.empty()); + + for (int x = 1; x < w - 1; x++) { + uint8_t v = im.data[y*s + x]; + + if (v == 127) + continue; + + // (dx,dy) pairs for 8 connectivity: + // (REFERENCE) (1, 0) + // (-1, 1) (0, 1) (1, 1) + // + DO_UNIONFIND(1, 0); + DO_UNIONFIND(0, 1); + if (v == 255) { + DO_UNIONFIND(-1, 1); + DO_UNIONFIND(1, 1); + } + } + } + #undef DO_UNIONFIND + + struct ufrec { + // the parent of this node. If a node's parent is its own index, + // then it is a root. + uint32_t parent; + + // for the root of a connected component, the number of components + // connected to it. For intermediate values, it's not meaningful. + uint32_t size; + }; + uint32_t maxid; - struct ufrec *data; + std::vector data; }; -struct ufrec{ - // the parent of this node. If a node's parent is its own index, - // then it is a root. - uint32_t parent; - - // for the root of a connected component, the number of components - // connected to it. For intermediate values, it's not meaningful. - uint32_t size; -}; - -static inline unionfind_t *unionfind_create(uint32_t maxid){ - unionfind_t *uf = (unionfind_t*) calloc(1, sizeof(unionfind_t)); - uf->maxid = maxid; - uf->data = (struct ufrec*) malloc((maxid+1) * sizeof(struct ufrec)); - for (unsigned int i = 0; i <= maxid; i++) { - uf->data[i].size = 1; - uf->data[i].parent = i; - } - return uf; -} - -static inline void unionfind_destroy(unionfind_t *uf){ - free(uf->data); - free(uf); -} - -/* -static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id) -{ - // base case: a node is its own parent - if (uf->data[id].parent == id) - return id; - - // otherwise, recurse - uint32_t root = unionfind_get_representative(uf, uf->data[id].parent); - - // short circuit the path. [XXX This write prevents tail recursion] - uf->data[id].parent = root; - - return root; -} - */ - -// this one seems to be every-so-slightly faster than the recursive -// version above. -static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id){ - uint32_t root = id; - - // chase down the root - while (uf->data[root].parent != root) { - root = uf->data[root].parent; - } - - // go back and collapse the tree. - // - // XXX: on some of our workloads that have very shallow trees - // (e.g. image segmentation), we are actually faster not doing - // this... - while (uf->data[id].parent != root) { - uint32_t tmp = uf->data[id].parent; - uf->data[id].parent = root; - id = tmp; - } - - return root; -} - -static inline uint32_t unionfind_get_set_size(unionfind_t *uf, uint32_t id){ - uint32_t repid = unionfind_get_representative(uf, id); - return uf->data[repid].size; -} - -static inline uint32_t unionfind_connect(unionfind_t *uf, uint32_t aid, uint32_t bid){ - uint32_t aroot = unionfind_get_representative(uf, aid); - uint32_t broot = unionfind_get_representative(uf, bid); - - if (aroot == broot) - return aroot; - - // we don't perform "union by rank", but we perform a similar - // operation (but probably without the same asymptotic guarantee): - // We join trees based on the number of *elements* (as opposed to - // rank) contained within each tree. I.e., we use size as a proxy - // for rank. In my testing, it's often *faster* to use size than - // rank, perhaps because the rank of the tree isn't that critical - // if there are very few nodes in it. - uint32_t asize = uf->data[aroot].size; - uint32_t bsize = uf->data[broot].size; - - // optimization idea: We could shortcut some or all of the tree - // that is grafted onto the other tree. Pro: those nodes were just - // read and so are probably in cache. Con: it might end up being - // wasted effort -- the tree might be grafted onto another tree in - // a moment! - if (asize > bsize) { - uf->data[broot].parent = aroot; - uf->data[aroot].size += bsize; - return aroot; - } else { - uf->data[aroot].parent = broot; - uf->data[broot].size += asize; - return broot; - } -} }} #endif diff --git a/modules/objdetect/src/aruco/apriltag/zmaxheap.cpp b/modules/objdetect/src/aruco/apriltag/zmaxheap.cpp index 5b0ac85dcb..5e7ca49063 100644 --- a/modules/objdetect/src/aruco/apriltag/zmaxheap.cpp +++ b/modules/objdetect/src/aruco/apriltag/zmaxheap.cpp @@ -84,7 +84,6 @@ void zmaxheap_destroy(zmaxheap_t *heap) { free(heap->values); free(heap->data); - memset(heap, 0, sizeof(zmaxheap_t)); free(heap); } diff --git a/modules/objdetect/src/aruco/aruco_detector.cpp b/modules/objdetect/src/aruco/aruco_detector.cpp index 97dba71970..81b1453941 100644 --- a/modules/objdetect/src/aruco/aruco_detector.cpp +++ b/modules/objdetect/src/aruco/aruco_detector.cpp @@ -9,6 +9,7 @@ #include "opencv2/objdetect/aruco_board.hpp" #include "apriltag/apriltag_quad_thresh.hpp" #include "aruco_utils.hpp" +#include #include #include @@ -124,7 +125,7 @@ static void _threshold(InputArray _in, OutputArray _out, int winSize, double con /** - * @brief Given a tresholded image, find the contours, calculate their polygonal approximation + * @brief Given a thresholded image, find the contours, calculate their polygonal approximation * and take those that accomplish some conditions */ static void _findMarkerContours(const Mat &in, vector > &candidates, @@ -313,10 +314,11 @@ static void _detectInitialCandidates(const Mat &grey, vector > & * the border bits */ static Mat _extractBits(InputArray _image, const vector& corners, int markerSize, - int markerBorderBits, int cellSize, double cellMarginRate, double minStdDevOtsu) { + int markerBorderBits, int cellSize, double cellMarginRate, double minStdDevOtsu, + OutputArray _cellPixelRatio = noArray()) { CV_Assert(_image.getMat().channels() == 1); CV_Assert(corners.size() == 4ull); - CV_Assert(markerBorderBits > 0 && cellSize > 0 && cellMarginRate >= 0 && cellMarginRate <= 1); + CV_Assert(markerBorderBits > 0 && cellSize > 0 && cellMarginRate >= 0 && cellMarginRate <= 0.5); CV_Assert(minStdDevOtsu >= 0); // number of bits in the marker @@ -353,9 +355,16 @@ static Mat _extractBits(InputArray _image, const vector& corners, int m bits.setTo(1); else bits.setTo(0); + if(_cellPixelRatio.needed()) bits.convertTo(_cellPixelRatio, CV_32F); return bits; } + Mat cellPixelRatio; + if (_cellPixelRatio.needed()) { + _cellPixelRatio.create(markerSizeWithBorders, markerSizeWithBorders, CV_32FC1); + cellPixelRatio = _cellPixelRatio.getMatRef(); + } + // now extract code, first threshold using Otsu threshold(resultImg, resultImg, 125, 255, THRESH_BINARY | THRESH_OTSU); @@ -369,6 +378,9 @@ static Mat _extractBits(InputArray _image, const vector& corners, int m // count white pixels on each cell to assign its value size_t nZ = (size_t) countNonZero(square); if(nZ > square.total() / 2) bits.at(y, x) = 1; + + // define the cell pixel ratio as the ratio of the white pixels. For inverted markers, the ratio will be inverted. + if(_cellPixelRatio.needed()) cellPixelRatio.at(y, x) = (nZ / (float)square.total()); } } @@ -403,6 +415,52 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) { } +/** @brief Given a matrix containing the percentage of white pixels in each marker cell, returns the normalized marker confidence [0;1]. + * The confidence is defined as 1 - normalized uncertainty, where 1 describes a pixel perfect detection. + * The rotation is set to 0,1,2,3 for [0, 90, 180, 270] deg CCW rotations. + */ + +static float _getMarkerConfidence(const Mat& groundTruthbits, const Mat &cellPixelRatio, const int markerSize, const int borderSize) { + + CV_Assert(markerSize == groundTruthbits.cols && markerSize == groundTruthbits.rows); + + const int sizeWithBorders = markerSize + 2 * borderSize; + CV_Assert(markerSize > 0 && cellPixelRatio.cols == sizeWithBorders && cellPixelRatio.rows == sizeWithBorders); + + // Get border uncertainty. cellPixelRatio has the opposite color as the borders --> it is the uncertainty. + float tempBorderUnc = 0.f; + for(int y = 0; y < sizeWithBorders; y++) { + for(int k = 0; k < borderSize; k++) { + // Left and right vertical sides + tempBorderUnc += cellPixelRatio.ptr(y)[k]; + tempBorderUnc += cellPixelRatio.ptr(y)[sizeWithBorders - 1 - k]; + } + } + for(int x = borderSize; x < sizeWithBorders - borderSize; x++) { + for(int k = 0; k < borderSize; k++) { + // Top and bottom horizontal sides + tempBorderUnc += cellPixelRatio.ptr(k)[x]; + tempBorderUnc += cellPixelRatio.ptr(sizeWithBorders - 1 - k)[x]; + } + } + + // Get the inner marker uncertainty. For a white or black cell, the uncertainty is the ratio of black or white pixels respectively. + float tempInnerUnc = 0.f; + for(int y = borderSize; y < markerSize + borderSize; y++) { + for(int x = borderSize; x < markerSize + borderSize; x++) { + tempInnerUnc += std::abs(groundTruthbits.ptr(y - borderSize)[x - borderSize] - cellPixelRatio.ptr(y)[x]); + } + } + + // Compute the overall normalized marker uncertainty and convert it to confidence + const float area = static_cast(sizeWithBorders) * sizeWithBorders; + const float normalizedMarkerUnc = (tempInnerUnc + tempBorderUnc) / area; + const float normalizedMarkerConfidence = 1.f - normalizedMarkerUnc; + + return std::max(0.f, std::min(1.f, normalizedMarkerConfidence)); +} + + /** * @brief Tries to identify one candidate given the dictionary * @return candidate typ. zero if the candidate is not valid, @@ -412,6 +470,7 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) { static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _image, const vector& _corners, int& idx, const DetectorParameters& params, int& rotation, + float &markerConfidence, bool confidenceNeeded, const float scale = 1.f) { CV_DbgAssert(params.markerBorderBits > 0); uint8_t typ=1; @@ -423,10 +482,12 @@ static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _i scaled_corners[i].y = _corners[i].y * scale; } + Mat cellPixelRatio; Mat candidateBits = _extractBits(_image, scaled_corners, dictionary.markerSize, params.markerBorderBits, params.perspectiveRemovePixelPerCell, - params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev); + params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev, + cellPixelRatio); // analyze border bits int maximumErrorsInBorder = @@ -441,6 +502,7 @@ static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _i int invBError = _getBorderErrors(invertedImg, dictionary.markerSize, params.markerBorderBits); // white marker if(invBError 0); @@ -717,6 +787,7 @@ struct ArucoDetector::ArucoDetectorImpl { vector > candidates; vector > contours; vector ids; + vector markersConfidence; /// STEP 2.a Detect marker candidates :: using AprilTag if(detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_APRILTAG){ @@ -738,7 +809,7 @@ struct ArucoDetector::ArucoDetectorImpl { /// STEP 2: Check candidate codification (identify markers) identifyCandidates(grey, grey_pyramid, selectedCandidates, candidates, contours, - ids, dictionary, rejectedImgPoints); + ids, dictionary, rejectedImgPoints, markersConfidence, _markersConfidence.needed()); /// STEP 3: Corner refinement :: use corner subpix if (detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_SUBPIX) { @@ -766,7 +837,7 @@ struct ArucoDetector::ArucoDetectorImpl { // temporary variable to store the current candidates vector> currentCandidates; identifyCandidates(grey, grey_pyramid, candidatesPerDictionarySize.at(currentDictionary.markerSize), currentCandidates, contours, - ids, currentDictionary, rejectedImgPoints); + ids, currentDictionary, rejectedImgPoints, markersConfidence, _markersConfidence.needed()); if (_dictIndices.needed()) { dictIndices.insert(dictIndices.end(), currentCandidates.size(), dictIndex); } @@ -849,6 +920,9 @@ struct ArucoDetector::ArucoDetectorImpl { if (_dictIndices.needed()) { Mat(dictIndices).copyTo(_dictIndices); } + if (_markersConfidence.needed()) { + Mat(markersConfidence).copyTo(_markersConfidence); + } } /** @@ -982,9 +1056,10 @@ struct ArucoDetector::ArucoDetectorImpl { */ void identifyCandidates(const Mat& grey, const vector& image_pyr, vector& selectedContours, vector >& accepted, vector >& contours, - vector& ids, const Dictionary& currentDictionary, vector>& rejected) const { + vector& ids, const Dictionary& currentDictionary, vector>& rejected, vector& markersConfidence, bool confidenceNeeded) const { size_t ncandidates = selectedContours.size(); + vector markersConfidenceTmp(ncandidates, 0.f); vector idsTmp(ncandidates, -1); vector rotated(ncandidates, 0); vector validCandidates(ncandidates, 0); @@ -1018,11 +1093,11 @@ struct ArucoDetector::ArucoDetectorImpl { } const float scale = detectorParams.useAruco3Detection ? img.cols / static_cast(grey.cols) : 1.f; - validCandidates[v] = _identifyOneCandidate(currentDictionary, img, selectedContours[v].corners, idsTmp[v], detectorParams, rotated[v], scale); + validCandidates[v] = _identifyOneCandidate(currentDictionary, img, selectedContours[v].corners, idsTmp[v], detectorParams, rotated[v], markersConfidenceTmp[v], confidenceNeeded, scale); if (validCandidates[v] == 0 && checkCloseContours) { for (const MarkerCandidate& closeMarkerCandidate: selectedContours[v].closeContours) { - validCandidates[v] = _identifyOneCandidate(currentDictionary, img, closeMarkerCandidate.corners, idsTmp[v], detectorParams, rotated[v], scale); + validCandidates[v] = _identifyOneCandidate(currentDictionary, img, closeMarkerCandidate.corners, idsTmp[v], detectorParams, rotated[v], markersConfidenceTmp[v], confidenceNeeded, scale); if (validCandidates[v] > 0) { selectedContours[v].corners = closeMarkerCandidate.corners; selectedContours[v].contour = closeMarkerCandidate.contour; @@ -1052,17 +1127,24 @@ struct ArucoDetector::ArucoDetectorImpl { for (size_t i = 0ull; i < selectedContours.size(); i++) { if (validCandidates[i] > 0) { - // shift corner positions to the correct rotation - correctCornerPosition(selectedContours[i].corners, rotated[i]); + // shift corner positions to the correct rotation + correctCornerPosition(selectedContours[i].corners, rotated[i]); - accepted.push_back(selectedContours[i].corners); - contours.push_back(selectedContours[i].contour); - ids.push_back(idsTmp[i]); - } - else { + accepted.push_back(selectedContours[i].corners); + contours.push_back(selectedContours[i].contour); + ids.push_back(idsTmp[i]); + } else { rejected.push_back(selectedContours[i].corners); } } + + if(confidenceNeeded) { + for (size_t i = 0ull; i < selectedContours.size(); i++) { + if (validCandidates[i] > 0) { + markersConfidence.push_back(markersConfidenceTmp[i]); + } + } + } } void performCornerSubpixRefinement(const Mat& grey, const vector& grey_pyramid, int closest_pyr_image_idx, const vector>& candidates, const Dictionary& dictionary) const { @@ -1103,14 +1185,19 @@ ArucoDetector::ArucoDetector(const vector &_dictionaries, arucoDetectorImpl = makePtr(_dictionaries, _detectorParams, _refineParams); } +void ArucoDetector::detectMarkersWithConfidence(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids, OutputArray _markersConfidence, + OutputArrayOfArrays _rejectedImgPoints) const { + arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, noArray(), _markersConfidence, DictionaryMode::Single); +} + void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids, OutputArrayOfArrays _rejectedImgPoints) const { - arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, noArray(), DictionaryMode::Single); + arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, noArray(), noArray(), DictionaryMode::Single); } void ArucoDetector::detectMarkersMultiDict(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids, OutputArrayOfArrays _rejectedImgPoints, OutputArray _dictIndices) const { - arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, _dictIndices, DictionaryMode::Multi); + arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, _dictIndices, noArray(), DictionaryMode::Multi); } /** diff --git a/modules/objdetect/src/aruco/aruco_dictionary.cpp b/modules/objdetect/src/aruco/aruco_dictionary.cpp index 3d5f9b1bfd..350ccc63eb 100644 --- a/modules/objdetect/src/aruco/aruco_dictionary.cpp +++ b/modules/objdetect/src/aruco/aruco_dictionary.cpp @@ -194,17 +194,24 @@ Mat Dictionary::getByteListFromBits(const Mat &bits) { } -Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize) { +Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize, int rotationId) { CV_Assert(byteList.total() > 0 && byteList.total() >= (unsigned int)markerSize * markerSize / 8 && byteList.total() <= (unsigned int)markerSize * markerSize / 8 + 1); + + CV_Assert(rotationId >=0 && rotationId < 4); + Mat bits(markerSize, markerSize, CV_8UC1, Scalar::all(0)); unsigned char base2List[] = { 128, 64, 32, 16, 8, 4, 2, 1 }; + + // Use a base offset for the selected rotation + int nbytes = (bits.cols * bits.rows + 8 - 1) / 8; // integer ceil + int base = rotationId * nbytes; int currentByteIdx = 0; - // we only need the bytes in normal rotation - unsigned char currentByte = byteList.ptr()[0]; + unsigned char currentByte = byteList.ptr()[base + currentByteIdx]; int currentBit = 0; + for(int row = 0; row < bits.rows; row++) { for(int col = 0; col < bits.cols; col++) { if(currentByte >= base2List[currentBit]) { @@ -214,7 +221,7 @@ Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize) { currentBit++; if(currentBit == 8) { currentByteIdx++; - currentByte = byteList.ptr()[currentByteIdx]; + currentByte = byteList.ptr()[base + currentByteIdx]; // if not enough bits for one more byte, we are in the end // update bit position accordingly if(8 * (currentByteIdx + 1) > (int)bits.total()) diff --git a/modules/objdetect/src/aruco/charuco_detector.cpp b/modules/objdetect/src/aruco/charuco_detector.cpp index 589d837d12..be771c4272 100644 --- a/modules/objdetect/src/aruco/charuco_detector.cpp +++ b/modules/objdetect/src/aruco/charuco_detector.cpp @@ -316,8 +316,8 @@ struct CharucoDetector::CharucoDetectorImpl { CV_Assert((markerCorners.empty() && markerIds.empty() && !image.empty()) || (markerCorners.total() == markerIds.total())); vector> tmpMarkerCorners; vector tmpMarkerIds; - InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners; - InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds; + InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : _InputOutputArray(tmpMarkerCorners); + InputOutputArray _markerIds = markerIds.needed() ? markerIds : _InputOutputArray(tmpMarkerIds); if (markerCorners.empty() && markerIds.empty()) { vector > rejectedMarkers; @@ -341,8 +341,8 @@ struct CharucoDetector::CharucoDetectorImpl { InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds) { vector> tmpMarkerCorners; vector tmpMarkerIds; - InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners; - InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds; + InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : _InputOutputArray(tmpMarkerCorners); + InputOutputArray _markerIds = markerIds.needed() ? markerIds : _InputOutputArray(tmpMarkerIds); detectBoard(image, charucoCorners, charucoIds, _markerCorners, _markerIds); if (charucoParameters.checkMarkers && checkBoard(_markerCorners, _markerIds, charucoCorners, charucoIds) == false) { CV_LOG_DEBUG(NULL, "ChArUco board is built incorrectly"); @@ -402,8 +402,8 @@ void CharucoDetector::detectDiamonds(InputArray image, OutputArrayOfArrays _diam vector> tmpMarkerCorners; vector tmpMarkerIds; - InputOutputArrayOfArrays _markerCorners = inMarkerCorners.needed() ? inMarkerCorners : tmpMarkerCorners; - InputOutputArray _markerIds = inMarkerIds.needed() ? inMarkerIds : tmpMarkerIds; + InputOutputArrayOfArrays _markerCorners = inMarkerCorners.needed() ? inMarkerCorners : _InputOutputArray(tmpMarkerCorners); + InputOutputArray _markerIds = inMarkerIds.needed() ? inMarkerIds : _InputOutputArray(tmpMarkerIds); if (_markerCorners.empty() && _markerIds.empty()) { charucoDetectorImpl->arucoDetector.detectMarkers(image, _markerCorners, _markerIds); } diff --git a/modules/objdetect/src/precomp.hpp b/modules/objdetect/src/precomp.hpp index 63ca440076..82ecec2751 100644 --- a/modules/objdetect/src/precomp.hpp +++ b/modules/objdetect/src/precomp.hpp @@ -43,6 +43,7 @@ #ifndef __OPENCV_PRECOMP_H__ #define __OPENCV_PRECOMP_H__ +#include "opencv2/core.hpp" #include "opencv2/objdetect.hpp" #include "opencv2/objdetect/barcode.hpp" #include "opencv2/imgproc.hpp" diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index aa77d557c9..f0e5952257 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -80,8 +80,10 @@ static Point2f intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2) // / | // a/ | c -static inline double getCosVectors(Point2f a, Point2f b, Point2f c) +static inline double getCosVectors(Point a, Point b, Point c) { + CV_DbgCheckNE(a, b, "Angle between vector and point is undetermined"); + CV_DbgCheckNE(b, c, "Angle between vector and point is undetermined"); return ((a - b).x * (c - b).x + (a - b).y * (c - b).y) / (norm(a - b) * norm(c - b)); } @@ -407,9 +409,9 @@ void QRDetect::fixationPoints(vector &local_point) list_area_pnt.push_back(current_point); vector list_line_iter; - list_line_iter.push_back(LineIterator(bin_barcode, current_point, left_point)); - list_line_iter.push_back(LineIterator(bin_barcode, current_point, central_point)); - list_line_iter.push_back(LineIterator(bin_barcode, current_point, right_point)); + list_line_iter.emplace_back(bin_barcode, current_point, left_point); + list_line_iter.emplace_back(bin_barcode, current_point, central_point); + list_line_iter.emplace_back(bin_barcode, current_point, right_point); for (size_t k = 0; k < list_line_iter.size(); k++) { @@ -752,7 +754,7 @@ vector QRDetect::getQuadrilateral(vector angle_list) { int x = cvRound(angle_list[i].x); int y = cvRound(angle_list[i].y); - locations.push_back(Point(x, y)); + locations.emplace_back(x,y); } vector integer_hull; @@ -822,7 +824,11 @@ vector QRDetect::getQuadrilateral(vector angle_list) Point intrsc_line_hull = intersectionLines(hull[index_hull], hull[next_index_hull], angle_list[1], angle_list[2]); - double temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt); + double temp_norm = min_norm; + if (intrsc_line_hull != angle_closest_pnt) + { + temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt); + } if (min_norm > temp_norm && norm(hull[index_hull] - hull[next_index_hull]) > norm(angle_list[1] - angle_list[2]) * 0.1) @@ -860,7 +866,11 @@ vector QRDetect::getQuadrilateral(vector angle_list) Point intrsc_line_hull = intersectionLines(hull[index_hull], hull[next_index_hull], angle_list[0], angle_list[1]); - double temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt); + double temp_norm = min_norm; + if (intrsc_line_hull != angle_closest_pnt) + { + temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt); + } if (min_norm > temp_norm && norm(hull[index_hull] - hull[next_index_hull]) > norm(angle_list[0] - angle_list[1]) * 0.05) @@ -2204,13 +2214,13 @@ bool QRDecode::straightenQRCodeInParts() vector perspective_points; - perspective_points.push_back(Point2f(0.0, start_cut)); - perspective_points.push_back(Point2f(perspective_curved_size, start_cut)); + perspective_points.emplace_back(0.0f, start_cut); + perspective_points.emplace_back(perspective_curved_size, start_cut); - perspective_points.push_back(Point2f(perspective_curved_size, start_cut + dist)); - perspective_points.push_back(Point2f(0.0, start_cut+dist)); + perspective_points.emplace_back(perspective_curved_size, start_cut + dist); + perspective_points.emplace_back(0.0f, start_cut+dist); - perspective_points.push_back(Point2f(perspective_curved_size * 0.5f, start_cut + dist * 0.5f)); + perspective_points.emplace_back(perspective_curved_size * 0.5f, start_cut + dist * 0.5f); if (i == 1) { @@ -2281,13 +2291,13 @@ bool QRDecode::straightenQRCodeInParts() } vector perspective_straight_points; - perspective_straight_points.push_back(Point2f(0.f, 0.f)); - perspective_straight_points.push_back(Point2f(perspective_curved_size, 0.f)); + perspective_straight_points.emplace_back(0.f, 0.f); + perspective_straight_points.emplace_back(perspective_curved_size, 0.f); - perspective_straight_points.push_back(Point2f(perspective_curved_size, perspective_curved_size)); - perspective_straight_points.push_back(Point2f(0.f, perspective_curved_size)); + perspective_straight_points.emplace_back(perspective_curved_size, perspective_curved_size); + perspective_straight_points.emplace_back(0.f, perspective_curved_size); - perspective_straight_points.push_back(Point2f(perspective_curved_size * 0.5f, perspective_curved_size * 0.5f)); + perspective_straight_points.emplace_back(perspective_curved_size * 0.5f, perspective_curved_size * 0.5f); Mat H = findHomography(original_curved_points, perspective_straight_points); if (H.empty()) @@ -3272,9 +3282,9 @@ void QRDetectMulti::fixationPoints(vector &local_point) list_area_pnt.push_back(current_point); vector list_line_iter; - list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, left_point)); - list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, central_point)); - list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, right_point)); + list_line_iter.emplace_back(bin_barcode_temp, current_point, left_point); + list_line_iter.emplace_back(bin_barcode_temp, current_point, central_point); + list_line_iter.emplace_back(bin_barcode_temp, current_point, right_point); for (size_t k = 0; k < list_line_iter.size(); k++) { diff --git a/modules/objdetect/test/test_arucodetection.cpp b/modules/objdetect/test/test_arucodetection.cpp index ba90e490bb..4ee91967f2 100644 --- a/modules/objdetect/test/test_arucodetection.cpp +++ b/modules/objdetect/test/test_arucodetection.cpp @@ -321,6 +321,358 @@ void CV_ArucoDetectionPerspective::run(int) { } } +// Helper struct and functions for CV_ArucoDetectionConfidence + +// Inverts a square subregion inside selected cells of a marker to simulate a confidence drop +enum class MarkerRegionToTemper { + BORDER, // Only invert cells within the marker border bits + INNER, // Only invert cells in the inner part of the marker (excluding borders) + ALL // Invert any cells +}; + +// Define the characteristics of cell inversions +struct MarkerTemperingConfig { + float cellRatioToTemper; // [0,1] ratio of the cell to invert + int numCellsToTemper; // Number of cells to invert + MarkerRegionToTemper markerRegionToTemper; // Which cells to invert (BORDER, INNER, ALL) +}; + +// Test configs for CV_ArucoDetectionConfidence +struct ArucoConfidenceTestConfig { + MarkerTemperingConfig markerTemperingConfig; // Configuration of cells to invert (percentage, number and markerRegionToTemper) + float perspectiveRemoveIgnoredMarginPerCell; // Width of the margin of pixels on each cell not considered for the marker identification + int markerBorderBits; // Number of bits of the marker border + float distortionRatio; // Percentage of offset used for perspective distortion, bigger means more distorted +}; + +enum class markerRot +{ + NONE = 0, + ROT_90, + ROT_180, + ROT_270 +}; + +struct markerDetectionGT { + int id; // Marker identification + double confidence; // Pixel-based confidence defined as 1 - (inverted area / total area) + bool expectDetection; // True if we expect to detect the marker +}; + +struct MarkerCreationConfig { + int id; // Marker identification + int markerSidePixels; // Marker size (in pixels) + markerRot rotation; // Rotation of the marker in degrees (0, 90, 180, 270) +}; + +void rotateMarker(Mat &marker, const markerRot rotation) +{ + if(rotation == markerRot::NONE) + return; + + if (rotation == markerRot::ROT_90) { + cv::transpose(marker, marker); + cv::flip(marker, marker, 0); + } else if (rotation == markerRot::ROT_180) { + cv::flip(marker, marker, -1); + } else if (rotation == markerRot::ROT_270) { + cv::transpose(marker, marker); + cv::flip(marker, marker, 1); + } +} + +void distortMarker(Mat &marker, const float distortionRatio) +{ + + if (distortionRatio < FLT_EPSILON) + return; + + // apply a distortion (a perspective warp) to simulate a non-ideal capture + vector src = { {0, 0}, + {static_cast(marker.cols), 0}, + {static_cast(marker.cols), static_cast(marker.rows)}, + {0, static_cast(marker.rows)} }; + float offset = marker.cols * distortionRatio; // distortionRatio % offset for distortion + vector dst = { {offset, offset}, + {marker.cols - offset, 0}, + {marker.cols - offset, marker.rows - offset}, + {0, marker.rows - offset} }; + Mat M = getPerspectiveTransform(src, dst); + warpPerspective(marker, marker, M, marker.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(255)); +} + +/** + * @brief Inverts a square subregion inside selected cells of a marker image to simulate confidence degradation. + * + * The function computes the marker grid parameters and then applies a bitwise inversion + * on a square markerRegionToTemper inside the chosen cells. The number of cells to be inverted is determined by + * the parameter 'numCellsToTemper'. The candidate cells can be filtered to only include border cells, + * inner cells, or all cells according to the parameter 'markerRegionToTemper'. + * + * @param marker The marker image + * @param markerSidePixels The total size of the marker in pixels (inner and border). + * @param markerId The id of the marker + * @param params The Aruco detector configuration (provides border bits, margin ratios, etc.). + * @param dictionary The Aruco marker dictionary (used to determine marker grid size). + * @param cellTempConfig Cell tempering config as defined in MarkerTemperingConfig + * @return Cell tempering ground truth as defined in markerDetectionGT + */ +markerDetectionGT applyTemperingToMarkerCells(cv::Mat &marker, + const int markerSidePixels, + const int markerId, + const aruco::DetectorParameters ¶ms, + const aruco::Dictionary &dictionary, + const MarkerTemperingConfig &cellTempConfig) +{ + + // nothing to invert + if(cellTempConfig.numCellsToTemper <= 0 || cellTempConfig.cellRatioToTemper <= FLT_EPSILON) + return {markerId, 1.0, true}; + + // compute the overall grid dimensions. + const int markerSizeWithBorders = dictionary.markerSize + 2 * params.markerBorderBits; + const int cellSidePixelsSize = markerSidePixels / markerSizeWithBorders; + + // compute the margin within each cell used for identification. + const int cellMarginPixels = static_cast(params.perspectiveRemoveIgnoredMarginPerCell * cellSidePixelsSize); + const int innerCellSizePixels = cellSidePixelsSize - 2 * cellMarginPixels; + + // determine the size of the square that will be inverted in each cell. + // (cellSidePixelsInvert / innerCellSizePixels)^2 should equal cellRatioToTemper. + const int cellSidePixelsInvert = min(cellSidePixelsSize, static_cast(innerCellSizePixels * std::sqrt(cellTempConfig.cellRatioToTemper))); + const int inversionOffsetPixels = (cellSidePixelsSize - cellSidePixelsInvert) / 2; + + // nothing to invert + if(cellSidePixelsInvert <= 0) + return {markerId, 1.0, true}; + + int cellsTempered = 0; + int borderErrors = 0; + int innerCellsErrors = 0; + // iterate over each cell in the grid. + for (int row = 0; row < markerSizeWithBorders; row++) { + for (int col = 0; col < markerSizeWithBorders; col++) { + + // decide if this cell falls in the markerRegionToTemper to temper. + const bool isBorder = (row < params.markerBorderBits || + col < params.markerBorderBits || + row >= markerSizeWithBorders - params.markerBorderBits || + col >= markerSizeWithBorders - params.markerBorderBits); + + const bool inRegion = (cellTempConfig.markerRegionToTemper == MarkerRegionToTemper::ALL || + (isBorder && cellTempConfig.markerRegionToTemper == MarkerRegionToTemper::BORDER) || + (!isBorder && cellTempConfig.markerRegionToTemper == MarkerRegionToTemper::INNER)); + + // apply the inversion to simulate tempering. + if (inRegion && (cellsTempered < cellTempConfig.numCellsToTemper)) { + const int xStart = col * cellSidePixelsSize + inversionOffsetPixels; + const int yStart = row * cellSidePixelsSize + inversionOffsetPixels; + cv::Rect cellRect(xStart, yStart, cellSidePixelsInvert, cellSidePixelsInvert); + cv::Mat cellROI = marker(cellRect); + cv::bitwise_not(cellROI, cellROI); + ++cellsTempered; + + // cell too tempered, no detection expected + if(cellTempConfig.cellRatioToTemper > 0.5f) { + if(isBorder){ + ++borderErrors; + } else { + ++innerCellsErrors; + } + } + } + + if(cellsTempered >= cellTempConfig.numCellsToTemper) + break; + } + + if(cellsTempered >= cellTempConfig.numCellsToTemper) + break; + } + + // compute the ground-truth confidence + const double invertedArea = cellsTempered * cellSidePixelsInvert * cellSidePixelsInvert; + const double totalDetectionArea = markerSizeWithBorders * innerCellSizePixels * markerSizeWithBorders * innerCellSizePixels; + const double groundTruthConfidence = std::max(0.0, 1.0 - invertedArea / totalDetectionArea); + + // check if marker is expected to be detected + const int maximumErrorsInBorder = static_cast(dictionary.markerSize * dictionary.markerSize * params.maxErroneousBitsInBorderRate); + const int maxCorrectionRecalculed = static_cast(dictionary.maxCorrectionBits * params.errorCorrectionRate); + const bool expectDetection = static_cast(borderErrors <= maximumErrorsInBorder && innerCellsErrors <= maxCorrectionRecalculed); + + return {markerId, groundTruthConfidence, expectDetection}; +} + +/** + * @brief Create an image of a marker with inverted (tempered) regions to simulate detection confidence + * + * Applies an optional rotation and an optional perspective warp to simulate a distorted marker. + * Inverts a square subregion inside selected cells of a marker image to simulate a drop in confidence. + * Computes the ground-truth confidence as one minus the ratio of inverted area to the total marker area used for identification. + * + */ +markerDetectionGT generateTemperedMarkerImage(Mat &marker, const MarkerCreationConfig &markerConfig, const MarkerTemperingConfig &markerTemperingConfig, + const aruco::DetectorParameters ¶ms, const aruco::Dictionary &dictionary, const float distortionRatio = 0.f) +{ + // generate the synthetic marker image + aruco::generateImageMarker(dictionary, markerConfig.id, markerConfig.markerSidePixels, + marker, params.markerBorderBits); + + // rotate marker if necessary + rotateMarker(marker, markerConfig.rotation); + + // temper with cells to simulate detection confidence drops + markerDetectionGT groundTruth = applyTemperingToMarkerCells(marker, markerConfig.markerSidePixels, markerConfig.id, params, dictionary, markerTemperingConfig); + + // apply a distortion (a perspective warp) to simulate a non-ideal capture + distortMarker(marker, distortionRatio); + + return groundTruth; +} + + +/** + * @brief Copies a marker image into a larger image at the given top-left position. + */ +void placeMarker(Mat &img, const Mat &marker, const Point2f &topLeft) +{ + Rect roi(Point(static_cast(topLeft.x), static_cast(topLeft.y)), marker.size()); + marker.copyTo(img(roi)); +} + + +/** + * @brief Test the marker confidence computations + * + * Loops over a set of detector configurations (e.g. expected confidence, distortion, DetectorParameters) + * For each configuration, it creates a synthetic image containing four markers arranged in a 2x2 grid. + * Each marker is generated with its own configuration (id, size, rotation). + * Finally, it runs the detector and checks that each marker is detected and + * that its computed confidence is close to the ground truth value. + * + */ +static void runArucoDetectionConfidence(ArucoAlgParams arucoAlgParam) { + aruco::DetectorParameters params; + // make sure there are no bits have any detection errors + params.maxErroneousBitsInBorderRate = 0.0; + params.errorCorrectionRate = 0.0; + params.perspectiveRemovePixelPerCell = 8; // esnsure that there is enough resolution to properly handle distortions + aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params); + + const bool detectInvertedMarker = (arucoAlgParam == ArucoAlgParams::DETECT_INVERTED_MARKER); + + // define several detector configurations to test different settings + // {{MarkerTemperingConfig}, perspectiveRemoveIgnoredMarginPerCell, markerBorderBits, distortionRatio} + vector detectorConfigs = { + // No margins, No distortion + {{0.f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.f}, + {{0.01f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.f}, + {{0.05f, 100, MarkerRegionToTemper::ALL}, 0.0f, 2, 0.f}, + {{0.1f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.f}, + {{0.15f, 30, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.f}, + {{0.20f, 55, MarkerRegionToTemper::ALL}, 0.0f, 2, 0.f}, + // Margins, No distortion + {{0.f, 26, MarkerRegionToTemper::BORDER}, 0.05f, 1, 0.f}, + {{0.01f, 56, MarkerRegionToTemper::BORDER}, 0.05f, 2, 0.f}, + {{0.05f, 144, MarkerRegionToTemper::ALL}, 0.1f, 3, 0.f}, + {{0.10f, 49, MarkerRegionToTemper::ALL}, 0.15f, 1, 0.f}, + // No margins, distortion + {{0.f, 36, MarkerRegionToTemper::INNER}, 0.0f, 1, 0.01f}, + {{0.01f, 36, MarkerRegionToTemper::INNER}, 0.0f, 1, 0.02f}, + {{0.05f, 12, MarkerRegionToTemper::INNER}, 0.0f, 2, 0.05f}, + {{0.1f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.1f}, + {{0.1f, 81, MarkerRegionToTemper::ALL}, 0.0f, 2, 0.2f}, + // Margins, distortion + {{0.f, 81, MarkerRegionToTemper::ALL}, 0.05f, 2, 0.01f}, + {{0.01f, 64, MarkerRegionToTemper::ALL}, 0.05f, 1, 0.02f}, + {{0.05f, 81, MarkerRegionToTemper::ALL}, 0.1f, 2, 0.05f}, + {{0.1f, 64, MarkerRegionToTemper::ALL}, 0.15f, 1, 0.1f}, + {{0.1f, 64, MarkerRegionToTemper::ALL}, 0.0f, 1, 0.2f}, + // no marker detection, too much tempering + {{0.9f, 1, MarkerRegionToTemper::ALL}, 0.05f, 2, 0.0f}, + {{0.9f, 1, MarkerRegionToTemper::BORDER}, 0.05f, 2, 0.0f}, + {{0.9f, 1, MarkerRegionToTemper::INNER}, 0.05f, 2, 0.0f}, + }; + + // define marker configurations for the 4 markers in each image + const int markerSidePixels = 480; // To simplify the cell division, markerSidePixels is a multiple of 8. (6x6 dict + 2 border bits) + vector markerCreationConfig = { + {0, markerSidePixels, markerRot::ROT_90}, // {id, markerSidePixels, rotation} + {1, markerSidePixels, markerRot::ROT_270}, + {2, markerSidePixels, markerRot::NONE}, + {3, markerSidePixels, markerRot::ROT_180} + }; + + // loop over each detector configuration + for (size_t cfgIdx = 0; cfgIdx < detectorConfigs.size(); cfgIdx++) { + ArucoConfidenceTestConfig detCfg = detectorConfigs[cfgIdx]; + SCOPED_TRACE(cv::format("detectorConfig=%zu", cfgIdx)); + + // update detector parameters + params.perspectiveRemoveIgnoredMarginPerCell = detCfg.perspectiveRemoveIgnoredMarginPerCell; + params.markerBorderBits = detCfg.markerBorderBits; + params.detectInvertedMarker = detectInvertedMarker; + detector.setDetectorParameters(params); + + // create a blank image large enough to hold 4 markers in a 2x2 grid + const int margin = markerSidePixels / 2; + const int imageSize = (markerSidePixels * 2) + margin * 3; + Mat img(imageSize, imageSize, CV_8UC1, Scalar(255)); + + vector groundTruths; + const aruco::Dictionary &dictionary = detector.getDictionary(); + + // place each marker into the image + for (int row = 0; row < 2; row++) { + for (int col = 0; col < 2; col++) { + int index = row * 2 + col; + MarkerCreationConfig markerCfg = markerCreationConfig[index]; + // adjust marker id to be unique for each detector configuration + markerCfg.id += static_cast(cfgIdx * markerCreationConfig.size()); + + // generate img + Mat markerImg; + markerDetectionGT gt = generateTemperedMarkerImage(markerImg, markerCfg, detCfg.markerTemperingConfig, params, dictionary, detCfg.distortionRatio); + groundTruths.push_back(gt); + + // place marker in the image + Point2f topLeft(static_cast(margin + col * (markerSidePixels + margin)), + static_cast(margin + row * (markerSidePixels + margin))); + placeMarker(img, markerImg, topLeft); + } + } + + // if testing inverted markers globally, invert the whole image + if (detectInvertedMarker) { + bitwise_not(img, img); + } + + // run detection. + vector> corners, rejected; + vector ids; + vector markerConfidence; + detector.detectMarkersWithConfidence(img, corners, ids, markerConfidence, rejected); + + ASSERT_EQ(ids.size(), corners.size()); + ASSERT_EQ(ids.size(), markerConfidence.size()); + + std::map confidenceById; + for (size_t i = 0; i < ids.size(); i++) { + confidenceById[ids[i]] = markerConfidence[i]; + } + + // verify that every marker is detected and its confidence is within tolerance + for (const auto& currentGT : groundTruths) { + const bool detected = confidenceById.find(currentGT.id) != confidenceById.end(); + EXPECT_EQ(currentGT.expectDetection, detected) << "Marker id: " << currentGT.id; + + if (currentGT.expectDetection && detected) { + EXPECT_NEAR(currentGT.confidence, confidenceById[currentGT.id], 0.05) + << "Marker id: " << currentGT.id; + } + } + } +} /** * @brief Check max and min size in marker detection parameters @@ -552,6 +904,83 @@ TEST(CV_ArucoBitCorrection, algorithmic) { test.safe_run(); } +TEST(CV_ArucoDetectionConfidence, algorithmic) { + runArucoDetectionConfidence(ArucoAlgParams::USE_DEFAULT); +} + +TEST(CV_InvertedArucoDetectionConfidence, algorithmic) { + runArucoDetectionConfidence(ArucoAlgParams::DETECT_INVERTED_MARKER); +} + +TEST(CV_InvertedFlagArucoDetectionConfidence, algorithmic) { + aruco::DetectorParameters params; + params.maxErroneousBitsInBorderRate = 0.0; + params.errorCorrectionRate = 0.0; + params.perspectiveRemovePixelPerCell = 8; + params.detectInvertedMarker = false; + + const aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250); + + // create a blank image large enough to hold 4 markers in a 2x2 grid + const int markerSidePixels = 480; + const int margin = markerSidePixels / 2; + const int imageSize = (markerSidePixels * 2) + margin * 3; + Mat img(imageSize, imageSize, CV_8UC1, Scalar(255)); + + // place 4 markers into the image + for (int row = 0; row < 2; row++) { + for (int col = 0; col < 2; col++) { + const int id = row * 2 + col; + Mat markerImg; + aruco::generateImageMarker(dictionary, id, markerSidePixels, markerImg, params.markerBorderBits); + + Point2f topLeft(static_cast(margin + col * (markerSidePixels + margin)), + static_cast(margin + row * (markerSidePixels + margin))); + placeMarker(img, markerImg, topLeft); + } + } + + // run detection with detectInvertedMarker = false (baseline) + aruco::ArucoDetector detector(dictionary, params); + vector> corners, rejected; + vector ids; + vector confidenceDefault; + detector.detectMarkersWithConfidence(img, corners, ids, confidenceDefault, rejected); + ASSERT_EQ(ids.size(), corners.size()); + ASSERT_EQ(ids.size(), confidenceDefault.size()); + + std::map confidenceByIdDefault; + for (size_t i = 0; i < ids.size(); i++) { + confidenceByIdDefault[ids[i]] = confidenceDefault[i]; + } + + // run detection with detectInvertedMarker = true, without inverting the image + params.detectInvertedMarker = true; + aruco::ArucoDetector detectorInvertedFlag(dictionary, params); + vector confidenceInvertedFlag; + detectorInvertedFlag.detectMarkersWithConfidence(img, corners, ids, confidenceInvertedFlag, rejected); + ASSERT_EQ(ids.size(), corners.size()); + ASSERT_EQ(ids.size(), confidenceInvertedFlag.size()); + + std::map confidenceByIdInvertedFlag; + for (size_t i = 0; i < ids.size(); i++) { + confidenceByIdInvertedFlag[ids[i]] = confidenceInvertedFlag[i]; + } + + // detectInvertedMarker should not invert/flip confidence for non-inverted markers. + for (int id = 0; id < 4; id++) { + ASSERT_NE(confidenceByIdDefault.find(id), confidenceByIdDefault.end()) << "Marker id: " << id; + ASSERT_NE(confidenceByIdInvertedFlag.find(id), confidenceByIdInvertedFlag.end()) << "Marker id: " << id; + + const float confDefault = confidenceByIdDefault[id]; + const float confInvertedFlag = confidenceByIdInvertedFlag[id]; + + EXPECT_GT(confDefault, 0.8f) << "Marker id: " << id; + EXPECT_GT(confInvertedFlag, 0.8f) << "Marker id: " << id; + EXPECT_NEAR(confDefault, confInvertedFlag, 0.2f) << "Marker id: " << id; + } +} + TEST(CV_ArucoDetectMarkers, regression_3192) { aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_4X4_50)); diff --git a/modules/photo/src/hdr_common.cpp b/modules/photo/src/hdr_common.cpp index 70b24322b1..b2f2def688 100644 --- a/modules/photo/src/hdr_common.cpp +++ b/modules/photo/src/hdr_common.cpp @@ -59,16 +59,17 @@ void checkImageDimensions(const std::vector& images) } } -Mat triangleWeights() +Mat triangleWeights(int length) { // hat function - Mat w(LDR_SIZE, 1, CV_32F); - int half = LDR_SIZE / 2; - int maxVal = LDR_SIZE - 1; + CV_Assert(length >= 2); + Mat w(length, 1, CV_32F); + int half = length / 2; + int maxVal = length - 1; float epsilon = 1e-6f; w.at(0) = epsilon; - w.at(LDR_SIZE-1) = epsilon; - for (int i = 1; i < LDR_SIZE-1; i++){ + w.at(length - 1) = epsilon; + for (int i = 1; i < length - 1; i++){ w.at(i) = (i < half) ? static_cast(i) : static_cast(maxVal - i); @@ -76,15 +77,16 @@ Mat triangleWeights() return w; } -Mat RobertsonWeights() +Mat RobertsonWeights(int length) { - Mat weight(LDR_SIZE, 1, CV_32FC3); - float q = (LDR_SIZE - 1) / 4.0f; + CV_Assert(length >= 1); + Mat weight(length, 1, CV_32FC3); + float q = (length - 1) / 4.0f; float e4 = exp(4.f); float scale = e4/(e4 - 1.f); float shift = 1 / (1.f - e4); - for(int i = 0; i < LDR_SIZE; i++) { + for(int i = 0; i < length; i++) { float value = i / q - 2.0f; value = scale*exp(-value * value) + shift; weight.at(i) = Vec3f::all(value); @@ -104,11 +106,28 @@ void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation) merge(channels, dst); } -Mat linearResponse(int channels) +Mat linearResponse(int channels, int length) { - Mat response = Mat(LDR_SIZE, 1, CV_MAKETYPE(CV_32F, channels)); - for(int i = 0; i < LDR_SIZE; i++) { - response.at(i) = Vec3f::all(static_cast(i)); + CV_Assert(length >= 1); + Mat response = Mat(length, 1, CV_MAKETYPE(CV_32F, channels)); + + if (channels == 1) + { + for (int i = 0; i < length; i++) + { + response.at(i) = static_cast(i); + } + } + else if (channels == 3) + { + for (int i = 0; i < length; i++) + { + response.at(i) = Vec3f::all(static_cast(i)); + } + } + else + { + CV_Error(Error::StsBadArg, "Unsupported number of channels in linearResponse"); } return response; } diff --git a/modules/photo/src/hdr_common.hpp b/modules/photo/src/hdr_common.hpp index b9846fd7e9..cb96068bd9 100644 --- a/modules/photo/src/hdr_common.hpp +++ b/modules/photo/src/hdr_common.hpp @@ -50,13 +50,13 @@ namespace cv void checkImageDimensions(const std::vector& images); -Mat triangleWeights(); +Mat triangleWeights(int length = LDR_SIZE); void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation); -Mat RobertsonWeights(); +Mat RobertsonWeights(int length = LDR_SIZE); -Mat linearResponse(int channels); +Mat linearResponse(int channels, int length = LDR_SIZE); } #endif diff --git a/modules/photo/src/merge.cpp b/modules/photo/src/merge.cpp index 0707d27e9f..2f7868b4bd 100644 --- a/modules/photo/src/merge.cpp +++ b/modules/photo/src/merge.cpp @@ -66,25 +66,46 @@ public: CV_Assert(images.size() == times.total()); checkImageDimensions(images); - CV_Assert(images[0].depth() == CV_8U); + int depth = images[0].depth(); + CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_32F); int channels = images[0].channels(); Size size = images[0].size(); int CV_32FCC = CV_MAKETYPE(CV_32F, channels); + const bool use16bitLUT = (depth == CV_16U || depth == CV_32F); + const int lutLength = use16bitLUT ? 65536 : LDR_SIZE; + + std::vector lutImages(images.size()); + if (depth == CV_8U || depth == CV_16U) + { + lutImages = images; + } + else + { + const double scale = static_cast(lutLength - 1); + for (size_t i = 0; i < images.size(); ++i) + { + Mat clipped; + cv::max(images[i], 0.0, clipped); + cv::min(clipped, 1.0, clipped); + clipped.convertTo(lutImages[i], CV_16U, scale); + } + } + dst.create(images[0].size(), CV_32FCC); Mat result = dst.getMat(); Mat response = input_response.getMat(); if(response.empty()) { - response = linearResponse(channels); + response = linearResponse(channels, lutLength); response.at(0) = response.at(1); } Mat log_response; log(response, log_response); - CV_Assert(log_response.rows == LDR_SIZE && log_response.cols == 1 && + CV_Assert(log_response.rows == lutLength && log_response.cols == 1 && log_response.channels() == channels); Mat exp_values(times.clone()); @@ -95,19 +116,23 @@ public: split(result, result_split); Mat weight_sum = Mat::zeros(size, CV_32F); + Mat weights_lut = use16bitLUT + ? triangleWeights(lutLength) + : weights; + for(size_t i = 0; i < images.size(); i++) { std::vector splitted; - split(images[i], splitted); + split(lutImages[i], splitted); Mat w = Mat::zeros(size, CV_32F); for(int c = 0; c < channels; c++) { - LUT(splitted[c], weights, splitted[c]); + LUT(splitted[c], weights_lut, splitted[c]); w += splitted[c]; } w /= channels; Mat response_img; - LUT(images[i], log_response, response_img); + LUT(lutImages[i], log_response, response_img); split(response_img, splitted); for(int c = 0; c < channels; c++) { result_split[c] += w.mul(splitted[c] - exp_values.at((int)i)); @@ -328,28 +353,52 @@ public: CV_Assert(images.size() == times.total()); checkImageDimensions(images); - CV_Assert(images[0].depth() == CV_8U); + int depth = images[0].depth(); + CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_32F); int channels = images[0].channels(); int CV_32FCC = CV_MAKETYPE(CV_32F, channels); + const bool use16bitLUT = (depth == CV_16U || depth == CV_32F); + const int lutLength = use16bitLUT ? 65536 : LDR_SIZE; + + // Build LUT index images (see MergeDebevecImpl for details). + std::vector lutImages(images.size()); + if (depth == CV_8U || depth == CV_16U) + { + lutImages = images; + } + else // CV_32F + { + const double scale = static_cast(lutLength - 1); + for (size_t i = 0; i < images.size(); ++i) + { + images[i].convertTo(lutImages[i], CV_16U, scale); + } + } + dst.create(images[0].size(), CV_32FCC); Mat result = dst.getMat(); Mat response = input_response.getMat(); if(response.empty()) { - float middle = static_cast(LDR_SIZE) / 2.0f; - response = linearResponse(channels) / middle; + float middle = static_cast(lutLength) / 2.0f; + response = linearResponse(channels, lutLength) / middle; } - CV_Assert(response.rows == LDR_SIZE && response.cols == 1 && + CV_Assert(response.rows == lutLength && response.cols == 1 && response.channels() == channels); result = Mat::zeros(images[0].size(), CV_32FCC); Mat wsum = Mat::zeros(images[0].size(), CV_32FCC); + + Mat weight_lut = use16bitLUT + ? RobertsonWeights(lutLength) + : weight; + for(size_t i = 0; i < images.size(); i++) { Mat im, w; - LUT(images[i], weight, w); - LUT(images[i], response, im); + LUT(lutImages[i], weight_lut, w); + LUT(lutImages[i], response, im); result += times.at((int)i) * w.mul(im); wsum += times.at((int)i) * times.at((int)i) * w; diff --git a/modules/photo/test/test_hdr.cpp b/modules/photo/test/test_hdr.cpp index 264a7d7257..9922a01fff 100644 --- a/modules/photo/test/test_hdr.cpp +++ b/modules/photo/test/test_hdr.cpp @@ -209,6 +209,68 @@ TEST(Photo_MergeRobertson, regression) checkEqual(expected, result, eps, "MergeRobertson"); } +TEST(Photo_MergeDebevec, regression_depth_consistency) +{ + string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/"; + + vector images8; + vector times; + loadExposureSeq(test_path + "exposures/", images8, times); + + vector images16(images8.size()), images32(images8.size()); + for (size_t i = 0; i < images8.size(); ++i) + { + images8[i].convertTo(images16[i], CV_16UC3, 257.0); + images8[i].convertTo(images32[i], CV_32FC3, 1.0 / 255.0); + } + + Ptr merge = createMergeDebevec(); + Ptr map = createTonemap(); + + Mat hdr8, hdr16, hdr32; + merge->process(images8, hdr8, times); + merge->process(images16, hdr16, times); + merge->process(images32, hdr32, times); + + map->process(hdr8, hdr8); + map->process(hdr16, hdr16); + map->process(hdr32, hdr32); + + checkEqual(hdr8, hdr16, 2e-2f, "Debevec realdata 16U vs 8U"); + checkEqual(hdr8, hdr32, 2e-2f, "Debevec realdata 32F vs 8U"); +} + +TEST(Photo_MergeRobertson, regression_depth_consistency) +{ + string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/"; + + vector images8; + vector times; + loadExposureSeq(test_path + "exposures/", images8, times); + + vector images16(images8.size()), images32(images8.size()); + for (size_t i = 0; i < images8.size(); ++i) + { + images8[i].convertTo(images16[i], CV_16UC3, 257.0); + images8[i].convertTo(images32[i], CV_32FC3, 1.0 / 255.0); + } + + Ptr merge = createMergeRobertson(); + Ptr map = createTonemap(); + + Mat hdr8, hdr16, hdr32; + merge->process(images8, hdr8, times); + merge->process(images16, hdr16, times); + merge->process(images32, hdr32, times); + + map->process(hdr8, hdr8); + map->process(hdr16, hdr16); + map->process(hdr32, hdr32); + + checkEqual(hdr8, hdr16, 3e-2f, "Robertson realdata 16U vs 8U"); + checkEqual(hdr8, hdr32, 3e-2f, "Robertson realdata 32F vs 8U"); +} + TEST(Photo_CalibrateDebevec, regression) { string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/"; diff --git a/modules/python/package/setup.py b/modules/python/package/setup.py index ed143b7bcf..d0569be159 100644 --- a/modules/python/package/setup.py +++ b/modules/python/package/setup.py @@ -67,6 +67,8 @@ def main(): "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: C++", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Scientific/Engineering", diff --git a/modules/python/standalone.cmake b/modules/python/standalone.cmake index 1188a02eb9..25a669803e 100644 --- a/modules/python/standalone.cmake +++ b/modules/python/standalone.cmake @@ -29,6 +29,9 @@ if(NOT PYTHON_NUMPY_INCLUDE_DIRS) message(FATAL_ERROR "Can't find Python 'numpy' development files") endif() +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVInstallLayout.cmake") +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectDLPack.cmake") + status("-----------------------------------------------------------------") status(" Python:") status(" Interpreter:" "${PYTHON_EXECUTABLE} (ver ${PYTHON_VERSION_STRING})") diff --git a/modules/python/test/test_cuda.py b/modules/python/test/test_cuda.py index 4362cca044..ce1c901085 100644 --- a/modules/python/test/test_cuda.py +++ b/modules/python/test/test_cuda.py @@ -52,9 +52,9 @@ class cuda_test(NewOpenCVTests): self.assertTrue(asyncstream.cudaPtr() != 0) def test_cuda_buffer_pool(self): + stream_a = cv.cuda.Stream() cv.cuda.setBufferPoolUsage(True) cv.cuda.setBufferPoolConfig(cv.cuda.getDevice(), 1024 * 1024 * 64, 2) - stream_a = cv.cuda.Stream() pool_a = cv.cuda.BufferPool(stream_a) cuMat = pool_a.getBuffer(1024, 1024, cv.CV_8UC3) cv.cuda.setBufferPoolUsage(False) @@ -70,7 +70,6 @@ class cuda_test(NewOpenCVTests): self.assertTrue(cuMat.step == 0) self.assertTrue(cuMat.size() == (0, 0)) - @unittest.skip("failed test") def test_cuda_convertTo(self): # setup npMat_8UC4 = (np.random.random((128, 128, 4)) * 255).astype(np.uint8) @@ -106,7 +105,6 @@ class cuda_test(NewOpenCVTests): stream.waitForCompletion() self.assertTrue(np.array_equal(npMat_32FC4, npMat_32FC4_out)) - @unittest.skip("failed test") def test_cuda_copyTo(self): # setup npMat_8UC4 = (np.random.random((128, 128, 4)) * 255).astype(np.uint8) diff --git a/modules/stitching/src/cuda/build_warp_maps.cu b/modules/stitching/src/cuda/build_warp_maps.cu index d9e94276d5..42d55720a7 100644 --- a/modules/stitching/src/cuda/build_warp_maps.cu +++ b/modules/stitching/src/cuda/build_warp_maps.cu @@ -54,23 +54,23 @@ namespace cv { namespace cuda { namespace device { // TODO use intrinsics like __sinf and so on - namespace build_warp_maps + struct WarpParams { - - __constant__ float ck_rinv[9]; - __constant__ float cr_kinv[9]; - __constant__ float ct[3]; - __constant__ float cscale; - } - + float k_rinv[9]; + float r_kinv[9]; + float t[3]; + float scale; + }; class PlaneMapper { public: - static __device__ __forceinline__ void mapBackward(float u, float v, float &x, float &y) + static __device__ __forceinline__ void mapBackward(float u, float v, float &x, float &y, + const WarpParams& params) { - using namespace build_warp_maps; - + const float *ck_rinv = params.k_rinv; + const float *ct = params.t; + const float cscale = params.scale; float x_ = u / cscale - ct[0]; float y_ = v / cscale - ct[1]; @@ -88,10 +88,11 @@ namespace cv { namespace cuda { namespace device class CylindricalMapper { public: - static __device__ __forceinline__ void mapBackward(float u, float v, float &x, float &y) + static __device__ __forceinline__ void mapBackward(float u, float v, float &x, float &y, + const WarpParams& params) { - using namespace build_warp_maps; - + const float *ck_rinv = params.k_rinv; + const float cscale = params.scale; u /= cscale; float x_ = ::sinf(u); float y_ = v / cscale; @@ -111,10 +112,11 @@ namespace cv { namespace cuda { namespace device class SphericalMapper { public: - static __device__ __forceinline__ void mapBackward(float u, float v, float &x, float &y) + static __device__ __forceinline__ void mapBackward(float u, float v, float &x, float &y, + const WarpParams& params) { - using namespace build_warp_maps; - + const float *ck_rinv = params.k_rinv; + const float cscale = params.scale; v /= cscale; u /= cscale; @@ -136,7 +138,8 @@ namespace cv { namespace cuda { namespace device template __global__ void buildWarpMapsKernel(int tl_u, int tl_v, int cols, int rows, - PtrStepf map_x, PtrStepf map_y) + PtrStepf map_x, PtrStepf map_y, + const WarpParams params) { int du = blockIdx.x * blockDim.x + threadIdx.x; int dv = blockIdx.y * blockDim.y + threadIdx.y; @@ -145,7 +148,7 @@ namespace cv { namespace cuda { namespace device float u = tl_u + du; float v = tl_v + dv; float x, y; - Mapper::mapBackward(u, v, x, y); + Mapper::mapBackward(u, v, x, y, params); map_x.ptr(dv)[du] = x; map_y.ptr(dv)[du] = y; } @@ -156,10 +159,16 @@ namespace cv { namespace cuda { namespace device const float k_rinv[9], const float r_kinv[9], const float t[3], float scale, cudaStream_t stream) { - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::ck_rinv, k_rinv, 9*sizeof(float))); - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::cr_kinv, r_kinv, 9*sizeof(float))); - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::ct, t, 3*sizeof(float))); - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::cscale, &scale, sizeof(float))); + WarpParams params; + for (int i = 0; i < 9; ++i) + { + params.k_rinv[i] = k_rinv[i]; + params.r_kinv[i] = r_kinv[i]; + } + params.t[0] = t[0]; + params.t[1] = t[1]; + params.t[2] = t[2]; + params.scale = scale; int cols = map_x.cols; int rows = map_x.rows; @@ -167,7 +176,8 @@ namespace cv { namespace cuda { namespace device dim3 threads(32, 8); dim3 grid(divUp(cols, threads.x), divUp(rows, threads.y)); - buildWarpMapsKernel<<>>(tl_u, tl_v, cols, rows, map_x, map_y); + buildWarpMapsKernel<<>>(tl_u, tl_v, cols, rows, + map_x, map_y, params); cudaSafeCall(cudaGetLastError()); if (stream == 0) cudaSafeCall(cudaDeviceSynchronize()); @@ -178,9 +188,16 @@ namespace cv { namespace cuda { namespace device const float k_rinv[9], const float r_kinv[9], float scale, cudaStream_t stream) { - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::ck_rinv, k_rinv, 9*sizeof(float))); - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::cr_kinv, r_kinv, 9*sizeof(float))); - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::cscale, &scale, sizeof(float))); + WarpParams params; + for (int i = 0; i < 9; ++i) + { + params.k_rinv[i] = k_rinv[i]; + params.r_kinv[i] = r_kinv[i]; + } + params.t[0] = 0.f; + params.t[1] = 0.f; + params.t[2] = 0.f; + params.scale = scale; int cols = map_x.cols; int rows = map_x.rows; @@ -188,7 +205,8 @@ namespace cv { namespace cuda { namespace device dim3 threads(32, 8); dim3 grid(divUp(cols, threads.x), divUp(rows, threads.y)); - buildWarpMapsKernel<<>>(tl_u, tl_v, cols, rows, map_x, map_y); + buildWarpMapsKernel<<>>(tl_u, tl_v, cols, rows, + map_x, map_y, params); cudaSafeCall(cudaGetLastError()); if (stream == 0) cudaSafeCall(cudaDeviceSynchronize()); @@ -199,9 +217,16 @@ namespace cv { namespace cuda { namespace device const float k_rinv[9], const float r_kinv[9], float scale, cudaStream_t stream) { - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::ck_rinv, k_rinv, 9*sizeof(float))); - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::cr_kinv, r_kinv, 9*sizeof(float))); - cudaSafeCall(cudaMemcpyToSymbol(build_warp_maps::cscale, &scale, sizeof(float))); + WarpParams params; + for (int i = 0; i < 9; ++i) + { + params.k_rinv[i] = k_rinv[i]; + params.r_kinv[i] = r_kinv[i]; + } + params.t[0] = 0.f; + params.t[1] = 0.f; + params.t[2] = 0.f; + params.scale = scale; int cols = map_x.cols; int rows = map_x.rows; @@ -209,7 +234,8 @@ namespace cv { namespace cuda { namespace device dim3 threads(32, 8); dim3 grid(divUp(cols, threads.x), divUp(rows, threads.y)); - buildWarpMapsKernel<<>>(tl_u, tl_v, cols, rows, map_x, map_y); + buildWarpMapsKernel<<>>(tl_u, tl_v, cols, rows, + map_x, map_y, params); cudaSafeCall(cudaGetLastError()); if (stream == 0) cudaSafeCall(cudaDeviceSynchronize()); diff --git a/modules/stitching/src/seam_finders.cpp b/modules/stitching/src/seam_finders.cpp index a2c6a3efb4..3b29094ba8 100644 --- a/modules/stitching/src/seam_finders.cpp +++ b/modules/stitching/src/seam_finders.cpp @@ -338,8 +338,8 @@ void DpSeamFinder::findComponents() states_.push_back(SECOND); floodFill(labels_, Point(x, y), ++ncomps_); - tls_.push_back(Point(x, y)); - brs_.push_back(Point(x+1, y+1)); + tls_.emplace_back(x,y); + brs_.emplace_back(x+1, y+1); contours_.push_back(std::vector()); } @@ -356,7 +356,7 @@ void DpSeamFinder::findComponents() if ((x == 0 || labels_(y, x-1) != l) || (x == unionSize_.width-1 || labels_(y, x+1) != l) || (y == 0 || labels_(y-1, x) != l) || (y == unionSize_.height-1 || labels_(y+1, x) != l)) { - contours_[ci].push_back(Point(x, y)); + contours_[ci].emplace_back(x,y); } } } @@ -517,7 +517,7 @@ void DpSeamFinder::resolveConflicts( if ((x == 0 || labels_(y, x-1) != l[i]) || (x == unionSize_.width-1 || labels_(y, x+1) != l[i]) || (y == 0 || labels_(y-1, x) != l[i]) || (y == unionSize_.height-1 || labels_(y+1, x) != l[i])) { - contours_[c[i]].push_back(Point(x, y)); + contours_[c[i]].emplace_back(x,y); } } } @@ -637,7 +637,7 @@ bool DpSeamFinder::getSeamTips(int comp1, int comp2, Point &p1, Point &p2) (x < unionSize_.width-1 && labels_(y, x+1) == l2) || (y < unionSize_.height-1 && labels_(y+1, x) == l2))) { - specialPoints.push_back(Point(x, y)); + specialPoints.emplace_back(x,y); } } diff --git a/modules/ts/src/ocl_test.cpp b/modules/ts/src/ocl_test.cpp index 07fb1cdc4a..cc584a7390 100644 --- a/modules/ts/src/ocl_test.cpp +++ b/modules/ts/src/ocl_test.cpp @@ -104,7 +104,6 @@ double TestUtils::checkRectSimilarity(const Size & sz, std::vector& ob1, s { cv::Mat cpu_result_roi(cpu_result, *r); cpu_result_roi.setTo(1); - cpu_result.copyTo(cpu_result); } int cpu_area = cv::countNonZero(cpu_result > 0); @@ -114,7 +113,6 @@ double TestUtils::checkRectSimilarity(const Size & sz, std::vector& ob1, s { cv::Mat gpu_result_roi(gpu_result, *r2); gpu_result_roi.setTo(1); - gpu_result.copyTo(gpu_result); } cv::Mat result_; diff --git a/modules/video/include/opencv2/video/background_segm.hpp b/modules/video/include/opencv2/video/background_segm.hpp index 73409f27d4..2d3ea823b3 100644 --- a/modules/video/include/opencv2/video/background_segm.hpp +++ b/modules/video/include/opencv2/video/background_segm.hpp @@ -117,7 +117,7 @@ public: CV_WRAP virtual int getNMixtures() const = 0; /** @brief Sets the number of gaussian components in the background model. - The model needs to be reinitalized to reserve memory. + The model needs to be reinitialized to reserve memory. */ CV_WRAP virtual void setNMixtures(int nmixtures) = 0;//needs reinitialization! @@ -268,7 +268,7 @@ public: CV_WRAP virtual int getNSamples() const = 0; /** @brief Sets the number of data samples in the background model. - The model needs to be reinitalized to reserve memory. + The model needs to be reinitialized to reserve memory. */ CV_WRAP virtual void setNSamples(int _nN) = 0;//needs reinitialization! diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index 873f6f04d1..542f02a085 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -700,6 +700,16 @@ public: /** @copybrief getFinestScale @see getFinestScale */ CV_WRAP virtual void setFinestScale(int val) = 0; + /** @brief Sets the coarsest scale + @param val Coarsest level of the Gaussian pyramid on which the flow is computed. + If set to -1, the auto-computed coarsest scale will be used. + */ + CV_WRAP virtual void setCoarsestScale(int val) = 0; + + /** @brief Gets the coarsest scale + */ + CV_WRAP virtual int getCoarsestScale() const = 0; + /** @brief Size of an image patch for matching (in pixels). Normally, default 8x8 patches work well enough in most cases. @see setPatchSize */ diff --git a/modules/video/perf/perf_ecc.cpp b/modules/video/perf/perf_ecc.cpp index 217b98fa0e..f81f64d8a6 100644 --- a/modules/video/perf/perf_ecc.cpp +++ b/modules/video/perf/perf_ecc.cpp @@ -55,7 +55,16 @@ PERF_TEST_P(ECCPerfTest, findTransformECC, TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 5, -1)); } - SANITY_CHECK(warpMat, 3e-3); + if (transform_type == MOTION_HOMOGRAPHY) + { + // NOTE: for Mac M1 + KleidiCV + // ECCPerfTest_findTransformECC.findTransformECC/6, where GetParam() = (MOTION_HOMOGRAPHY, IMREAD_GRAYSCALE) + SANITY_CHECK(warpMat, 8.3e-3); + } + else + { + SANITY_CHECK(warpMat, 3e-3); + } } } // namespace opencv_test diff --git a/modules/video/src/dis_flow.cpp b/modules/video/src/dis_flow.cpp index 75090d093d..c859555c22 100644 --- a/modules/video/src/dis_flow.cpp +++ b/modules/video/src/dis_flow.cpp @@ -60,6 +60,7 @@ class DISOpticalFlowImpl CV_FINAL : public DISOpticalFlow protected: //!< algorithm parameters int finest_scale, coarsest_scale; + int user_coarsest_scale; int patch_size; int patch_stride; int grad_descent_iter; @@ -79,6 +80,8 @@ class DISOpticalFlowImpl CV_FINAL : public DISOpticalFlow public: int getFinestScale() const CV_OVERRIDE { return finest_scale; } void setFinestScale(int val) CV_OVERRIDE { finest_scale = val; } + int getCoarsestScale() const CV_OVERRIDE { return user_coarsest_scale; } + void setCoarsestScale(int val) CV_OVERRIDE { user_coarsest_scale = val; } int getPatchSize() const CV_OVERRIDE { return patch_size; } void setPatchSize(int val) CV_OVERRIDE { patch_size = val; } int getPatchStride() const CV_OVERRIDE { return patch_stride; } @@ -228,6 +231,7 @@ DISOpticalFlowImpl::DISOpticalFlowImpl() use_mean_normalization = true; use_spatial_propagation = true; coarsest_scale = 10; + user_coarsest_scale = -1; /* Use separate variational refinement instances for different scales to avoid repeated memory allocation: */ int max_possible_scales = 10; @@ -1367,8 +1371,14 @@ bool DISOpticalFlowImpl::ocl_calc(InputArray I0, InputArray I1, InputOutputArray bool use_input_flow = false; if (flow.sameSize(I0) && flow.depth() == CV_32F && flow.channels() == 2) use_input_flow = true; - coarsest_scale = min((int)(log(max(I0Mat.cols, I0Mat.rows) / (4.0 * patch_size)) / log(2.0) + 0.5), /* Original code search for maximal movement of width/4 */ - (int)(log(min(I0Mat.cols, I0Mat.rows) / patch_size) / log(2.0))); /* Deepest pyramid level greater or equal than patch*/ + + int max_possible_scale = min((int)(log(max(I0Mat.cols, I0Mat.rows) / (4.0 * patch_size)) / log(2.0) + 0.5), /* Original code search for maximal movement of width/4 */ + (int)(log(min(I0Mat.cols, I0Mat.rows) / patch_size) / log(2.0))); /* Deepest pyramid level greater or equal than patch*/ + + if (user_coarsest_scale != -1) + coarsest_scale = min(user_coarsest_scale, max_possible_scale); + else + coarsest_scale = max_possible_scale; if (coarsest_scale<0) CV_Error(cv::Error::StsBadSize, "The input image must have either width or height >= 12"); @@ -1449,9 +1459,14 @@ void DISOpticalFlowImpl::calc(InputArray I0, InputArray I1, InputOutputArray flo else flow.create(I1Mat.size(), CV_32FC2); Mat flowMat = flow.getMat(); - coarsest_scale = min((int)(log(max(I0Mat.cols, I0Mat.rows) / (4.0 * patch_size)) / log(2.0) + 0.5), /* Original code search for maximal movement of width/4 */ - (int)(log(min(I0Mat.cols, I0Mat.rows) / patch_size) / log(2.0))); /* Deepest pyramid level greater or equal than patch*/ + int max_possible_scale = min((int)(log(max(I0Mat.cols, I0Mat.rows) / (4.0 * patch_size)) / log(2.0) + 0.5), /* Original code search for maximal movement of width/4 */ + (int)(log(min(I0Mat.cols, I0Mat.rows) / patch_size) / log(2.0))); /* Deepest pyramid level greater or equal than patch*/ + + if (user_coarsest_scale != -1) + coarsest_scale = min(user_coarsest_scale, max_possible_scale); + else + coarsest_scale = max_possible_scale; if (coarsest_scale<0) CV_Error(cv::Error::StsBadSize, "The input image must have either width or height >= 12"); diff --git a/modules/video/src/optflowgf.cpp b/modules/video/src/optflowgf.cpp index 02e878a577..7d654f747b 100644 --- a/modules/video/src/optflowgf.cpp +++ b/modules/video/src/optflowgf.cpp @@ -239,7 +239,6 @@ FarnebackUpdateMatrices( const Mat& _R0, const Mat& _R1, const Mat& _flow, Mat& #if 1 int x1 = cvFloor(fx), y1 = cvFloor(fy); - const float* ptr = R1 + y1*step1 + x1*5; float r2, r3, r4, r5, r6; fx -= x1; fy -= y1; @@ -247,6 +246,7 @@ FarnebackUpdateMatrices( const Mat& _R0, const Mat& _R1, const Mat& _flow, Mat& if( (unsigned)x1 < (unsigned)(width-1) && (unsigned)y1 < (unsigned)(height-1) ) { + const float* ptr = R1 + y1*step1 + x1*5; float a00 = (1.f-fx)*(1.f-fy), a01 = fx*(1.f-fy), a10 = (1.f-fx)*fy, a11 = fx*fy; @@ -262,12 +262,12 @@ FarnebackUpdateMatrices( const Mat& _R0, const Mat& _R1, const Mat& _flow, Mat& } #else int x1 = cvRound(fx), y1 = cvRound(fy); - const float* ptr = R1 + y1*step1 + x1*5; float r2, r3, r4, r5, r6; if( (unsigned)x1 < (unsigned)width && (unsigned)y1 < (unsigned)height ) { + const float* ptr = R1 + y1*step1 + x1*5; r2 = ptr[0]; r3 = ptr[1]; r4 = (R0[x*5+2] + ptr[2])*0.5f; diff --git a/modules/video/test/test_OF_accuracy.cpp b/modules/video/test/test_OF_accuracy.cpp index b99ffce2a8..cf51cd1dc4 100644 --- a/modules/video/test/test_OF_accuracy.cpp +++ b/modules/video/test/test_OF_accuracy.cpp @@ -172,4 +172,29 @@ TEST(DenseOpticalFlow_VariationalRefinement, ReferenceAccuracy) EXPECT_LE(calcRMSE(GT, flow), target_RMSE); } -}} // namespace +TEST(DenseOpticalFlow_DIS, ManualCoarsestScale) +{ + Mat prev = Mat::zeros(Size(320, 240), CV_8UC1); + Mat next = Mat::zeros(Size(320, 240), CV_8UC1); + randu(prev, 0, 255); + randu(next, 0, 255); + + Ptr dis = DISOpticalFlow::create(DISOpticalFlow::PRESET_FAST); + + EXPECT_EQ(dis->getCoarsestScale(), -1); + + Mat flow; + dis->calc(prev, next, flow); + EXPECT_FALSE(flow.empty()); + int manual_scale = 3; + dis->setCoarsestScale(manual_scale); + EXPECT_EQ(dis->getCoarsestScale(), manual_scale); + + dis->calc(prev, next, flow); + EXPECT_FALSE(flow.empty()); + + dis->setCoarsestScale(-1); + EXPECT_EQ(dis->getCoarsestScale(), -1); +} + +}} // namespace \ No newline at end of file diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index e40df9ce89..e38907de9e 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -149,7 +149,7 @@ enum VideoCaptureProperties { CAP_PROP_HUE =13, //!< Hue of the image (only for cameras). CAP_PROP_GAIN =14, //!< Gain of the image (only for those cameras that support). CAP_PROP_EXPOSURE =15, //!< Exposure (only for those cameras that support). - CAP_PROP_CONVERT_RGB =16, //!< Boolean flags indicating whether images should be converted to RGB.
+ CAP_PROP_CONVERT_RGB =16, //!< Boolean flags indicating whether images should be converted to BGR.
//!< *GStreamer note*: The flag is ignored in case if custom pipeline is used. It's user responsibility to interpret pipeline output. CAP_PROP_WHITE_BALANCE_BLUE_U =17, //!< Currently unsupported. CAP_PROP_RECTIFICATION =18, //!< Rectification flag for stereo cameras (note: only supported by DC1394 v 2.x backend currently). diff --git a/modules/videoio/src/cap_avfoundation.mm b/modules/videoio/src/cap_avfoundation.mm index 2bad9cc99c..4f74ddfaab 100644 --- a/modules/videoio/src/cap_avfoundation.mm +++ b/modules/videoio/src/cap_avfoundation.mm @@ -1155,228 +1155,198 @@ CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int double fps, const cv::Size& frame_size, int is_color) { - NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + @autoreleasepool { + frameCount = 0; + movieFPS = fps; + movieSize = frame_size; + movieColor = is_color; + argbimage = cv::Mat(movieSize, CV_8UC4); + path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain]; + /* + AVFileTypeQuickTimeMovie + UTI for the QuickTime movie file format. + The value of this UTI is com.apple.quicktime-movie. Files are identified with the .mov and .qt extensions. - frameCount = 0; - movieFPS = fps; - movieSize = frame_size; - movieColor = is_color; - argbimage = cv::Mat(movieSize, CV_8UC4); - path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain]; + AVFileTypeMPEG4 + UTI for the MPEG-4 file format. + The value of this UTI is public.mpeg-4. Files are identified with the .mp4 extension. + AVFileTypeAppleM4V + UTI for the iTunes video file format. + The value of this UTI is com.apple.mpeg-4-video. Files are identified with the .m4v extension. - /* - AVFileTypeQuickTimeMovie - UTI for the QuickTime movie file format. - The value of this UTI is com.apple.quicktime-movie. Files are identified with the .mov and .qt extensions. + AVFileType3GPP + UTI for the 3GPP file format. + The value of this UTI is public.3gpp. Files are identified with the .3gp, .3gpp, and .sdv extensions. + */ - AVFileTypeMPEG4 - UTI for the MPEG-4 file format. - The value of this UTI is public.mpeg-4. Files are identified with the .mp4 extension. - - AVFileTypeAppleM4V - UTI for the iTunes video file format. - The value of this UTI is com.apple.mpeg-4-video. Files are identified with the .m4v extension. - - AVFileType3GPP - UTI for the 3GPP file format. - The value of this UTI is public.3gpp. Files are identified with the .3gp, .3gpp, and .sdv extensions. - */ - - NSString *fileExt =[[[path pathExtension] lowercaseString] copy]; - if ([fileExt isEqualToString:@"mov"] || [fileExt isEqualToString:@"qt"]){ - fileType = [AVFileTypeQuickTimeMovie copy]; - }else if ([fileExt isEqualToString:@"mp4"]){ - fileType = [AVFileTypeMPEG4 copy]; - }else if ([fileExt isEqualToString:@"m4v"]){ - fileType = [AVFileTypeAppleM4V copy]; + NSString *fileExt = [[[path pathExtension] lowercaseString] copy]; + if ([fileExt isEqualToString:@"mov"] || [fileExt isEqualToString:@"qt"]) { + fileType = [AVFileTypeQuickTimeMovie copy]; + } else if ([fileExt isEqualToString:@"mp4"]) { + fileType = [AVFileTypeMPEG4 copy]; + } else if ([fileExt isEqualToString:@"m4v"]) { + fileType = [AVFileTypeAppleM4V copy]; #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR - }else if ([fileExt isEqualToString:@"3gp"] || [fileExt isEqualToString:@"3gpp"] || [fileExt isEqualToString:@"sdv"] ){ - fileType = [AVFileType3GPP copy]; + } else if ([fileExt isEqualToString:@"3gp"] || [fileExt isEqualToString:@"3gpp"] || [fileExt isEqualToString:@"sdv"]) { + fileType = [AVFileType3GPP copy]; #endif - } else{ - fileType = [AVFileTypeMPEG4 copy]; //default mp4 - } - [fileExt release]; + } else { + fileType = [AVFileTypeMPEG4 copy]; //default mp4 + } + [fileExt release]; - char cc[5]; - cc[0] = fourcc & 255; - cc[1] = (fourcc >> 8) & 255; - cc[2] = (fourcc >> 16) & 255; - cc[3] = (fourcc >> 24) & 255; - cc[4] = 0; - int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]); - if (cc2!=fourcc) { - std::cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc - << " but got " << cc2 << "." << std::endl; - //exception; - } + char cc[5]; + cc[0] = fourcc & 255; + cc[1] = (fourcc >> 8) & 255; + cc[2] = (fourcc >> 16) & 255; + cc[3] = (fourcc >> 24) & 255; + cc[4] = 0; + int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]); + if (cc2 != fourcc) { + std::cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc + << " but got " << cc2 << "." << std::endl; + } - // Three codec supported AVVideoCodecTypeH264 AVVideoCodecTypeJPEG AVVideoCodecTypeHEVC - // On iPhone 3G H264 is not supported. - if (fourcc == CV_FOURCC('J','P','E','G') || fourcc == CV_FOURCC('j','p','e','g') || - fourcc == CV_FOURCC('M','J','P','G') || fourcc == CV_FOURCC('m','j','p','g')){ - codec = [AVVideoCodecTypeJPEG copy]; // Use JPEG codec if specified, otherwise H264 - }else if(fourcc == CV_FOURCC('H','2','6','4') || fourcc == CV_FOURCC('a','v','c','1')){ - codec = [AVVideoCodecTypeH264 copy]; + // Three codec supported AVVideoCodecTypeH264 AVVideoCodecTypeJPEG AVVideoCodecTypeHEVC + // On iPhone 3G H264 is not supported. + if (fourcc == CV_FOURCC('J','P','E','G') || fourcc == CV_FOURCC('j','p','e','g') || + fourcc == CV_FOURCC('M','J','P','G') || fourcc == CV_FOURCC('m','j','p','g')) { + codec = [AVVideoCodecTypeJPEG copy]; // Use JPEG codec if specified, otherwise H264 + } else if (fourcc == CV_FOURCC('H','2','6','4') || fourcc == CV_FOURCC('a','v','c','1')) { + codec = [AVVideoCodecTypeH264 copy]; // Available since iOS 11 #if TARGET_OS_VISION || (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) - }else if(fourcc == CV_FOURCC('H','2','6','5') || fourcc == CV_FOURCC('h','v','c','1') || - fourcc == CV_FOURCC('H','E','V','C') || fourcc == CV_FOURCC('h','e','v','c')){ - if (@available(iOS 11, *)) { - codec = [AVVideoCodecTypeHEVC copy]; - } else { - codec = [AVVideoCodecTypeH264 copy]; - } + } else if (fourcc == CV_FOURCC('H','2','6','5') || fourcc == CV_FOURCC('h','v','c','1') || + fourcc == CV_FOURCC('H','E','V','C') || fourcc == CV_FOURCC('h','e','v','c')) { + if (@available(iOS 11, *)) { + codec = [AVVideoCodecTypeHEVC copy]; + } else { + codec = [AVVideoCodecTypeH264 copy]; + } #endif - }else{ - codec = [AVVideoCodecTypeH264 copy]; // default canonical H264. + } else { + codec = [AVVideoCodecTypeH264 copy]; // default canonical H264. + } + + NSError *error = nil; + + // Wire the writer: + // Supported file types: + // AVFileTypeQuickTimeMovie AVFileTypeMPEG4 AVFileTypeAppleM4V AVFileType3GPP + + mMovieWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] + fileType:fileType + error:&error]; + + NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: + codec, AVVideoCodecKey, + [NSNumber numberWithInt:movieSize.width], AVVideoWidthKey, + [NSNumber numberWithInt:movieSize.height], AVVideoHeightKey, + nil]; + + mMovieWriterInput = [[AVAssetWriterInput + assetWriterInputWithMediaType:AVMediaTypeVideo + outputSettings:videoSettings] retain]; + + [mMovieWriter addInput:mMovieWriterInput]; + + mMovieWriterAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:mMovieWriterInput sourcePixelBufferAttributes:nil]; + + //Start a session: + [mMovieWriter startWriting]; + [mMovieWriter startSessionAtSourceTime:kCMTimeZero]; + + if (mMovieWriter.status == AVAssetWriterStatusFailed) { + NSLog(@"%@", [mMovieWriter.error localizedDescription]); + // TODO: error handling, cleanup. Throw exception? + } } - - //NSLog(@"Path: %@", path); - - NSError *error = nil; - - - // Make sure the file does not already exist. Necessary to overwrite?? - /* - NSFileManager *fileManager = [NSFileManager defaultManager]; - if ([fileManager fileExistsAtPath:path]){ - [fileManager removeItemAtPath:path error:&error]; - } - */ - - // Wire the writer: - // Supported file types: - // AVFileTypeQuickTimeMovie AVFileTypeMPEG4 AVFileTypeAppleM4V AVFileType3GPP - - mMovieWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] - fileType:fileType - error:&error]; - //NSParameterAssert(mMovieWriter); - - NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: - codec, AVVideoCodecKey, - [NSNumber numberWithInt:movieSize.width], AVVideoWidthKey, - [NSNumber numberWithInt:movieSize.height], AVVideoHeightKey, - nil]; - - mMovieWriterInput = [[AVAssetWriterInput - assetWriterInputWithMediaType:AVMediaTypeVideo - outputSettings:videoSettings] retain]; - - //NSParameterAssert(mMovieWriterInput); - //NSParameterAssert([mMovieWriter canAddInput:mMovieWriterInput]); - - [mMovieWriter addInput:mMovieWriterInput]; - - mMovieWriterAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:mMovieWriterInput sourcePixelBufferAttributes:nil]; - - - //Start a session: - [mMovieWriter startWriting]; - [mMovieWriter startSessionAtSourceTime:kCMTimeZero]; - - - if(mMovieWriter.status == AVAssetWriterStatusFailed){ - NSLog(@"%@", [mMovieWriter.error localizedDescription]); - // TODO: error handling, cleanup. Throw exception? - // return; - } - - [localpool drain]; } CvVideoWriter_AVFoundation::~CvVideoWriter_AVFoundation() { - NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; - - [mMovieWriterInput markAsFinished]; - [mMovieWriter finishWritingWithCompletionHandler:^() { - [mMovieWriter release]; - [mMovieWriterInput release]; - [mMovieWriterAdaptor release]; - [path release]; - [codec release]; - [fileType release]; - argbimage.release(); - - [localpool drain]; - }]; + @autoreleasepool { + [mMovieWriterInput markAsFinished]; + [mMovieWriter finishWritingWithCompletionHandler:^() { + @autoreleasepool { + [mMovieWriter release]; + [mMovieWriterInput release]; + [mMovieWriterAdaptor release]; + [path release]; + [codec release]; + [fileType release]; + argbimage.release(); + } + }]; + } } void CvVideoWriter_AVFoundation::write(cv::InputArray image) { - NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + @autoreleasepool { + // writer status check + if (![mMovieWriterInput isReadyForMoreMediaData] || mMovieWriter.status != AVAssetWriterStatusWriting) { + NSLog(@"[mMovieWriterInput isReadyForMoreMediaData] Not ready for media data or ..."); + NSLog(@"mMovieWriter.status: %d. Error: %@", (int)mMovieWriter.status, [mMovieWriter.error localizedDescription]); + return; + } - // writer status check - if (![mMovieWriterInput isReadyForMoreMediaData] || mMovieWriter.status != AVAssetWriterStatusWriting ) { - NSLog(@"[mMovieWriterInput isReadyForMoreMediaData] Not ready for media data or ..."); - NSLog(@"mMovieWriter.status: %d. Error: %@", (int)mMovieWriter.status, [mMovieWriter.error localizedDescription]); - [localpool drain]; - return; + BOOL success = FALSE; + + if (image.size().height != movieSize.height || image.size().width != movieSize.width) { + std::cout << "Frame size does not match video size." << std::endl; + return; + } + + if (movieColor) { + cv::cvtColor(image, argbimage, cv::COLOR_BGR2BGRA); + } else { + cv::cvtColor(image, argbimage, cv::COLOR_GRAY2BGRA); + } + + //IplImage -> CGImage conversion + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + NSData *nsData = [NSData dataWithBytes:argbimage.data length:argbimage.total() * argbimage.elemSize()]; + CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)nsData); + CGImageRef cgImage = CGImageCreate(argbimage.size().width, argbimage.size().height, + 8, 32, argbimage.step[0], + colorSpace, kCGImageAlphaLast|kCGBitmapByteOrderDefault, + provider, NULL, false, kCGRenderingIntentDefault); + + //CGImage -> CVPixelBufferRef conversion + CVPixelBufferRef pixelBuffer = NULL; + CFDataRef cfData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage)); + int status = CVPixelBufferCreateWithBytes(NULL, + movieSize.width, + movieSize.height, + kCVPixelFormatType_32BGRA, + (void*)CFDataGetBytePtr(cfData), + CGImageGetBytesPerRow(cgImage), + NULL, + 0, + NULL, + &pixelBuffer); + if (status == kCVReturnSuccess) { + success = [mMovieWriterAdaptor appendPixelBuffer:pixelBuffer + withPresentationTime:CMTimeMake(frameCount, movieFPS)]; + } + + //cleanup + CFRelease(cfData); + CVPixelBufferRelease(pixelBuffer); + CGImageRelease(cgImage); + CGDataProviderRelease(provider); + CGColorSpaceRelease(colorSpace); + + if (success) { + frameCount++; + return; + } else { + NSLog(@"Frame appendPixelBuffer failed."); + return; + } } - - BOOL success = FALSE; - - if (image.size().height!=movieSize.height || image.size().width!=movieSize.width){ - std::cout<<"Frame size does not match video size."<nChannels == 3); - cv::cvtColor(image, argbimage, cv::COLOR_BGR2BGRA); - }else{ - //assert(image->nChannels == 1); - cv::cvtColor(image, argbimage, cv::COLOR_GRAY2BGRA); - } - //IplImage -> CGImage conversion - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - NSData *nsData = [NSData dataWithBytes:argbimage.data length:argbimage.total() * argbimage.elemSize()]; - CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)nsData); - CGImageRef cgImage = CGImageCreate(argbimage.size().width, argbimage.size().height, - 8, 32, argbimage.step[0], - colorSpace, kCGImageAlphaLast|kCGBitmapByteOrderDefault, - provider, NULL, false, kCGRenderingIntentDefault); - - //CGImage -> CVPixelBufferRef conversion - CVPixelBufferRef pixelBuffer = NULL; - CFDataRef cfData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage)); - int status = CVPixelBufferCreateWithBytes(NULL, - movieSize.width, - movieSize.height, - kCVPixelFormatType_32BGRA, - (void*)CFDataGetBytePtr(cfData), - CGImageGetBytesPerRow(cgImage), - NULL, - 0, - NULL, - &pixelBuffer); - if(status == kCVReturnSuccess){ - success = [mMovieWriterAdaptor appendPixelBuffer:pixelBuffer - withPresentationTime:CMTimeMake(frameCount, movieFPS)]; - } - - //cleanup - CFRelease(cfData); - CVPixelBufferRelease(pixelBuffer); - CGImageRelease(cgImage); - CGDataProviderRelease(provider); - CGColorSpaceRelease(colorSpace); - - [localpool drain]; - - if (success) { - frameCount ++; - //NSLog(@"Frame #%d", frameCount); - return; - }else{ - NSLog(@"Frame appendPixelBuffer failed."); - return; - } - } -#pragma clang diagnostic pop +#pragma clang diagnostic pop \ No newline at end of file diff --git a/modules/videoio/src/cap_dshow.cpp b/modules/videoio/src/cap_dshow.cpp index 5cb8232fa8..15eddfbe1e 100644 --- a/modules/videoio/src/cap_dshow.cpp +++ b/modules/videoio/src/cap_dshow.cpp @@ -2394,7 +2394,7 @@ int videoInput::getVideoPropertyFromCV(int cv_property){ case CAP_PROP_MONOCHROME: return VideoProcAmp_ColorEnable; - case CAP_PROP_WHITE_BALANCE_BLUE_U: + case cv::VideoCaptureProperties::CAP_PROP_WB_TEMPERATURE: return VideoProcAmp_WhiteBalance; case cv::VideoCaptureProperties::CAP_PROP_AUTO_WB: @@ -3422,7 +3422,7 @@ double VideoCapture_DShow::getProperty(int propIdx) const case CAP_PROP_SHARPNESS: case CAP_PROP_GAMMA: case CAP_PROP_MONOCHROME: - case CAP_PROP_WHITE_BALANCE_BLUE_U: + case cv::VideoCaptureProperties::CAP_PROP_WB_TEMPERATURE: case CAP_PROP_BACKLIGHT: case CAP_PROP_GAIN: if (g_VI.getVideoSettingFilter(m_index, g_VI.getVideoPropertyFromCV(propIdx), min_value, max_value, stepping_delta, current_value, flags, defaultValue)) @@ -3589,7 +3589,7 @@ bool VideoCapture_DShow::setProperty(int propIdx, double propVal) else flags = VideoProcAmp_Flags_Manual; break; - case CAP_PROP_WHITE_BALANCE_BLUE_U: + case cv::VideoCaptureProperties::CAP_PROP_WB_TEMPERATURE: flags = VideoProcAmp_Flags_Manual; break; } @@ -3604,7 +3604,7 @@ bool VideoCapture_DShow::setProperty(int propIdx, double propVal) case CAP_PROP_SHARPNESS: case CAP_PROP_GAMMA: case CAP_PROP_MONOCHROME: - case CAP_PROP_WHITE_BALANCE_BLUE_U: + case cv::VideoCaptureProperties::CAP_PROP_WB_TEMPERATURE: case cv::VideoCaptureProperties::CAP_PROP_AUTO_WB: case CAP_PROP_BACKLIGHT: case CAP_PROP_GAIN: diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 69a355fb4a..afd8fd4098 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -524,6 +524,17 @@ inline static std::string _opencv_ffmpeg_get_error_string(int error_code) return std::string("Unknown error"); } +static inline int64_t to_avtb(int64_t ts, AVRational tb) +{ + return av_rescale_q(ts, tb, AV_TIME_BASE_Q); +} + +static inline int64_t from_avtb(int64_t ts_avtb, AVRational tb) +{ + return av_rescale_q(ts_avtb, AV_TIME_BASE_Q, tb); +} + + struct CvCapture_FFMPEG { bool open(const char* filename, int index, const Ptr& stream, const VideoCaptureParameters& params); @@ -563,6 +574,10 @@ struct CvCapture_FFMPEG int64_t pts_in_fps_time_base; int64_t dts_delay_in_fps_time_base; + /// Timestamp offset in AV_TIME_BASE units for normalization + int64_t ts_offset_avtb = 0; + bool ts_offset_decided = false; + AVIOContext * avio_context; AVPacket packet; @@ -623,6 +638,8 @@ void CvCapture_FFMPEG::init() picture_pts = AV_NOPTS_VALUE_; pts_in_fps_time_base = 0; dts_delay_in_fps_time_base = 0; + ts_offset_avtb = 0; + ts_offset_decided = false; first_frame_number = -1; memset( &rgb_picture, 0, sizeof(rgb_picture) ); memset( &frame, 0, sizeof(frame) ); @@ -1705,15 +1722,74 @@ bool CvCapture_FFMPEG::grabFrame() if (picture_pts == AV_NOPTS_VALUE_) { int64_t dts = 0; if (!rawMode) { - picture_pts = picture->CV_FFMPEG_PTS_FIELD != AV_NOPTS_VALUE_ && picture->CV_FFMPEG_PTS_FIELD != 0 ? picture->CV_FFMPEG_PTS_FIELD : picture->pkt_dts; - if(frame_number == 0) dts = picture->pkt_dts; - } - else { - const AVPacket& packet_raw = packet.data != 0 ? packet : packet_filtered; - picture_pts = packet_raw.pts != AV_NOPTS_VALUE_ && packet_raw.pts != 0 ? packet_raw.pts : packet_raw.dts; + picture_pts = (picture->CV_FFMPEG_PTS_FIELD != AV_NOPTS_VALUE_) + ? picture->CV_FFMPEG_PTS_FIELD + : picture->pkt_dts; + if (frame_number == 0) dts = picture->pkt_dts; + } else { + const AVPacket& packet_raw = (packet.data != 0) ? packet : packet_filtered; + picture_pts = (packet_raw.pts != AV_NOPTS_VALUE_) + ? packet_raw.pts + : packet_raw.dts; if (frame_number == 0) dts = packet_raw.dts; - if (picture_pts < 0) picture_pts = 0; } + + // Decide timestamp offset once on first frame to normalize all timestamps to start at zero. + // This handles videos with negative DTS values (e.g., from B-frame reordering) or non-zero + // start_time. Similar to FFmpeg's -avoid_negative_ts make_zero option. + if (!ts_offset_decided) + { + int64_t min_start_avtb = INT64_MAX; + + // Check container start_time (already in AV_TIME_BASE units) + if (ic && ic->start_time != AV_NOPTS_VALUE_) + { + min_start_avtb = ic->start_time; + } + + // Check stream start_time + AVStream* st = ic->streams[video_stream]; + if (st->start_time != AV_NOPTS_VALUE_) + { + int64_t s = to_avtb(st->start_time, st->time_base); + if (s < min_start_avtb) min_start_avtb = s; + } + + // Check first observed timestamp (PTS preferred, else DTS from frame 0) + int64_t first_ts_stream = picture_pts; + if (first_ts_stream == AV_NOPTS_VALUE_ && dts != AV_NOPTS_VALUE_) + { + first_ts_stream = dts; + } + if (first_ts_stream != AV_NOPTS_VALUE_) + { + int64_t t = to_avtb(first_ts_stream, st->time_base); + if (t < min_start_avtb) min_start_avtb = t; + } + + // Compute offset to shift negative timestamps to zero + ts_offset_avtb = (min_start_avtb != INT64_MAX && min_start_avtb < 0) ? -min_start_avtb : 0; + ts_offset_decided = true; + } + + // Apply normalization to picture_pts + if (picture_pts != AV_NOPTS_VALUE_) + { + int64_t t = to_avtb(picture_pts, video_st->time_base); + t += ts_offset_avtb; + picture_pts = from_avtb(t, video_st->time_base); + } + + // Also normalize dts + if (dts != AV_NOPTS_VALUE_) + { + int64_t t = to_avtb(dts, video_st->time_base); + t += ts_offset_avtb; + dts = from_avtb(t, video_st->time_base); + } + + + #if LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(54, 1, 0) || LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) AVRational frame_rate = video_st->avg_frame_rate; #else @@ -1769,16 +1845,28 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step, // if hardware frame, copy it to system memory if (picture && picture->hw_frames_ctx) { sw_picture = av_frame_alloc(); + if (!sw_picture) { + CV_LOG_ERROR(NULL, "av_frame_alloc failed"); + return false; + } //if (av_hwframe_map(sw_picture, picture, AV_HWFRAME_MAP_READ) < 0) { if (av_hwframe_transfer_data(sw_picture, picture, 0) < 0) { CV_LOG_ERROR(NULL, "Error copying data from GPU to CPU (av_hwframe_transfer_data)"); + av_frame_free(&sw_picture); return false; } } #endif if (!sw_picture || !sw_picture->data[0]) + { +#if USE_AV_HW_CODECS + if (sw_picture != picture) + av_frame_free(&sw_picture); +#endif + CV_LOG_ERROR(NULL, "Picture does not contain data"); return false; + } #if LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(56, 72, 0) const char* color_space_name = av_color_space_name(sw_picture->colorspace); @@ -1829,7 +1917,14 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step, img_convert_ctx = sws_alloc_context(); if (img_convert_ctx == NULL) - return false;//CV_Error(0, "Cannot initialize the conversion context!"); + { + CV_LOG_ERROR(NULL, "Cannot initialize the conversion context!"); +#if USE_AV_HW_CODECS + if (sw_picture != picture) + av_frame_free(&sw_picture); +#endif + return false; + } av_opt_set_int(img_convert_ctx, "sws_flags", SWS_BICUBIC, 0); av_opt_set_int(img_convert_ctx, "threads", requestedThreads, 0); @@ -1870,8 +1965,14 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step, ); #endif - if (img_convert_ctx == NULL) - return false;//CV_Error(0, "Cannot initialize the conversion context!"); + if (img_convert_ctx == NULL) { + CV_LOG_ERROR(NULL, "Cannot initialize the conversion context!"); +#if USE_AV_HW_CODECS + if (sw_picture != picture) + av_frame_free(&sw_picture); +#endif + return false; + } #if USE_AV_FRAME_GET_BUFFER av_frame_unref(&rgb_picture); @@ -1880,7 +1981,11 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step, rgb_picture.height = buffer_height; if (0 != av_frame_get_buffer(&rgb_picture, 32)) { - CV_WARN("OutOfMemory"); + CV_LOG_ERROR(NULL, "Out of memory issue on av_frame_get_buffer!"); +#if USE_AV_HW_CODECS + if (sw_picture != picture) + av_frame_free(&sw_picture); +#endif return false; } #else @@ -2120,8 +2225,13 @@ int64_t CvCapture_FFMPEG::dts_to_frame_number(int64_t dts) double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const { - return (double)(dts - ic->streams[video_stream]->start_time) * - r2d(ic->streams[video_stream]->time_base); + const AVStream* st = ic->streams[video_stream]; + int64_t ts = dts; + + if (ts_offset_avtb == 0 && st->start_time != AV_NOPTS_VALUE_) + ts -= st->start_time; + + return ts * r2d(st->time_base); } void CvCapture_FFMPEG::get_rotation_angle() @@ -2174,9 +2284,19 @@ void CvCapture_FFMPEG::seek(int64_t _frame_number) { int64_t _frame_number_temp = std::max(_frame_number-delta, (int64_t)0); double sec = (double)_frame_number_temp / get_fps(); - int64_t time_stamp = ic->streams[video_stream]->start_time; - double time_base = r2d(ic->streams[video_stream]->time_base); - time_stamp += (int64_t)(sec / time_base + 0.5); + + AVStream* st = ic->streams[video_stream]; + int64_t time_stamp = st->start_time; + double time_base = r2d(st->time_base); + int64_t ts_norm = (int64_t)(sec / time_base + 0.5); + + if (ts_offset_avtb != 0) { + // map normalized target back to original demux timeline + time_stamp += ts_norm - from_avtb(ts_offset_avtb, st->time_base); + } else { + time_stamp += ts_norm; + } + if (get_total_frames() > 1) av_seek_frame(ic, video_stream, time_stamp, AVSEEK_FLAG_BACKWARD); if(!rawMode) avcodec_flush_buffers(context); diff --git a/modules/videoio/src/cap_v4l.cpp b/modules/videoio/src/cap_v4l.cpp index 9450f85f5a..30a86f672e 100644 --- a/modules/videoio/src/cap_v4l.cpp +++ b/modules/videoio/src/cap_v4l.cpp @@ -1103,7 +1103,7 @@ bool CvCaptureCAM_V4L::grabFrame() FirstCapture = false; #if defined(V4L_ABORT_BADJPEG) - // skip first frame. it is often bad -- this is unnotied in traditional apps, + // skip first frame. it is often bad -- this is unnoticed in traditional apps, // but could be fatal if bad jpeg is enabled if (!read_frame_v4l2()) return false; diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 3021bdfa4e..65db9e6086 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -1055,6 +1055,61 @@ TEST(ffmpeg_cap_properties, set_pos_get_msec) EXPECT_EQ(cap.get(CAP_PROP_POS_MSEC), 0.0); } -#endif // WIN32 +// Test that seeking twice to the same frame in videos with negative DTS +// does not result in negative position or timestamp values +// related issue: https://github.com/opencv/opencv/issues/27819 +TEST(videoio_ffmpeg, seek_with_negative_dts) +{ + if (!videoio_registry::hasBackend(CAP_FFMPEG)) + throw SkipTestException("FFmpeg backend was not found"); + + const std::string filename = findDataFile("video/negdts_h264.mp4"); + VideoCapture cap(filename, CAP_FFMPEG); + + if (!cap.isOpened()) + throw SkipTestException("Video stream is not supported"); + + // after open, a single grab() should not yield negative POS_MSEC. + ASSERT_TRUE(cap.grab()); + EXPECT_GE(cap.get(CAP_PROP_POS_MSEC), 0.0) << "Negative ts immediately after open+grab()"; + + ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, 0)); + (void)cap.get(CAP_PROP_POS_FRAMES); + + const int framesToProbe[] = {2, 3, 4, 5}; + + for (int f : framesToProbe) + { + // Reset to frame 0 + ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, 0)); + cap.get(CAP_PROP_POS_FRAMES); + + // Seek to target frame + ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, f)); + const double posAfterFirstSeek = cap.get(CAP_PROP_POS_FRAMES); + + // Seek to the same frame again + ASSERT_TRUE(cap.set(CAP_PROP_POS_FRAMES, f)); + const double posAfterSecondSeek = cap.get(CAP_PROP_POS_FRAMES); + const double tsAfterSecondSeek = cap.get(CAP_PROP_POS_MSEC); + + EXPECT_GE(posAfterSecondSeek, 0) + << "Frame index became negative after second seek to frame " << f + << " (first seek gave " << posAfterFirstSeek << ")"; + EXPECT_GE(tsAfterSecondSeek, 0.0) + << "Timestamp became negative after second seek to frame " << f; + + // Per-iteration decode check: grab() + ts non-negative + ASSERT_TRUE(cap.grab()); + EXPECT_GE(cap.get(CAP_PROP_POS_MSEC), 0.0) << "Negative timestamp after grab() at frame " << f; + + // Verify that reading a frame works and position advances + Mat frame; + ASSERT_TRUE(cap.read(frame)); + ASSERT_FALSE(frame.empty()); + EXPECT_GE(cap.get(CAP_PROP_POS_FRAMES), f); + } +} +#endif // _WIN32 }} // namespace diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index 6aefd886bc..b66f479d43 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -785,7 +785,7 @@ TEST_P(videocapture_acceleration, read) { if (filename == "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4") throw SkipTestException("Unable to read the first frame with VP9 codec (media stack misconfiguration / bug)"); - // FFMPEG: [av1 @ 0000027ac07d1340] Your platform doesn't suppport hardware accelerated AV1 decoding. + // FFMPEG: [av1 @ 0000027ac07d1340] Your platform doesn't support hardware accelerated AV1 decoding. if (filename == "sample_322x242_15frames.yuv420p.libaom-av1.mp4") throw SkipTestException("Unable to read the first frame with AV1 codec (missing support)"); } @@ -807,7 +807,7 @@ TEST_P(videocapture_acceleration, read) { if (filename == "sample_322x242_15frames.yuv420p.libvpx-vp9.mp4") throw SkipTestException("Unable to read the first frame with VP9 codec (media stack misconfiguration / bug)"); - // FFMPEG: [av1 @ 0000027ac07d1340] Your platform doesn't suppport hardware accelerated AV1 decoding. + // FFMPEG: [av1 @ 0000027ac07d1340] Your platform doesn't support hardware accelerated AV1 decoding. if (filename == "sample_322x242_15frames.yuv420p.libaom-av1.mp4") throw SkipTestException("Unable to read the first frame with AV1 codec (missing support)"); } diff --git a/modules/world/CMakeLists.txt b/modules/world/CMakeLists.txt index b6a0fedde1..8c580ed1b2 100644 --- a/modules/world/CMakeLists.txt +++ b/modules/world/CMakeLists.txt @@ -83,6 +83,11 @@ endif() ocv_target_compile_definitions(${the_module} PRIVATE OPENCV_MODULE_IS_PART_OF_WORLD=1) +# NOTE: https://github.com/opencv/opencv/issues/25543 +if (WITH_QT) + qt_disable_unicode_defines(${the_module}) +endif() + if(BUILD_opencv_imgcodecs AND OPENCV_MODULE_opencv_imgcodecs_IS_PART_OF_WORLD) ocv_imgcodecs_configure_target() endif() diff --git a/platforms/linux/flags-aarch64.cmake b/platforms/linux/flags-aarch64.cmake index 5aeb7a2b6a..008df55ba2 100644 --- a/platforms/linux/flags-aarch64.cmake +++ b/platforms/linux/flags-aarch64.cmake @@ -1,6 +1,9 @@ # see https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-march function(ocv_set_platform_flags VAR) unset(flags) + if(ENABLE_SVE) + set(flags "${flags}+sve") + endif() if(ENABLE_BF16) set(flags "${flags}+bf16") endif() diff --git a/samples/cpp/gauge.cpp b/samples/cpp/gauge.cpp new file mode 100644 index 0000000000..e3a7dcfe92 --- /dev/null +++ b/samples/cpp/gauge.cpp @@ -0,0 +1,300 @@ +/** + @file gauge.cpp + @author Alessandro de Oliveira Faria (A.K.A. CABELO) + @brief This sample application processes an image frame of an analog gauge + and extracts its reading using functions from the OpenCV* computer vision + library. The workflow is divided into two stages: calibration and + measurement. Questions and suggestions email to: + Alessandro de Oliveira Faria cabelo[at]opensuse[dot]org or OpenCV Team. + @date Nov 26, 2025 +*/ + +#include "opencv2/imgproc.hpp" +#include +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +struct GaugeCalibration { + double min_angle; + double max_angle; + double min_value; + double max_value; + string units; + int x; + int y; + int r; +}; + +static Vec3f avg_circles(const vector &circles) { + double avg_x = 0.0; + double avg_y = 0.0; + double avg_r = 0.0; + + int b = static_cast(circles.size()); + for (int i = 0; i < b; ++i) { + avg_x += circles[i][0]; + avg_y += circles[i][1]; + avg_r += circles[i][2]; + } + + avg_x /= b; + avg_y /= b; + avg_r /= b; + + return Vec3f(static_cast(avg_x), + static_cast(avg_y), + static_cast(avg_r)); +} + +static double dist_2_pts(double x1, double y1, double x2, double y2) { + double dx = x2 - x1; + double dy = y2 - y1; + return std::sqrt(dx * dx + dy * dy); +} + +static GaugeCalibration calibrate_gauge(string filename) { + GaugeCalibration cal; + Mat img = imread(filename); + if (img.empty()) { + cerr << "Error loading image: " << filename << endl; + exit(1); + } + + int height = img.rows; + + Mat gray; + cvtColor(img, gray, COLOR_BGR2GRAY); + + // Detects circles (HoughCircles) + vector circles; + HoughCircles(gray, circles, HOUGH_GRADIENT, 1, + 20, + 100, 50, + static_cast(height * 0.35), + static_cast(height * 0.48)); + + if (circles.empty()) { + cerr << "No circles found." << endl; + exit(1); + } + + Vec3f avg = avg_circles(circles); + int x = static_cast(avg[0]); + int y = static_cast(avg[1]); + int r = static_cast(avg[2]); + + cal.x = x; + cal.y = y; + cal.r = r; + + // Draw the circle and the center. + circle(img, Point(x, y), r, Scalar(0, 0, 255), 3, LINE_AA); + circle(img, Point(x, y), 2, Scalar(0, 255, 0), 3, LINE_AA); + + // Generation of calibration lines. + double separation = 10.0; // in degrees + int interval = static_cast(360.0 / separation); + + vector p1(interval); + vector p2(interval); + vector p_text(interval); + + for (int i = 0; i < interval; ++i) { + double angle_rad = separation * i * CV_PI / 180.0; + p1[i].x = x + 0.9 * r * std::cos(angle_rad); + p1[i].y = y + 0.9 * r * std::sin(angle_rad); + } + + int text_offset_x = 10; + int text_offset_y = 5; + + for (int i = 0; i < interval; ++i) { + double angle_rad = separation * i * CV_PI / 180.0; + p2[i].x = x + r * std::cos(angle_rad); + p2[i].y = y + r * std::sin(angle_rad); + + double text_angle_rad = separation * (i + 9) * CV_PI / 180.0; // i+9 = rotate 90° + p_text[i].x = x - text_offset_x + 1.2 * r * std::cos(text_angle_rad); + p_text[i].y = y + text_offset_y + 1.2 * r * std::sin(text_angle_rad); + } + + // Draws lines and text. + for (int i = 0; i < interval; ++i) { + line(img, + Point(static_cast(p1[i].x), static_cast(p1[i].y)), + Point(static_cast(p2[i].x), static_cast(p2[i].y)), + Scalar(0, 255, 0), 2); + + putText(img, + to_string(static_cast(i * separation)), + Point(static_cast(p_text[i].x), static_cast(p_text[i].y)), + FONT_HERSHEY_SIMPLEX, 0.3, Scalar(0, 0, 0), 1, LINE_AA); + } + + // Save calibration image + imwrite("gauge-calibration.jpg", img); + + cout << "Min angle (lowest possible angle of dial) - in degrees: "; + cin >> cal.min_angle; + + cout << "Max angle (highest possible angle) - in degrees: "; + cin >> cal.max_angle; + + cout << "Min value: "; + cin >> cal.min_value; + + cout << "Max value: "; + cin >> cal.max_value; + + cout << "Enter units: "; + cin >> cal.units; + + return cal; +} + +static double get_current_value(Mat img, + const GaugeCalibration &cal) { + Mat gray2; + cvtColor(img, gray2, COLOR_BGR2GRAY); + + int thresh = 175; + int maxValue = 255; + + Mat dst2; + threshold(gray2, dst2, thresh, maxValue, THRESH_BINARY_INV); + + // For debugging: threshold image + //imwrite("gauge-tempdst2.jpg", dst2); + + // Detect lines + vector lines; + int minLineLength = 10; + int maxLineGap = 0; + HoughLinesP(dst2, lines, 3, CV_PI / 180, 100, minLineLength, maxLineGap); + + if (lines.empty()) { + cerr << "No rows found." << endl; + return 0.0; + } + + // Filter lines by distance from center + vector final_line_list; + + double diff1LowerBound = 0.15; + double diff1UpperBound = 0.25; + double diff2LowerBound = 0.5; + double diff2UpperBound = 1.0; + + int x = cal.x; + int y = cal.y; + int r = cal.r; + + for (size_t i = 0; i < lines.size(); ++i) { + int x1 = lines[i][0]; + int y1 = lines[i][1]; + int x2 = lines[i][2]; + int y2 = lines[i][3]; + + double diff1 = dist_2_pts(x, y, x1, y1); + double diff2 = dist_2_pts(x, y, x2, y2); + + // ensures that diff1 is the smallest (closest to the center) + if (diff1 > diff2) { + std::swap(diff1, diff2); + } + + if ((diff1 < diff1UpperBound * r) && (diff1 > diff1LowerBound * r) && + (diff2 < diff2UpperBound * r) && (diff2 > diff2LowerBound * r)) { + final_line_list.push_back(lines[i]); + } + } + + if (final_line_list.empty()) { + cerr << "No lines within the expected radius." << endl; + return 0.0; + } + + // Use the first filtered line. + int x1 = final_line_list[0][0]; + int y1 = final_line_list[0][1]; + int x2 = final_line_list[0][2]; + int y2 = final_line_list[0][3]; + + // Draw the line on the original image for debugging. + line(img, Point(x1, y1), Point(x2, y2), Scalar(0, 255, 0), 2); + //imwrite("gauge-lines-2.jpg", img); + + // Decide which point is furthest from the center. + double dist_pt_0 = dist_2_pts(x, y, x1, y1); + double dist_pt_1 = dist_2_pts(x, y, x2, y2); + + double x_angle, y_angle; + if (dist_pt_0 > dist_pt_1) { + x_angle = x1 - x; + y_angle = y - y1; + } else { + x_angle = x2 - x; + y_angle = y - y2; + } + + double res = 0; + // atan(y/x) in radians + if(x_angle != 0) + { + res = std::atan(y_angle / x_angle); + res = res * 180.0 / CV_PI; // rad2deg + } + double final_angle = 0.0; + + if (x_angle > 0 && y_angle > 0) { // Quadrante I + final_angle = 270.0 - res; + } + if (x_angle < 0 && y_angle > 0) { // Quadrante II + final_angle = 90.0 - res; + } + if (x_angle < 0 && y_angle < 0) { // Quadrante III + final_angle = 90.0 - res; + } + if (x_angle > 0 && y_angle < 0) { // Quadrante IV + final_angle = 270.0 - res; + } + + double old_min = cal.min_angle; + double old_max = cal.max_angle; + double new_min = cal.min_value; + double new_max = cal.max_value; + + double old_value = final_angle; + double old_range = (old_max - old_min); + double new_range = (new_max - new_min); + + double new_value = (((old_value - old_min) * new_range) / old_range) + new_min; + + return new_value; +} + +int main(int argc, char *argv[]) { + CommandLineParser parser(argc, argv, "{@input |gauge-1.jpg|Input image to reads the value using functions from the OpenCV. }"); + parser.about("Analog-gauge-reader example:\n"); + parser.printMessage(); + string filename = parser.get("@input"); + + Mat img = imread(filename); + if (img.empty()) { + cerr << "Error loading image. " << filename << endl; + return 1; + } + + // Calibration + GaugeCalibration cal = calibrate_gauge(filename); + + double val = get_current_value(img, cal ); + cout << "Current reading: " << val << " " << cal.units << endl; + + return 0; +} diff --git a/samples/data/gauge-1.jpg b/samples/data/gauge-1.jpg new file mode 100644 index 0000000000..ea0573806d Binary files /dev/null and b/samples/data/gauge-1.jpg differ diff --git a/samples/dnn/js_face_recognition.html b/samples/dnn/js_face_recognition.html index aaee89327f..3b4ed390d7 100644 --- a/samples/dnn/js_face_recognition.html +++ b/samples/dnn/js_face_recognition.html @@ -132,7 +132,7 @@ function main() { var cell = document.getElementById("targetNames").insertCell(0); cell.innerHTML = name; - persons[name] = face2vec(face).mat_clone(); + persons[name] = face2vec(face).clone(); var canvas = document.createElement("canvas"); canvas.setAttribute("width", 112); diff --git a/samples/python/_coverage.py b/samples/python/_coverage.py index 62dd7aa5d8..634c6b8795 100755 --- a/samples/python/_coverage.py +++ b/samples/python/_coverage.py @@ -18,7 +18,7 @@ if __name__ == '__main__': for fn in glob('*.py'): print(' --- ', fn) code = open(fn).read() - found |= set(re.findall('cv2?\.\w+', code)) + found |= set(re.findall(r'cv2?\.\w+', code)) cv2_used = found & cv2_callable cv2_unused = cv2_callable - cv2_used diff --git a/samples/python/audio_spectrogram.py b/samples/python/audio_spectrogram.py index f211bbb584..cc156888d4 100644 --- a/samples/python/audio_spectrogram.py +++ b/samples/python/audio_spectrogram.py @@ -330,6 +330,10 @@ class AudioDrawing: """ time_step = self.windLen - self.overlap + if time_step <= 0: + raise ValueError( + "Invalid STFT parameters: overlap must be smaller than window length" + ) stft = [] if self.windowType == "Hann": @@ -801,4 +805,4 @@ if __name__ == "__main__": args = parser.parse_args() - AudioDrawing(args).Draw() + AudioDrawing(args).Draw() \ No newline at end of file diff --git a/samples/python/demo.py b/samples/python/demo.py index e90fb6495a..de36b91499 100755 --- a/samples/python/demo.py +++ b/samples/python/demo.py @@ -105,7 +105,11 @@ class App: webbrowser.open(url) def on_demo_select(self, evt): - name = self.demos_lb.get( self.demos_lb.curselection()[0] ) + selection = self.demos_lb.curselection() + if not selection: + return + + name = self.demos_lb.get(selection[0]) fn = self.samples[name] descr = "" diff --git a/samples/python/stitching_detailed.py b/samples/python/stitching_detailed.py index 4631420248..a595439c80 100644 --- a/samples/python/stitching_detailed.py +++ b/samples/python/stitching_detailed.py @@ -97,10 +97,8 @@ parser.add_argument( ) parser.add_argument( '--try_cuda', - action='store', - default=False, + action='store_true', help="Try to use CUDA. The default value is no. All default values are for CPU mode.", - type=bool, dest='try_cuda' ) parser.add_argument( '--work_megapix', action='store', default=0.6,