From a27a13921b6a47780bde064e931b38d969fc18de Mon Sep 17 00:00:00 2001 From: Ghazi-raad Date: Wed, 26 Nov 2025 21:41:28 +0000 Subject: [PATCH 001/103] Fix LINE_4/LINE_8 swap in drawContours (issue #26413) Changed condition to correctly route LINE_8 to Line() instead of Line2(). The condition 'line_type == 1 || line_type == 4' was causing LINE_8 (value 8) to be processed by Line2() which implements 4-connectivity, and LINE_4 (value 4) to be processed by Line() which was implementing 8-connectivity behavior. This resulted in swapped line connectivity. Changed to 'line_type == 1 || line_type == 8' so LINE_8 goes to Line() with 8-connectivity and LINE_4 goes to Line2() with 4-connectivity, matching the documented behavior where LINE_4 should produce 4-connected lines and LINE_8 should produce 8-connected lines. Fixes #26413 --- modules/imgproc/src/drawing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/src/drawing.cpp b/modules/imgproc/src/drawing.cpp index 8214b17013..fcac9ff303 100644 --- a/modules/imgproc/src/drawing.cpp +++ b/modules/imgproc/src/drawing.cpp @@ -1667,7 +1667,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; From e47916120fa2c7b62e18d00c7f77fe616aa0cf71 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 1 Dec 2025 13:47:27 +0300 Subject: [PATCH 002/103] Updated libpng to v1.6.51 and added RISC-V optimimizations. --- 3rdparty/libpng/CMakeLists.txt | 44 +- 3rdparty/libpng/arm/arm_init.c | 8 +- 3rdparty/libpng/mips/mips_init.c | 6 +- 3rdparty/libpng/png.c | 1284 +++--------- 3rdparty/libpng/png.h | 176 +- 3rdparty/libpng/pngconf.h | 47 +- 3rdparty/libpng/pngdebug.h | 11 +- 3rdparty/libpng/pngerror.c | 166 +- 3rdparty/libpng/pngget.c | 289 ++- 3rdparty/libpng/pnginfo.h | 99 +- 3rdparty/libpng/pngpread.c | 186 +- 3rdparty/libpng/pngpriv.h | 512 ++--- 3rdparty/libpng/pngread.c | 446 ++-- 3rdparty/libpng/pngrio.c | 4 +- 3rdparty/libpng/pngrtran.c | 453 ++-- 3rdparty/libpng/pngrutil.c | 1864 ++++++++--------- 3rdparty/libpng/pngset.c | 263 ++- 3rdparty/libpng/pngstruct.h | 112 +- 3rdparty/libpng/pngwio.c | 8 +- 3rdparty/libpng/pngwrite.c | 67 +- 3rdparty/libpng/pngwutil.c | 54 +- .../libpng/powerpc/filter_vsx_intrinsics.c | 2 +- 3rdparty/libpng/powerpc/powerpc_init.c | 2 +- 3rdparty/libpng/riscv/filter_rvv_intrinsics.c | 350 ++++ 3rdparty/libpng/riscv/riscv_init.c | 45 + 25 files changed, 3243 insertions(+), 3255 deletions(-) create mode 100644 3rdparty/libpng/riscv/filter_rvv_intrinsics.c create mode 100644 3rdparty/libpng/riscv/riscv_init.c diff --git a/3rdparty/libpng/CMakeLists.txt b/3rdparty/libpng/CMakeLists.txt index 4749c67b18..8e4e585589 100644 --- a/3rdparty/libpng/CMakeLists.txt +++ b/3rdparty/libpng/CMakeLists.txt @@ -172,7 +172,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 +182,43 @@ if(TARGET_ARCH MATCHES "^(loongarch)") endif() endif() +# Set definitions and sources for RISC-V. +if(PNG_TARGET_ARCHITECTURE MATCHES "^(riscv)") + include(CheckCCompilerFlag) + set(PNG_RISCV_RVV_POSSIBLE_VALUES on off) + set(PNG_RISCV_RVV "off" + 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 +246,11 @@ if(TARGET_ARCH MATCHES "^(loongarch)") add_definitions(-DPNG_LOONGARCH_LSX_OPT=0) endif() +# Set definitions and sources for RISC-V. +if(PNG_TARGET_ARCHITECTURE MATCHES "^(riscv)") + add_definitions(-DPNG_RISCV_RVV_OPT=0) +endif() + endif(PNG_HARDWARE_OPTIMIZATIONS) # ---------------------------------------------------------------------------------- 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/png.c b/3rdparty/libpng/png.c index 466af7d992..380c4c19e6 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_51 Your_png_h_is_not_version_1_6_51; + +/* 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.51" 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..fb93d2242b 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.51 * * 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.51, November 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.51 16 10651 16.so.16.51[.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.51" #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 51 /* 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 10651 /* 1.6.51 */ /* 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_51; /* 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..981df68d87 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.51 * * 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/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..79917daaaf 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) @@ -3693,6 +3595,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 +3647,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 +3708,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 +3729,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 +4014,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..2f52022551 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 @@ -1736,8 +1860,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 +2111,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 +5048,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..a71e561bba --- /dev/null +++ b/3rdparty/libpng/riscv/filter_rvv_intrinsics.c @@ -0,0 +1,350 @@ +/* filter_rvv_intrinsics.c - RISC-V Vector optimized filter functions + * + * Copyright (c) 2023 Google LLC + * Written by Manfred SCHLAEGL, 2022 + * 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 /* 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); + + /* tmp = a + b */ + vuint16m2_t tmp = __riscv_vwaddu_vv_u16m2(a, b, vl); + + /* a = tmp/2 */ + a = __riscv_vnsrl_wx_u8m1(tmp, 1, 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) +} + +#define MIN_CHUNK_LEN 256 +#define MAX_CHUNK_LEN 2048 + +static inline vuint8m1_t +prefix_sum(vuint8m1_t chunk, unsigned char* carry, size_t vl, + size_t max_chunk_len) +{ + size_t r; + + for (r = 1; r < MIN_CHUNK_LEN; r <<= 1) + { + vbool8_t shift_mask = __riscv_vmsgeu_vx_u8m1_b8(__riscv_vid_v_u8m1(vl), r, vl); + chunk = __riscv_vadd_vv_u8m1_mu(shift_mask, chunk, chunk, __riscv_vslideup_vx_u8m1(__riscv_vundefined_u8m1(), chunk, r, vl), vl); + } + + for (r = MIN_CHUNK_LEN; r < MAX_CHUNK_LEN && r < max_chunk_len; r <<= 1) + { + vbool8_t shift_mask = __riscv_vmsgeu_vx_u8m1_b8(__riscv_vid_v_u8m1(vl), r, vl); + chunk = __riscv_vadd_vv_u8m1_mu(shift_mask, chunk, chunk, __riscv_vslideup_vx_u8m1(__riscv_vundefined_u8m1(), chunk, r, vl), vl); + } + + chunk = __riscv_vadd_vx_u8m1(chunk, *carry, vl); + *carry = __riscv_vmv_x_s_u8m1_u8(__riscv_vslidedown_vx_u8m1(chunk, vl - 1, vl)); + + return chunk; +} + +static inline vint16m1_t +abs_diff(vuint16m1_t a, vuint16m1_t b, size_t vl) +{ + vint16m1_t diff = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vsub_vv_u16m1(a, b, vl)); + vbool16_t mask = __riscv_vmslt_vx_i16m1_b16(diff, 0, vl); + return __riscv_vrsub_vx_i16m1_m(mask, diff, 0, vl); +} + +static inline vint16m1_t +abs_sum(vint16m1_t a, vint16m1_t b, size_t vl) +{ + return __riscv_vadd_vv_i16m1(a, b, vl); +} + +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); + + /* Calculate p = b - c and pc = a - c using widening subtraction */ + vuint16m2_t p_wide = __riscv_vwsubu_vv_u16m2(b, c, vl); + vuint16m2_t pc_wide = __riscv_vwsubu_vv_u16m2(a, c, vl); + + /* Convert to signed for easier manipulation */ + size_t vl16 = __riscv_vsetvl_e16m2(bpp); + vint16m2_t p = __riscv_vreinterpret_v_u16m2_i16m2(p_wide); + vint16m2_t pc = __riscv_vreinterpret_v_u16m2_i16m2(pc_wide); + + /* pa = |p| */ + vbool8_t p_neg_mask = __riscv_vmslt_vx_i16m2_b8(p, 0, vl16); + vint16m2_t pa = __riscv_vrsub_vx_i16m2_m(p_neg_mask, p, 0, vl16); + + /* pb = |pc| */ + vbool8_t pc_neg_mask = __riscv_vmslt_vx_i16m2_b8(pc, 0, vl16); + vint16m2_t pb = __riscv_vrsub_vx_i16m2_m(pc_neg_mask, pc, 0, vl16); + + /* pc = |p + pc| */ + vint16m2_t p_plus_pc = __riscv_vadd_vv_i16m2(p, pc, vl16); + vbool8_t p_plus_pc_neg_mask = __riscv_vmslt_vx_i16m2_b8(p_plus_pc, 0, vl16); + pc = __riscv_vrsub_vx_i16m2_m(p_plus_pc_neg_mask, p_plus_pc, 0, vl16); + + /* + * 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 + */ + + /* Find which predictor to use based on minimum absolute difference */ + vbool8_t pa_le_pb = __riscv_vmsle_vv_i16m2_b8(pa, pb, vl16); + vbool8_t pa_le_pc = __riscv_vmsle_vv_i16m2_b8(pa, pc, vl16); + vbool8_t pb_le_pc = __riscv_vmsle_vv_i16m2_b8(pb, pc, vl16); + + /* use_a = pa <= pb && pa <= pc */ + vbool8_t use_a = __riscv_vmand_mm_b8(pa_le_pb, pa_le_pc, vl16); + + /* use_b = !use_a && pb <= pc */ + vbool8_t not_use_a = __riscv_vmnot_m_b8(use_a, vl16); + vbool8_t use_b = __riscv_vmand_mm_b8(not_use_a, pb_le_pc, vl16); + + /* Switch back to e8m1 for final operations */ + vl = __riscv_vsetvl_e8m1(bpp); + + /* Start with a, then conditionally replace with b or c */ + vuint8m1_t result = a; + result = __riscv_vmerge_vvm_u8m1(result, b, use_b, vl); + + /* use_c = !use_a && !use_b */ + vbool8_t use_c = __riscv_vmnand_mm_b8(__riscv_vmor_mm_b8(use_a, use_b, vl), __riscv_vmor_mm_b8(use_a, use_b, vl), vl); + result = __riscv_vmerge_vvm_u8m1(result, c, use_c, vl); + + /* a = result + x */ + a = __riscv_vadd_vv_u8m1(result, 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 */ From 54d066b89c6d4aee1f57e56bc50738b58e904f9c Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 1 Dec 2025 17:14:08 +0300 Subject: [PATCH 003/103] Force 3rdparty build for CI test. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc93888c93..de41a85379 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,7 +196,7 @@ set(BUILD_LIST "" CACHE STRING "Build only listed modules (comma-separated, e.g. OCV_OPTION(OPENCV_ENABLE_NONFREE "Enable non-free algorithms" OFF) # 3rd party libs -OCV_OPTION(OPENCV_FORCE_3RDPARTY_BUILD "Force using 3rdparty code from source" OFF) +OCV_OPTION(OPENCV_FORCE_3RDPARTY_BUILD "Force using 3rdparty code from source" ON) OCV_OPTION(BUILD_ZLIB "Build zlib from source" (WIN32 OR APPLE OR OPENCV_FORCE_3RDPARTY_BUILD) ) OCV_OPTION(BUILD_TIFF "Build libtiff from source" (WIN32 OR ANDROID OR APPLE OR OPENCV_FORCE_3RDPARTY_BUILD) ) OCV_OPTION(BUILD_OPENJPEG "Build OpenJPEG from source" (WIN32 OR ANDROID OR APPLE OR OPENCV_FORCE_3RDPARTY_BUILD) ) From fc7e70ef4a5cec91a4029cc749d5ee390f5e5f22 Mon Sep 17 00:00:00 2001 From: Galina Bykova Date: Mon, 1 Dec 2025 21:22:11 +0330 Subject: [PATCH 004/103] fix bug in approxPolyDP: calculate distance to a segment, not to a line --- modules/imgproc/src/approx.cpp | 24 +++++++++++++++++++----- modules/imgproc/test/test_approxpoly.cpp | 13 +++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/modules/imgproc/src/approx.cpp b/modules/imgproc/src/approx.cpp index 6c89f1d879..433ca669d5 100644 --- a/modules/imgproc/src/approx.cpp +++ b/modules/imgproc/src/approx.cpp @@ -586,26 +586,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/test/test_approxpoly.cpp b/modules/imgproc/test/test_approxpoly.cpp index 2b07c1a7b5..347b03ae8a 100644 --- a/modules/imgproc/test/test_approxpoly.cpp +++ b/modules/imgproc/test/test_approxpoly.cpp @@ -377,6 +377,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() From 2c8cfdebded263cfc5af4e4f33086b00fb543d33 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 2 Dec 2025 10:40:01 +0300 Subject: [PATCH 005/103] Added RISC-V RVV diagnostics for PNG. --- 3rdparty/libpng/CMakeLists.txt | 2 +- CMakeLists.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/3rdparty/libpng/CMakeLists.txt b/3rdparty/libpng/CMakeLists.txt index 8e4e585589..00366036df 100644 --- a/3rdparty/libpng/CMakeLists.txt +++ b/3rdparty/libpng/CMakeLists.txt @@ -186,7 +186,7 @@ endif() if(PNG_TARGET_ARCHITECTURE MATCHES "^(riscv)") include(CheckCCompilerFlag) set(PNG_RISCV_RVV_POSSIBLE_VALUES on off) - set(PNG_RISCV_RVV "off" + set(PNG_RISCV_RVV "on" 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}) diff --git a/CMakeLists.txt b/CMakeLists.txt index de41a85379..f71fe9be11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1557,6 +1557,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() From 4c7fd071f01227351df4a7deb47694505814cd18 Mon Sep 17 00:00:00 2001 From: sinkboy-chen <129042729+sinkboy-chen@users.noreply.github.com> Date: Wed, 3 Dec 2025 13:51:59 +0800 Subject: [PATCH 006/103] Merge pull request #28118 from sinkboy-chen:bugfix/cuda-race-condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stitching: pass warp params by value to avoid CUDA constant races #28118 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under the Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on code under GPL or another license that is incompatible with OpenCV. - [x] The PR is proposed to the proper branch. - [x] There is a reference to the original bug report and related work. - [ ] There are accuracy tests, performance tests, and test data in the opencv_extra repository, if applicable. The patch to opencv_extra uses the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake configuration. Fixes #26870. In `modules/stitching/src/cuda/build_warp_maps.cu`, the original implementation copied parameters into global GPU constant symbols: ```cpp 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))); ``` As discussed in the issue, this can cause race conditions when multiple warps are built concurrently. This patch removes the use of these global constant symbols and instead passes the required data as kernel parameters (a total of 11 floats encapsulated in `WarpParams`). One potential concern is increased register pressure due to additional kernel arguments. However, based on experiments using the test case from issue #26870, there is no significant performance regression; in fact, a small speed‑up was observed. Testing was performed on an NVIDIA GeForce RTX 4090 (single GPU). Note: ./bin/opencv_perf_stitching did not run successfully on my system even with an unmodified git checkout, so performance was evaluated using the test case from issue #26870 instead. --- modules/stitching/src/cuda/build_warp_maps.cu | 90 ++++++++++++------- 1 file changed, 58 insertions(+), 32 deletions(-) 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()); From 9c3d3dbee7ffa4b68ca0dd4ec6ab246ac021637d Mon Sep 17 00:00:00 2001 From: "Alessandro de Oliveira Faria (A.K.A.CABELO)" Date: Wed, 3 Dec 2025 04:05:50 -0300 Subject: [PATCH 007/103] Merge pull request #28082 from cabelo:4.x Analog Gauge Reader - cabelo@opensuse.org #28082 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake I humbly offer this example so that all developers can understand that many problems can be solved without VLM models. image image image --- samples/cpp/gauge.cpp | 300 +++++++++++++++++++++++++++++++++++++++ samples/data/gauge-1.jpg | Bin 0 -> 17297 bytes 2 files changed, 300 insertions(+) create mode 100644 samples/cpp/gauge.cpp create mode 100644 samples/data/gauge-1.jpg 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 0000000000000000000000000000000000000000..ea0573806da00e1560e43dbc2722d42cda2b035b GIT binary patch literal 17297 zcmbSybx<79x8?xBEjR?%Ac5fSHn=lrATSWzEx5Z3Zo%DQa1X)V-GaLZ*X8%#)~o$< zch9Zvs;*Og`@3DYzN7bT@ofX}QBGP`8UXV?zP}H^+be)vK}t-LLci{3H;IS#$#1L>)jS#6EaM}Iia*?RTp?~nyrY>kWj2#1zQSb=}i9XTN zeWqt%{ky)oy}N&S#CnGe`{4uJ2gLtChJkf^zu>Swz*DjzV2i0D8ad!lvHK(8ipS+b z{~%Lys9oS0J5HhC({OImUj7I5e}VqL!yNGc1^Rz5{}1H1E&vu13@Qvd9LzgKg8iyPsoiSfk`7ARE0cne2{$cg3P?|Pl1^{J=f!+Q5AYM*9Ub1+a$Dqx$?I4yG8Y3ZoK@eETp-=Hd zvyG^ZGkM=ZNLmCXQFVNRAzr2UH)q!YbDl<&!w^@bDaqe3eS#PfQRVFn&0dAl1Qaxk zmN6|oRO3LBE6Z?GSH3bDTV=}};mE6St}XHcJ{koA{jNWU? zmF#YR*qg3$Dv3e#6QJb}0gsQh`^#xsjlGnjgF;6^D-?W(hcZ*aPw)SYgqwcQ+5`%7YiIuBt)X?Vq=mD-bNs)IVD>`Od@`!pNA^= zvye5Veg2_1r<1CdDY~1XOl3}E=pN1PMDBi+mLw=NP`8?M1xVoiHMOnd5)?PV_f0#O zunP}5?Cc?Q7^2MF%0=f-N;B;a-W77MIMF=$p+B}4mo?kSKr$NTGr8m72Pi|Cf z{5wv4SRhTeJTh{o>8vcuTbWDNF}PphLG@79md-?G1wsmMDdXpZi=^0`1Dx)9C9KAM zx}sWXAyG#v$IVWdkTcVl8J{!YpQNc8bi?T2+e^y_;gaw)a1b#g#6{%c2hp)(;^KKD zob;)ZbaQ&Ps)%O>vz;E^&p?00u41e)oS3YI70?IL`1v$Q`%E#fMO1sEpyA^>cJ{MW zsITFXtNHszl*grcI=AMv%;k_is_z)z9=DRURuDeTKIpQ!XgL~rqI%Dh4<{&0a}hYp z)Q0R}$qk6dd$Mhopb~tsrD*)|Bz~^-&F?!2{(<{-i50XjQc+^9Wk3C?*VfmSyGq6X z25$zgd!bZ%n@r`7t7J)sAEl1{B9;QJLyGNFjit0Iw0WyAQQ-vUx?;(EzxY9CWj4x3 zBX%*4&C9I8JT46cT;gcDlY>l$ghFwf1VZ2yE8tx=W^x00Ae z3hqhc-td;C1rBm;KfIEs`WCZG%Xg(QY!dbTeyRc)=}sz6WBgQy1Jh!Y2aMzf-HdI? zg1pgLG=yD4zno;dq*?G%n=fQ=b5hI|h5h>%F*frJquIEilj?Fl&*&c-*6{>aWRYvT zf+?v%v5*_p-;urQK>|Cn4dZ(GB<(Aj*+OS2o)K(F^}nKE8cAMNkTH278>aNK1x!`K z19ZE&BCU+wl=z;sMBH?Z`|qex27rI=L~5`BmD)xCyeEs`D54ubn$pNLV6s0FV(wD$fR$Gd5yLB>!$`G z!ETW?yNkST#-QDZ4VmtYOd62SGgWoI)z{$(*X}TJsyZgr6>}KZ8;LK<3vDJ zm|KZ@~?mw{MCmVd(jx(71}h`qKZwI2IkN^oSmnKy8`e zTZ~-b0^D=@FZk$@pZVG#LYNh@Gxt~O?CD7O3Ea#KvIDj(ZLlPKxV|WLM+3N4V32o9VJVYll`nMZ^h?k80`Vj8vx}jSb&@-tgq>; z_rRKSkQrf%N5s+F3Q7Y(A8K+PD?m`f^${wK)s2{CgQ*>;^_L2EDy_LVof{<6H|7#r z^p$g^9ilF5x{#}R1IRh9_xL|*E>2`D2X-gpzfh}rKfF4lW=uk)8zY0m0go!e#)c=0 zh|hI~!;vUE*ygv@J(8gpxY}B5Rqdojs{v??Ew2ZEp8M%PnX!3a90}rBFmX2RITululM0*hAwPn37Y0HcB(5*=ctJ1Pda zVhCH2q4rVryXDlD4$>8@MIUCTuQ{_?KH%j`4K|c>#T&CYuc)7d+Vh@5=Btfe_}MpH zs$lyn6O9ZIFR5!A-JEkG@PWjeX%Bh$c2 zgs7#2YdJDVy*XCGLmf_fK|gs(uzs9>5})pMvb{qSFbe8#dU<_VGb6P%N2qdKq;=km&`J(wNp`0i+uw% z4xqa6oH!rS*+Z-L1;NuvVIE|4dG`iP6f_x#300U<<#PRsneqLI|1mf8`l?X5FD3+7 z!+js0dzxmdch$!xTT~tN`znmnLVEoASlL~gdz01S`UzZtDq;w=S&q)k>SkTiu~QOy}l|(yy0f`4viffD;*WFrGDwC7Yx0@5QP6P{2EH<&FS# zD#dSoR3K0lEIGm1IU~4FIVtutBk7|*rFi--k$1+`A7n)3YNq^m1&^)`Jx6&V$WmAt9#ym)vWEqt$7MSf>H(=k56v0lck- zmO9K}+d} zsc3Im`i2zgJ0H=B=R&R=#-4iV&s#oudCex3^5qW0psSUZ`9ps7)xnbdWZp;3k4qjb|gv0AHX~ka$4L?kS^);Y`{G)X%?1GX()KRX9B!1qS{lNK}1nB7|HBm7^ z8@;$3Ll(|!l`_AGi&MC<@X=B!&_<@9#UNepZ1Lm|8XQJQE7m`DkPY4$%XR~Ot;1fk z)b*+dW`BufLVtY!l*pPbx`Y;I&?;F7SxwF1@K^45iQWQ?eM*gNhx9CohArLbxO{|6 z`1&koccL$%pMzN(7q5p-HS$NGNeU9BWFVd9OlC0qpljI;UBxCDYZula1=jr}0!vZ3 z3yrH8u`IUiN%`ROnCa)qn~RHGC2yPDOb5Qk7LI%z6chGq^;lT?yNPWbC!NfQ^h879 z=oIU!=>61~5T4+GxRtoLz~>jHB9{FGgNVGuJnpSk9$hoiaS7cist$eDp0A8O=L{7m zmT9}ol?k$J;VGuQVCpF3SU2)hOgRuIpRA@Sytd0mpj3#xM{pW61@4STLZ^1YI7Lok z(Ul(NXps}fWYj~g1_$U2T&H>E)J95I(-WeH2~qQyhCBFuuT20B{!+Y1|C;nK0`2K< z*^D<ig6u}hF489f(OXa!*O2~StXXmxIL81BZEr5;@>c8N0+%XxK#;<7!hI8m*0y#}|@^x!J|d`@c4 zFnuLXwVUhaib2?IaplF!#4_T)ANy%{gez9FM{+9jD%^jjXH4%JqR_=LNkomb1p4CP zGYF6fHxw{WzMh1+Nx#fO4v!ePjL+Pa8h*GpODztHk7k9|LCjuV=)$(ozw4zAXXO{7 zcn^sOh2}iG?zQ)gND2kc)qqH1DMy(1?%7 z*f4QF5=}|lR>8h|C{6~Mgg3%)tkn}Nnv*A#kq$_|#lDSRV4Vu%Oi3c|v;H|%q~?=b zCcTQWyk&!L4U*kShM5q{Fguh_v$i`iwKa)6VpE>nwIPO;K-veCu^KNYXeQri#Fp-2pcl#&s5^GgV; z-$DRcVCo_oc{L|YKDGg+COes#^ALF#48?1rHJtJ6IW^WpSS>PI{w^;CFe8Ipg#kWP zcTM|fFaf3}A&GYxX#MJ?PGfqO>2MqN+4bM)>C!K=%N73~JF zo;EpgKWi~cvd}Oo>4BUNJWCw$SBkYOQ_h~g_AzMg652`}6&G*ue=6X1@Cf>8$BJ3` zW6HeHKPn<1=!O2{rVKnTKn3PH2UFtTpjv0cCV36N+#S`pdl}?t<($CjhLj#L)S+=pl#zOhCRtzhQ_UwgurUcvbg%r&89!d^zKp8(J=lCkKH zmM2^~eKT7Tw9AB3*@Ng(i7c;*y()E<=~JB#1w@okj)`H2S$puP^C9POe>?05CNCY>WY<2`t{2t^@Uge`tF~eDSNV3th@LU2 zkFlQACxvY370}VsQMTvaxw(B8usv5m&eqV;rvVW5JlJmMu^kW}Py2D6o;L1ey*P;y zO%$e92k%MuN9V;UUsCS+Qj3xTR39-%8kmE$+UjDiDPn!g9H`y^?H%-ss2XTPbIc02 zS3qk#Gx}q4YrErzs41wF##Az@OE>^b0!xb+22SRbq?ym;9ma_AqCR*We>uU=-^O`FL|C5D|O|!j7R!+Pu`uUB5RrzcGGH z+~*-;s<{paD*iTTsY;zlGxy5$60DhaNKWQ|AVC@_S?d=iJ+=+~qGtCkhWl|-vZZre z0~s4@ku3VmDy>7=HIsJmC8hh0d1dui@48A~b}MlySzbLb1(Xoj5?Ue_m0nI#x_IS_ zYXKet6YhN(417s=h57`R;BiCC+yo=Q%~PNt2ETwlSb$G7r+~ zmErB++OBWa?m&+Ra4XMk70Z?lxi_9od%%{T6qRwt|IepTbhZqF}3&T3&9$$ z8{&MhW4Rn_q$9IIUlLc#2FpHPsUCp=f9b@m8+q86Sgr8S`AU(xs@>I>^1>>Zo|myFq_^LFD>^UQ`n;O=o;2#0w{!7!pP-8yq?k zcRytm%bi@>g~niw&tH?{@@_Z6WAXMQ%oP{^80{GZ3kFw%0%}DBM~|F94hL@hrY&t* zyaeT}{xW!Vpr@}N(7GQH-PR3 zy)jDE>3|qlZ)Do`Cxa6vPTRwnYJAsNPnRONJ{mhNvdxG#flObxnw-UdW;&K#kb_SmE(){y}7Jw2kl$;Jm*pl8qP4JyR!C zF&|i{XBrjVma+&eWMI7 zYBrmHEvk1_^@{{k()00BBSv03$s%4?OpSwFb8@@G!G$DVPoJVIo*hVj`c<6K&o=dH zd6{eOxO@cnP0_RO718hj+P=f_(8EOYCqNHz(=Uuomrhjg4cEi`cD z;7C-H4Jyz_Qgb;0d3uKQW4CIuOJ8r7;6 z#3Lg6?OFHmSc{@67@Yx|wA=VJ5 z{|ae}@VS?#<^%frc9w&TiB|ga@1^*^D>(@4_QXYSHsE^?6#+ccOZZ+lUShAr@7~cey8Ri;ROeP9cI*oAs8=P$RRo6*)64?a?x1R8&ZpvV_vrqE6~c{|4a6tr zndN4>mTwd7U2~d;{MdhUR}T44_X=TqZ17FOKE#7~_5Bw}?{^kVOh+M6z04HXMV~0XsV?Xe5PPg1yu4&u2abJIpi>#SfBJNjzUwNy6|8B1BBR5L5TvQ> z%6@J3wTNVNVD$~ae_MJG_uy@~CHqjf)Je)>r~d|U71^`O*@-*tE(en}@};@xg_*^3 zn}&joBqQXSk5FnVNHQlZGm)C<9)Ed&%I;Wvj4AiYbz283KN6Du{N{X zY-KsgILl>8{(51BoL$F3u|J@G7+hp?*}^5?AIk&Q7+wu<&%P)Ul+9C{nWoZjfaEjM z=Ia4+iI~J5;|TM-zhMQ8+#e2jQDpVKIUt3sEt=Ozs~_Se8YG;z;r%~UHwgyz!8qn# z?0m>4@^vQ~wDTVJ@J2v~aoo|>N;0{n{R5&YM}uWwX}P(}K?7bi`{;sJ;b2ax+o*3o zsK5(EQ!)n((Hs-AC}f;Yj8?{a|2z~OcT>5P>S)a7PQgKr`V~;SA3fH_oF&32Rf)n6 z3{%Q=*;V3BJ7dnK&bjI|=)6X*DxQQRiJ6VM75v+GTaY*LPeAlZwjWRRMK~01OpOQF zk-TuLiR0PTSD4(%<&HyN>vMC@RZatTcb|WbDvUL$g)xZ`^^?FgU7nT87-k zT_o5=2=iAlncHxT+LLN#VuftYV+(BUo)A}vL&ptNX(L7IN59NIDTgj|3`3O`IQo)? z%qQDE3(9jiab2+(_b-5lDrd5Zq6;?^YP%95ax>~vsfe8Q=VI5v#?jkEMdZUftuGAx z_)7xb@y39Y`K{ir5cQ-rPUDGoz?pvqy(6FRgnZ~2IDRCfqXWeYi_Lr;WzWbc|IYf_ zaE%kb5YWr7wsZIf_;5nvRREFO*UQ#ZeTuG{xZ*u#;FkICtw@;%+`|(I{|fT7>wfS$ zF71#6-_W&UjZm-R&RILHkM4avif`Urmo48!U9@Q)Hd&Q9NsXwnBd9huNxy!7rfEqTAJ^xH@sz(ouRF`!$~CAteC|e0w3*{`@_c2BCeF} z`%uwXQ3|D{y*)r`i%0n^(&=aR9#X4~DfVM1=LrJh`$X%_efBdcU($ttvqBns`pXaP z2Cu)n@Xbw2$>2@-QFfa0ZbzJXl2ZcL(tXT#);`L85*3y-ib`4S{Z3xwUZsB6_zzi8m9~!0saX*NXlINPd4!S-7)rfcn!N`$AoYEILDJCCtr3gFUkNVq)syvWK;aSSptg^%H5U z1uJS%@Gu22{eVS(;q zDNW#$xahi0cVBM?3CYWh3j;FBf0gvRX=d@J3d z(}j@6$|{tT+m92BHEFImaZ$IzTOB&gm|=>2MXe+=hjj3ZwXyj{fQkL%bN%N=A;XG; zwzM(Z#xhk|KAL!s1dXH^{sb3t4ptZGB?~4G>g;FdX_Nfj3P&cYF^gtN@{_5Wk&kF! zLSOzos*4|a68IymaWn5_jyWpYl}#>=Uc}eF}qvw2RPR1+V34aZxwAnDt*Y^ewNv4kY*PyljyUJ`JoLOz(oqVU8<(K z+K$Vy4s7vwia_`t&HYXIXxXhf-sK+(o;@p1DIm&&eB6&>E@-GEn_)XjP;N2F4Kpz= zNhq3)l)0b@Klz+?reH;*hMujTBf=OuVL?Vwhsrz(*&Q7CQo)@B=f{@=Vt4r(W={v2 zlg~bP&2i;zi#F4l=T+Q9K1d_4guf~BH5k^VyJbNy+t}AO9=O<9wVf z_R`7i{D{q`-awQHz%#tLV^gUeVUGADBF`3{+PtaXU^@h{d9!Y!5T2?t!>FS({-z{FmlxUv=B?t~NtHVKOa6TJ6UJ)-Hj~4_-cg={Y0QrT` ze(>x;ZezIiXYsn(&Q8JqbJJpl(momz>7!PW7hP`?dLLEX#An6AxF0?svj;}PBg(l`UYtg?5Ce;YVSHMoMlP~aU1nO;5f-32t#N-P&+F1xC>N7*~ zX6kFM=fc=RkEdnEMDEc{`N_74x%oP2>%!+J#T6pb*8QAY)y4Nh;Cr7z{%2tuoofWh z#(iZDtX&Fk*P-6xcH+rI*fL3-H)Bj<_$OE>1_R}CuM+q6s+OcH-;VzD*?6`K-f#wC z0q7Zq=$S2j$=rDA))}Ybpq6sD84c;0nyM9RcrKFiQbU$Xq2Tm`iR%Ps^<)*u=vYcp z8ceBOAN zz3%68SZsQ6b+qx2GQI8gdEQ_D)cFkndpMu}rPJaDEJk3a2Oq^9_%TPpLxm~XH3FbY zcwIyQ{dWE2CXj}kt&fr%up1l=CRTwb3yknY+3_<@4e%r2E%=}&9S{DAUVo;4OWmAP zpb^r&J~tXil~1x0!+%qR_<8GB(nr~P$>$G$Xk-c@yf<*y6+a$SmqJ1X`!tR`JW7)*U(VF*@hVx*EnEl7aDv`{&O%rrU0X*`)V0d$wXvw!Z5( zaYpKr*56+-{+jlYS^6Gs}`o$%4acuIJvAPuR7boY}?7L=CKRmb^Ju%uYwy4w3 zpRfAlT^S^AAc*&j^8FxFO4$uJkrzAN9Xn|4gjVX#TbU9E)c-)j(Ptqh(GLNzOcWwmZ#%Xq^iF{Tem!DV;MfRyx81 zbY$tjJD?Ma?zEla$wwk?6#ExOIt(0D&Up#u$-uRRh~@n_+ktQ|>EHj(;-@@B{eQje z9f-i_)JevCmV@jwF9H9Rco2Za7v$0(l@o2hIQi@F7&i(n{P~SZbV=JqLI%vKSTC6} z_vq^z`yEQ*T(2^LjXkj>N^*UEs*hckn`_BpkUhVTLsux6P=P?{cvtfwZ|Dj0;*i%FJg$jsL0W)aWis7kM9deI>k}h*U#{9=U0nQJ@Cu4y#?Jthp zR+l`=AU~c#5{mS5==WD#0&a4XXxXH^e+axoa3ZlKn(nmrNkW)#?LoaVf_OCKCA?f` z0~!VH@u11GPJ-h%z>-JG5)E(2h4p?;tYhfq4k&y@B1`7+t6Ht+qFn#6C5k{K@J0<1)&b#k{~6W8nJ^wi@j zy=p&F>X4sphV=tPMjhlfwvAfpf*$olWKFQ+K6MA7H%rm@c`Ngd3B*~EigNY}a3aCv zNQC#n)dcwAs<08~k_|klFp&X$ZJZzVzl7-$W=Y%HOQc;ClcnZZkQKt=N9%;lQ(y~W zAV|SM{VOe*K1i+Dhhwr!zq_EXA4k@dJ;H@Go$?Y%{xS;o+%?5tkJgFs67Jk}RSVol zJ@Gd97rV6q^DS=UEv6z}zfa{nD6u-aQ#}4#Z!>PughXXL?Cp&z8fdu7*dI2yp0BjX zHHr97__S-tLl39pqtsa`F-Towhg(*iGGbO;xzeTQidv*vT4b;aoq|NQvyF81FXuzY zbff=SYN@R@>1b#7)I66Fa|5+DJFw+1sKlcGa*&I9U>@tp>E{baUHta1ygmR~`PXt0 zjd*XrNP)*3gD!zFmDlED4Jkzc!*=h9oX0C~CLz7r#gY`~rR}yE>hvAJ=TPV0(mRmh zPPz8K4YxjKbxO$6`+SbeUY-WS>pJqR!8BU*Iaz*e`y|eION2`vMBE;)p*UiuxYpdY z)f5>UFP<+}2Q|y0S7lzn-jAyp3Gd|r9J~oy@MOf-7ic^s;W#6%q6@I>r<ktm)VHv_mkN^ zbAA$_b#EZx!RDnv*bkgBz7-CgFRY!WuFcw|lw|?CqQ>T2jYjyWL(ctY$r{k6%w0cm zO)LXb+x(cji~@LfJhp}FlW*ztNa-B(2rX;{X#&Dm1alFj4e+Kvl%Oj4&nryGkR05x>$kcH4vuE zu;DuOJ3xsjf$|4+{=56US^BjWQaW-m72)!uK{_TSk9bgco`#KAq{UQ-Z8p1FoPQ=* z_?r@%>*p0=sTqmV<=2i0-7jR+yI*R5@u3k<7d-9!hnFZZbn)wcq$NNAd?X!Jz=I8Os}tsWXB zJk7U)xrOZUqq2O~3F8h~R+ZA-N~4cj66!B0cJ(ahl)SbC#ZHHYS+fftUzBr0Vjl?a zu9kCQwU>$T0?YN9Qga8S7x}%dmSfh7iBCM{W8`(ZmzWa~X})Ltix(ibox9d{F{d6b zZMo1ah%wO=;>hsO;+!b$R*#k~86rj2j{#Ka)q6|UI4{@A;#b;s4s&m8czi0q5iH$> z{2ao&iA=T2MMR^K7$Et6gV+Aq29v+&8!z2T%b&k``}=Ukh6lc%u8q&UW;eE$z0vIu zf95`a^`N8q+ekuQ?DqKh<6D7i@B6 zuX6d3LPdAAr6wWb2qaYfn?8JU%`mAl58`rOQFRRGTDFTrUK|&;>4?NgyRHN`7BhP? z=n|7+h0_pzM=FO7d16aoi*=)^%OsEF9)(jiC+1-b(SLB?jpS~;vtg={cSR1i#$D?I zpD6xNo!41Z&Q&BqXGll>!$|l%I1JM-gyAb!Kv&`HSr*Rw2Eehz)(w&r@ajj+*scVq zHe8(c^@{6@7Z zJ;m1b9Z2e4;;HkgTMk>zdxHC$kWB-8$bE3wPXFSAeqi@L^ai0tgKPG#Y+J--M1Jiz zxEdKRW?y-&+_6W5e!yOUT=#HA;*YB1Y&q?qLyHW>HFY6@goAlsb5>IK09To%gGKVW z3tx8*FX-&?rG<};9=sRPN?Ugzq29IZdg*O zR-^x$U=?w}?Wn(gMU{D?X@w9{tHJuRqs~WVNNhu! zZ9%K5!|Thxj8U#CXI2)U7L0{Ks=Hnl*S|x_cs|KjrFkqixW265^jZ((z5$FNzMH~x zr71mS!v0OrM$o?`N7+GA4V+bsjLA6ml4WQENdYL7&4#8ZNE=%dDajz!Kg=jlG;aT~ zI$$^5t#xLfdyEJB>qV2-<$4@YzQBTGMP%8oh==181yf1|L0I(&sIH)J$fKedWn(4_ z0R{DZ9(kErCF@n;tcH#(-Fs?6yx7+g>?q-HFUI`+F!@Lv&ShM!B$@)a8l#Ps@BpgU zxP3nwNKE?Z@I7M%vyv!GEm`~AkwffS*D%G4p@>jC=EZ|3y(#M0mUauuD{Zg1W|f_t z5G91mgY^!k%)#4IHI2Ea#Jr9xLikORjp94K&cC1n&AQhi;@M2o-LKj=0O*?3QrcYk zYpQa~MAO4{8J2z{zY0|!^x?f&2*P{7_4MVCT+3ThHMkN^Dg`NN%Uzk@ zrb<&A-bs0Ca28@-`x6&R<}M8(WWTL+jo zp*agda?2v2BcSwBgw|_zTMBI0_c-XCfqrV`D*?NWYsQ%yT86*rah0D^Y%f5o%Cne2 zQ(5I3B4IW~FvUldo;!v{+I8$3cnr9$Y3Ky`M{TOI@%o$e$EYW?Pe&Gw?d&bMm;4QjKoD{2k} zVoTCrw1Zuj0tf^NCURpm1fjci4Qz{Zh|%+h!X3qF=cU`*Un>ZCcv|fyRvtLD0;wFwfyh9 z;r)p7)!+yx$H~ic{Cj8vH+OPSW$VS*HFb`sAET1`7Zb#eI={KSy~;B`Lz7ONi{MJ@%-(rui&EJ8F$+8vR&j%v(a zjwgy3eKf{)5MxFY)Fcu@N-qfsE7o*s5miIn@agoMhbBR|8%Y)QOC7B6R!u#?B|;jH z5Ic|4s7G?juE` ze-0g<6$k3-_rwoOeme@6?%edI z(uvP|GBx`q46E+E-=`jaOvW!579%1TIEkiC6LRK+D_O0kfoWYsRQTD%SyeYR?`q#G zitt2&g18Hb`=WqHe?M>~y?eiCRx|02QvN}S`iq3E%l9q>!?k1pIDlg~Zlw}M+A%;2 z>d5q)oeTVj5&3beVewx<#WIt;Lj;R-zjGaaDO~F8jg=5N`zm{R6D9DkT~0;X%;<8R zpq;@bSATt<@7$kdR*xqr;n3{?gMOmXigv@FRiF1R@7KWD8J4}BgbEa@ARE*>V9P6& zXH&6}LL|{XonM2$814x^c~iO;mdEOMoD+*09_Engz{#)I1>t7P^9uOuw50ivH-IQE z&v#v%ghO=)?ckW@UwF_{4MbSwa^6A1zHs%tHqIh$% zsoIK^G&VyeUN9Rhh_wGPO{(AJHQ=C9on*REp01K>3PBD$7pzUZ3cVO@&t;u~e7<>{ z6eEb|R5#@A-f14M)DedYm$XW%YeXQorcc%ZdS>P(22sU`*GEDSyga^TA&BUG?PYMN zCd7yLU;gop$s1tHO44KwqBs344=$&-;)_>!x5KVAkEMT}%Np6Btt<|G6tn~Y_2x>W ztqqSUhsaz1<9B8u`k|#3JvnVEbtQyy%E*}5wd@pkoYitO;SpWWcv}C2>lcnNtCIiV z0NL$Rtgsm$B-^(e76CA<4!Ak7JZXgV366wnr_4dFkEI5{V>gg662khM+keC&_eXDl zFVoHMYyVYhXTm;yN}YA$1Ti`gD(8@lyj`~FinlJiHQ2wnE?u-BX4e}+DcZS8YS9$* z>TEs+@&Vy+Mf<_H)a>|#Ws-64Q#2x8z9(O+ALTL+0Fw~BDH0Prjc(mw4^fO#62Fu5>-@5gGMS( zjS(#S!H24BPrvwE(a$@r89!B?jL2v9=v5DdBN2WUW#*XMo~_~Us4K4OkPaLZA7HxM z=fp#sSK5PrVCQNqk-yP3!Ud|H*i%KFyi+TMtA_U%uf|D3@AN+&6Zk5e&wIjy^IXyb z;xM}p%+J~S0X`_{kMy4~@KUK4m8xeS^dz7}+?_9RV51dvk5urL6-+)@ibbi*~ht+Z8D~1oVukuiiW(>L&MrOQ6&FjZ8_j6AICzzH0`7 zM?XvU~<6a2sJwci8elKRgf-sXOm^1aVEA zBT+%CeGFVVnW!kMz(y1p@2gl6^98E^AQ9Z^kLp~6UL;)?Ly+e4A_~@*r#vhup2v5X z2LL_H=!yjlR(wg|)-Mp5oYON^r;lEC!{%J6#AJv#J^%3iwonOWBk1LAk^h?*+`v2uSXL!>Ag( zaCD!=xO;8)UyV`EObkz-(q@b!r$FV%36qnb$$FuS!>j&L9vSN7)Xa3rr*~^w?+12%nR`K?j?CQaBAuiAQ__h0 z>Z5^4c}L2m)gSvyU#E5=w8Wv*2E!f1@V_IQA2rygg?u+L5)i5xk~iwhNXL~On+YGy zvOTsEUbaqbteEl-kmDyL>RMur2r|$hXP5rpSmq~3et6{yE&pAWr97D%gO+`bG#^fw zbcKWTN81Xak&6Ev5m-2DI*d4p2|*iwqQchxorr6~jr`2}V$rA=DPW?hp_6i#kNQj# zla=-c_#_5ZnsV)&Iu3l8k2Kbq)BE$9`3d8nw8-U6QJDFYhI39r$YpUjd5`@=*ayhh zkxx%|;8p(v{_9c?O93(qvcN4&EPr=T$NAkBnXC7L*_{}S*f!~lRd0D}iF^`s^c?2| z7RY?|t3E)4k!=6iJ|T!2V^D(H755}`e9)^OR|hpq9C`4Fz9Dd#ld^e&l)lDOjf2}_ zG28NUx|9I+5%T_adO6OiHGMO#cC}?&1PeTqwq@CYVYqhmA5_fgbXP$y11fah;&WGA ziMGrj%;|l9&_Wmclb{rS`)0eP1Wmd>`(x+q^aNa}9d|^=o{jHT<_bUPSbw8U( zH3%@Fg7`CFe($#%CSPi#7I=GJ--wK-jZbjjLkbjKKJe7Qo|^a8Vr;}ZJw&7(RYV)h z=icWlwFj~{hiG;=`>=X7K7Fl|lt#EM0@lg+SEf4W zWJxXhSFSqa4%uH(vI!A-rDRa>y#|c-92IkJ2zmmvTC%a>>dF%Ttjc(fxpB!&o z2&W4hx!K;o0W99b_<^z(UDn$UMEQ+lvQh_IhZV<}I&z^$?ftW70W_N8vlT=lwE~~- zGj(E);TOB@W>#O1vybIfT5<1x%t=enoc587nF|z{{JnR9#UXPzR8E~!JkfyG=@GWq z%Fu>WZ1`r(j;I+rn8k7US$+}Q7v{4DegNMW(7eL79_AB~L_hSVK6OI}}ddY?6s?fOt3? z!f^>l*v=3Un{0*aDcNDgNBv^9ALiH4JTlLcspDS}2nUnd9=MRw%)PncEn}=;h&HZs z3QlwTEIScuW()s?7=iB^?Fk>Zb427mDd(C^#4YDVu_3g3W3H@xpT}xa-q$iw95j?) z71m7-9y3m`PKR4ry$nt|y#c)DhsX&GP!d~Y!3%cp&Qvn|?+sL` z7|*=EfiMb+U4GE%TkVeePLFh$8X=sW^p+gqV zy=#%PQS#wI0B4SsaUq#LTRh50zyyA#x_Vf}=ym=dY-%>+)^z<^g`t~1s8iasMoj|@ zP$Ae9Sfk2T{3r;do`*2#nb`g`2+z5Zz!+oSxu8cm zcOxD$e>%Q}M|J9U8cP>}cXNOM{Hua{pQvT2dnE1A8e2f^S)Rp}>_;kqr;f&&F~lUP zy*hq|1-K0;2d00ecdd`h>-=oy??xwaT~XNcCAk_58qmnqgl&LZYG&9gG=XZ<(f0bfriMlOjSwnFwLFg+~K^WnI{{U!zGg=}gq0o3} d=To-N{P$1iTB8H%!Z3KMC6XSL5Hf;~|JeyjMW+A& literal 0 HcmV?d00001 From e3fe5a5681eae116755727d4d13ee5fd681a39a6 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Wed, 3 Dec 2025 10:26:44 +0300 Subject: [PATCH 008/103] Merge pull request #28112 from asmorkalov:as/jpeg_turbo_3.1.2 Merge pull request #28112 from asmorkalov:as/jpeg_turbo_3.1.2 ### Pull Request Readiness Checklist Previous update: https://github.com/opencv/opencv/pull/27031 See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/libjpeg-turbo/CMakeLists.txt | 4 +-- .../simd/arm/aarch32/jccolext-neon.c | 2 +- .../simd/arm/aarch32/jchuff-neon.c | 2 +- .../libjpeg-turbo/simd/arm/aarch32/jsimd.c | 2 -- .../simd/arm/aarch64/jccolext-neon.c | 2 +- .../simd/arm/aarch64/jchuff-neon.c | 2 +- .../libjpeg-turbo/simd/arm/aarch64/jsimd.c | 2 -- .../libjpeg-turbo/simd/arm/jccolor-neon.c | 18 ++++++------- 3rdparty/libjpeg-turbo/simd/arm/jcgray-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jcgryext-neon.c | 2 +- 3rdparty/libjpeg-turbo/simd/arm/jchuff.h | 6 ++--- .../libjpeg-turbo/simd/arm/jcphuff-neon.c | 12 ++++----- .../libjpeg-turbo/simd/arm/jcsample-neon.c | 8 +++--- .../libjpeg-turbo/simd/arm/jdcolext-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jdcolor-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jdmerge-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jdmrgext-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jdsample-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jfdctfst-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jfdctint-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jidctfst-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jidctint-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jidctred-neon.c | 2 +- .../libjpeg-turbo/simd/arm/jquanti-neon.c | 7 ++--- .../libjpeg-turbo/simd/i386/jccolext-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jccolext-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jccolext-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jccolor-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jccolor-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jccolor-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jcgray-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jcgray-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jcgray-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jcgryext-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jcgryext-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jcgryext-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jchuff-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jcphuff-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jcsample-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jcsample-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jcsample-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdcolext-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdcolext-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jdcolext-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdcolor-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdcolor-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jdcolor-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdmerge-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdmerge-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jdmerge-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdmrgext-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdmrgext-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jdmrgext-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdsample-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jdsample-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jdsample-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jfdctflt-3dn.asm | 2 +- .../libjpeg-turbo/simd/i386/jfdctflt-sse.asm | 2 +- .../libjpeg-turbo/simd/i386/jfdctfst-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jfdctfst-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jfdctint-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jfdctint-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jfdctint-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctflt-3dn.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctflt-sse.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctflt-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctfst-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctfst-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctint-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctint-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctint-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctred-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jidctred-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jquant-3dn.asm | 2 +- .../libjpeg-turbo/simd/i386/jquant-mmx.asm | 2 +- .../libjpeg-turbo/simd/i386/jquant-sse.asm | 2 +- .../libjpeg-turbo/simd/i386/jquantf-sse2.asm | 2 +- .../libjpeg-turbo/simd/i386/jquanti-avx2.asm | 2 +- .../libjpeg-turbo/simd/i386/jquanti-sse2.asm | 2 +- 3rdparty/libjpeg-turbo/simd/i386/jsimd.c | 2 -- 3rdparty/libjpeg-turbo/simd/i386/jsimdcpu.asm | 2 +- 3rdparty/libjpeg-turbo/simd/jsimd.h | 2 -- 3rdparty/libjpeg-turbo/simd/mips/jsimd.c | 2 -- 3rdparty/libjpeg-turbo/simd/mips64/jsimd.c | 2 -- 3rdparty/libjpeg-turbo/simd/powerpc/jsimd.c | 2 -- .../simd/x86_64/jccolext-avx2.asm | 2 +- .../simd/x86_64/jccolext-sse2.asm | 2 +- .../simd/x86_64/jccolor-avx2.asm | 2 +- .../simd/x86_64/jccolor-sse2.asm | 2 +- .../libjpeg-turbo/simd/x86_64/jcgray-avx2.asm | 2 +- .../libjpeg-turbo/simd/x86_64/jcgray-sse2.asm | 2 +- .../simd/x86_64/jcgryext-avx2.asm | 2 +- .../simd/x86_64/jcgryext-sse2.asm | 2 +- .../libjpeg-turbo/simd/x86_64/jchuff-sse2.asm | 2 +- .../simd/x86_64/jcphuff-sse2.asm | 3 +-- .../simd/x86_64/jcsample-avx2.asm | 2 +- .../simd/x86_64/jcsample-sse2.asm | 2 +- .../simd/x86_64/jdcolext-avx2.asm | 2 +- .../simd/x86_64/jdcolext-sse2.asm | 2 +- .../simd/x86_64/jdcolor-avx2.asm | 2 +- .../simd/x86_64/jdcolor-sse2.asm | 2 +- .../simd/x86_64/jdmerge-avx2.asm | 2 +- .../simd/x86_64/jdmerge-sse2.asm | 2 +- .../simd/x86_64/jdmrgext-avx2.asm | 2 +- .../simd/x86_64/jdmrgext-sse2.asm | 2 +- .../simd/x86_64/jdsample-avx2.asm | 2 +- .../simd/x86_64/jdsample-sse2.asm | 2 +- .../simd/x86_64/jfdctflt-sse.asm | 2 +- .../simd/x86_64/jfdctfst-sse2.asm | 2 +- .../simd/x86_64/jfdctint-avx2.asm | 2 +- .../simd/x86_64/jfdctint-sse2.asm | 2 +- .../simd/x86_64/jidctflt-sse2.asm | 2 +- .../simd/x86_64/jidctfst-sse2.asm | 2 +- .../simd/x86_64/jidctint-avx2.asm | 2 +- .../simd/x86_64/jidctint-sse2.asm | 2 +- .../simd/x86_64/jidctred-sse2.asm | 2 +- .../simd/x86_64/jquantf-sse2.asm | 2 +- .../simd/x86_64/jquanti-avx2.asm | 2 +- .../simd/x86_64/jquanti-sse2.asm | 2 +- 3rdparty/libjpeg-turbo/simd/x86_64/jsimd.c | 2 -- .../libjpeg-turbo/simd/x86_64/jsimdcpu.asm | 2 +- 3rdparty/libjpeg-turbo/src/jcapimin.c | 6 +++++ 3rdparty/libjpeg-turbo/src/jcapistd.c | 4 +++ 3rdparty/libjpeg-turbo/src/jccoefct.c | 3 ++- 3rdparty/libjpeg-turbo/src/jchuff.c | 5 ++-- 3rdparty/libjpeg-turbo/src/jcmainct.c | 1 + 3rdparty/libjpeg-turbo/src/jdapistd.c | 27 ++++++++++++++----- 3rdparty/libjpeg-turbo/src/jdcoefct.c | 3 ++- 3rdparty/libjpeg-turbo/src/jdmainct.c | 1 + 3rdparty/libjpeg-turbo/src/jdsample.c | 5 ++-- 3rdparty/libjpeg-turbo/src/jpeg_nbits.c | 4 +-- 3rdparty/libjpeg-turbo/src/jpeg_nbits.h | 4 +-- 132 files changed, 182 insertions(+), 167 deletions(-) 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 From 8efc0fd47be47730a2c13c571b2ee450a9b74c80 Mon Sep 17 00:00:00 2001 From: nishith-fujitsu <139734058+nishith-fujitsu@users.noreply.github.com> Date: Wed, 3 Dec 2025 13:12:28 +0530 Subject: [PATCH 009/103] Merge pull request #28055 from nishith-fujitsu:sve_fastGEMM1t dnn: add SVE optimized fastGEMM1T function and SVE dispatch #28055 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch **Description** This PR enables fastGemm1t vectorized with SVE for AARCH64 architecture that called by recurrent layers and fully connected layers with SVE dispatching mechanism. **ARM Compatibility:** Modified the build scripts, and configuration files to ensure compatibility with ARM processors. **Checklist** Code changes have been tested on ARM devices (Graviton3). **Modifications** - Implemented FastGemm1T kernel in SVE with Vector length agnostic approach. - Added Flags and checks to call our ported Kernel in Recurrent Layer and FullyConnected layer. - Changes made to cmakelist.txt to dispatch our ported kernel for SVE. - Flag OpenCV Dispatch with SVE optimization is added to support SVE implemented kernel for OpenCV. According to OpenCV build optimization https://github.com/opencv/opencv/wiki/CPU-optimizations-build-options cmake \ -DCPU_BASELINE=NEON\ -D CPU_DISPATCH=SVE\ **Performance Improvement** - The suggested optimizations Improves the performance of LSTM layer and fully connected layer. Name of Test | dnn_neon | dnn_sve | dnn_sve vs dnn_neon(x-factor) -- | -- | -- | -- lstm::Layer_LSTM::BATCH=1, IN=64, HIDDEN=192, TS=100 | 2.878 | 2.326 | 1.24 lstm::Layer_LSTM::BATCH=1, IN=192, HIDDEN=192, TS=100 | 4.162 | 3.08 | 1.35 lstm::Layer_LSTM::BATCH=1, IN=192, HIDDEN=512, TS=100 | 18.627 | 16.152 | 1.15 lstm::Layer_LSTM::BATCH=1, IN=1024, HIDDEN=192, TS=100 | 10.98 | 7.976 | 1.38 lstm::Layer_LSTM::BATCH=64, IN=64, HIDDEN=192, TS=2 | 4.41 | 3.459 | 1.27 lstm::Layer_LSTM::BATCH=64, IN=192, HIDDEN=192, TS=2 | 6.567 | 4.807 | 1.37 lstm::Layer_LSTM::BATCH=64, IN=192, HIDDEN=512, TS=2 | 28.471 | 22.909 | 1.24 lstm::Layer_LSTM::BATCH=64, IN=1024, HIDDEN=192, TS=2 | 15.491 | 12.537 | 1.24 lstm::Layer_LSTM::BATCH=128, IN=64, HIDDEN=192, TS=2 | 8.848 | 6.821 | 1.3 lstm::Layer_LSTM::BATCH=128, IN=192, HIDDEN=192, TS=2 | 12.969 | 9.522 | 1.36 lstm::Layer_LSTM::BATCH=128, IN=192, HIDDEN=512, TS=2 | 55.52 | 45.746 | 1.21 lstm::Layer_LSTM::BATCH=128, IN=1024, HIDDEN=192, TS=2 | 31.226 | 26.132 | 1.19 Name of Test | dnn_neon | dnn_sve | dnn_sve vs dnn_neon(x-factor) -- | -- | -- | -- fc::Layer_FullyConnected::([5, 16, 512, 128], 256, false, OCV/CPU) | 5.086 | 4.483 | 1.13 fc::Layer_FullyConnected::([5, 16, 512, 128], 256, true, OCV/CPU) | 8.512 | 8.347 | 1.02 fc::Layer_FullyConnected::([5, 16, 512, 128], 512, false, OCV/CPU) | 9.467 | 8.965 | 1.06 fc::Layer_FullyConnected::([5, 16, 512, 128], 512, true, OCV/CPU) | 14.855 | 13.527 | 1.1 fc::Layer_FullyConnected::([5, 16, 512, 128], 1024, false, OCV/CPU) | 18.821 | 18.023 | 1.04 fc::Layer_FullyConnected::([5, 16, 512, 128], 1024, true, OCV/CPU) | 27.558 | 24.966 | 1.1 fc::Layer_FullyConnected::([5, 512, 384, 0], 256, false, OCV/CPU) | 0.924 | 0.804 | 1.15 fc::Layer_FullyConnected::([5, 512, 384, 0], 256, true, OCV/CPU) | 1.259 | 1.126 | 1.12 fc::Layer_FullyConnected::([5, 512, 384, 0], 512, false, OCV/CPU) | 1.957 | 1.655 | 1.18 fc::Layer_FullyConnected::([5, 512, 384, 0], 512, true, OCV/CPU) | 2.831 | 2.775 | 1.02 fc::Layer_FullyConnected::([5, 512, 384, 0], 1024, false, OCV/CPU) | 5.92 | 6.379 | 0.93 fc::Layer_FullyConnected::([5, 512, 384, 0], 1024, true, OCV/CPU) | 8.924 | 8.993 | 0.99 --- cmake/OpenCVCompilerOptimizations.cmake | 15 ++- cmake/checks/cpu_sve.cpp | 24 ++++ .../include/opencv2/core/cv_cpu_dispatch.h | 8 ++ .../core/include/opencv2/core/cv_cpu_helper.h | 21 ++++ modules/core/include/opencv2/core/cvdef.h | 2 + modules/core/src/system.cpp | 2 + modules/dnn/CMakeLists.txt | 2 +- .../dnn/src/layers/fully_connected_layer.cpp | 8 ++ modules/dnn/src/layers/layers_common.simd.hpp | 114 +++++++++++++++++- modules/dnn/src/layers/recurrent_layers.cpp | 47 ++++++++ platforms/linux/flags-aarch64.cmake | 3 + 11 files changed, 241 insertions(+), 5 deletions(-) create mode 100644 cmake/checks/cpu_sve.cpp diff --git a/cmake/OpenCVCompilerOptimizations.cmake b/cmake/OpenCVCompilerOptimizations.cmake index a0c503ba57..64da33d303 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) @@ -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,7 +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") @@ -369,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/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/modules/core/include/opencv2/core/cv_cpu_dispatch.h b/modules/core/include/opencv2/core/cv_cpu_dispatch.h index b920ba349e..8b39dd549c 100644 --- a/modules/core/include/opencv2/core/cv_cpu_dispatch.h +++ b/modules/core/include/opencv2/core/cv_cpu_dispatch.h @@ -237,6 +237,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 @@ -362,6 +366,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 04b00d2024..d521b066d4 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 7fea9c0bd6..eda0d3edbc 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 @@ -341,6 +342,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/src/system.cpp b/modules/core/src/system.cpp index 082e0aa804..98971d57eb 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -427,6 +427,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"; @@ -589,6 +590,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/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index a2d8c957d6..641beb71ad 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/src/layers/fully_connected_layer.cpp b/modules/dnn/src/layers/fully_connected_layer.cpp index a6ec0c2181..837dd93757 100644 --- a/modules/dnn/src/layers/fully_connected_layer.cpp +++ b/modules/dnn/src/layers/fully_connected_layer.cpp @@ -228,6 +228,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); } @@ -277,6 +278,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); @@ -342,6 +349,7 @@ public: bool useAVX512; bool useRVV; bool useLASX; + bool useSVE; }; #ifdef HAVE_OPENCL 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/recurrent_layers.cpp b/modules/dnn/src/layers/recurrent_layers.cpp index 202933b4ca..3d960012e7 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 @@ -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/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() From b1d75bf477e77373b420d31ddf36709c0907dd32 Mon Sep 17 00:00:00 2001 From: Gursimar Singh Date: Wed, 3 Dec 2025 13:45:59 +0530 Subject: [PATCH 010/103] Merge pull request #28078 from gursimarsingh:codeql-migration Adding github CodeQL #28078 Related pipeline in CI repo: https://github.com/opencv/ci-gha-workflow/pull/280 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .github/workflows/4.x.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/4.x.yml diff --git a/.github/workflows/4.x.yml b/.github/workflows/4.x.yml new file mode 100644 index 0000000000..121878488e --- /dev/null +++ b/.github/workflows/4.x.yml @@ -0,0 +1,14 @@ +name: 4.x + +on: + schedule: + - cron: '0 3 * * *' + workflow_dispatch: + +jobs: + CodeQL: + uses: opencv/ci-gha-workflow/.github/workflows/OCV-CodeQL.yaml@main + with: + target_branch: '4.x' + workflow_branch: main + From 1d8d76cd508e939259dd9f76909d206adc4c6fc0 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 4 Dec 2025 11:28:32 +0300 Subject: [PATCH 011/103] RISC-V RVV diagnostic fix. --- 3rdparty/libpng/CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/3rdparty/libpng/CMakeLists.txt b/3rdparty/libpng/CMakeLists.txt index 00366036df..d84072ad45 100644 --- a/3rdparty/libpng/CMakeLists.txt +++ b/3rdparty/libpng/CMakeLists.txt @@ -183,7 +183,7 @@ if(TARGET_ARCH MATCHES "^(loongarch)") endif() # Set definitions and sources for RISC-V. -if(PNG_TARGET_ARCHITECTURE MATCHES "^(riscv)") +if(TARGET_ARCH MATCHES "^(riscv)") include(CheckCCompilerFlag) set(PNG_RISCV_RVV_POSSIBLE_VALUES on off) set(PNG_RISCV_RVV "on" @@ -194,7 +194,6 @@ if(PNG_TARGET_ARCHITECTURE MATCHES "^(riscv)") 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() { @@ -247,7 +246,7 @@ if(TARGET_ARCH MATCHES "^(loongarch)") endif() # Set definitions and sources for RISC-V. -if(PNG_TARGET_ARCHITECTURE MATCHES "^(riscv)") +if(TARGET_ARCH MATCHES "^(riscv)") add_definitions(-DPNG_RISCV_RVV_OPT=0) endif() From 71d49c2dc2c1313ccdf0fa026b846669d610956a Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 4 Dec 2025 14:53:33 +0300 Subject: [PATCH 012/103] Disabled incorrect in-place flip HAL on RISC-V RVV. --- hal/riscv-rvv/src/core/flip.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hal/riscv-rvv/src/core/flip.cpp b/hal/riscv-rvv/src/core/flip.cpp index 42f7c8b16d..0af8458ccf 100644 --- a/hal/riscv-rvv/src/core/flip.cpp +++ b/hal/riscv-rvv/src/core/flip.cpp @@ -322,8 +322,10 @@ int flip(int src_type, const uchar* src_data, size_t src_step, int src_width, in if (src_width < 0 || src_height < 0 || esz > 32) return CV_HAL_ERROR_NOT_IMPLEMENTED; + // BUG: https://github.com/opencv/opencv/issues/28124 if (src_data == dst_data) { - return flip_inplace(esz, dst_data, dst_step, src_width, src_height, flip_mode); + return CV_HAL_ERROR_NOT_IMPLEMENTED; + //return flip_inplace(esz, dst_data, dst_step, src_width, src_height, flip_mode); } if (flip_mode == 0) From e5075ffa33cc4ea0e9f3d51c15085d3af6963bbb Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 4 Dec 2025 14:53:33 +0300 Subject: [PATCH 013/103] Disabled incorrect in-place flip HAL on RISC-V RVV. --- hal/riscv-rvv/src/core/flip.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hal/riscv-rvv/src/core/flip.cpp b/hal/riscv-rvv/src/core/flip.cpp index 42f7c8b16d..0af8458ccf 100644 --- a/hal/riscv-rvv/src/core/flip.cpp +++ b/hal/riscv-rvv/src/core/flip.cpp @@ -322,8 +322,10 @@ int flip(int src_type, const uchar* src_data, size_t src_step, int src_width, in if (src_width < 0 || src_height < 0 || esz > 32) return CV_HAL_ERROR_NOT_IMPLEMENTED; + // BUG: https://github.com/opencv/opencv/issues/28124 if (src_data == dst_data) { - return flip_inplace(esz, dst_data, dst_step, src_width, src_height, flip_mode); + return CV_HAL_ERROR_NOT_IMPLEMENTED; + //return flip_inplace(esz, dst_data, dst_step, src_width, src_height, flip_mode); } if (flip_mode == 0) From e669a955c7d3f455f8c004b8586301c44b8c4d60 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 4 Dec 2025 16:18:45 +0300 Subject: [PATCH 014/103] Disabled risc-v rvv of png_read_filter_row_paeth in libpng. --- .../patches/riscv_rvv_issue_28126.patch | 20 +++++++++++++++++++ 3rdparty/libpng/riscv/riscv_init.c | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 3rdparty/libpng/patches/riscv_rvv_issue_28126.patch diff --git a/3rdparty/libpng/patches/riscv_rvv_issue_28126.patch b/3rdparty/libpng/patches/riscv_rvv_issue_28126.patch new file mode 100644 index 0000000000..12f8e3a38e --- /dev/null +++ b/3rdparty/libpng/patches/riscv_rvv_issue_28126.patch @@ -0,0 +1,20 @@ +diff --git a/3rdparty/libpng/riscv/riscv_init.c b/3rdparty/libpng/riscv/riscv_init.c +index 36a41b54f7..f2796ebccb 100644 +--- a/3rdparty/libpng/riscv/riscv_init.c ++++ b/3rdparty/libpng/riscv/riscv_init.c +@@ -30,13 +30,13 @@ png_init_filter_functions_rvv(png_structp pp, unsigned int bpp) + 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_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_PAETH-1] = png_read_filter_row_paeth4_rvv; + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_rvv; + } + } diff --git a/3rdparty/libpng/riscv/riscv_init.c b/3rdparty/libpng/riscv/riscv_init.c index 36a41b54f7..f2796ebccb 100644 --- a/3rdparty/libpng/riscv/riscv_init.c +++ b/3rdparty/libpng/riscv/riscv_init.c @@ -30,13 +30,13 @@ png_init_filter_functions_rvv(png_structp pp, unsigned int bpp) 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_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_PAETH-1] = png_read_filter_row_paeth4_rvv; pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_rvv; } } From ab23a89975e65132e3437b72e7d4b0bb3ce438bb Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 4 Dec 2025 18:07:21 +0300 Subject: [PATCH 015/103] Set PNG simd configuration defaults accornding to OpenCV settings. --- 3rdparty/libpng/CMakeLists.txt | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/3rdparty/libpng/CMakeLists.txt b/3rdparty/libpng/CMakeLists.txt index d84072ad45..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}) @@ -184,9 +199,14 @@ 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 "on" + 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}) From d88f080b7ecb988295568718f5f69d2eabc75a24 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 5 Dec 2025 07:52:26 +0300 Subject: [PATCH 016/103] Update to libpng 1.6.52 with RISC-V RVV fix. --- .../patches/riscv_rvv_issue_28126.patch | 20 ------ 3rdparty/libpng/png.c | 4 +- 3rdparty/libpng/png.h | 14 ++-- 3rdparty/libpng/pngconf.h | 2 +- 3rdparty/libpng/pngmem.c | 39 ++++++----- 3rdparty/libpng/pngread.c | 51 +++++++++++---- 3rdparty/libpng/pngrtran.c | 1 + 3rdparty/libpng/riscv/filter_rvv_intrinsics.c | 65 +++++++------------ 3rdparty/libpng/riscv/riscv_init.c | 4 +- 9 files changed, 92 insertions(+), 108 deletions(-) delete mode 100644 3rdparty/libpng/patches/riscv_rvv_issue_28126.patch diff --git a/3rdparty/libpng/patches/riscv_rvv_issue_28126.patch b/3rdparty/libpng/patches/riscv_rvv_issue_28126.patch deleted file mode 100644 index 12f8e3a38e..0000000000 --- a/3rdparty/libpng/patches/riscv_rvv_issue_28126.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/3rdparty/libpng/riscv/riscv_init.c b/3rdparty/libpng/riscv/riscv_init.c -index 36a41b54f7..f2796ebccb 100644 ---- a/3rdparty/libpng/riscv/riscv_init.c -+++ b/3rdparty/libpng/riscv/riscv_init.c -@@ -30,13 +30,13 @@ png_init_filter_functions_rvv(png_structp pp, unsigned int bpp) - 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_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_PAETH-1] = png_read_filter_row_paeth4_rvv; - pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_rvv; - } - } diff --git a/3rdparty/libpng/png.c b/3rdparty/libpng/png.c index 380c4c19e6..11b65d1f13 100644 --- a/3rdparty/libpng/png.c +++ b/3rdparty/libpng/png.c @@ -13,7 +13,7 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_51 Your_png_h_is_not_version_1_6_51; +typedef png_libpng_version_1_6_52 Your_png_h_is_not_version_1_6_52; /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the * corresponding macro definitions. This causes a compile time failure if @@ -817,7 +817,7 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.51" PNG_STRING_NEWLINE \ + "libpng version 1.6.52" 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 \ diff --git a/3rdparty/libpng/png.h b/3rdparty/libpng/png.h index fb93d2242b..bceb9aa45d 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.51 + * libpng version 1.6.52 * * 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.51, November 2025: + * libpng versions 1.6.36, December 2018, through 1.6.52, 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.51 16 10651 16.so.16.51[.0] + * 1.6.52 16 10651 16.so.16.52[.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.51" +#define PNG_LIBPNG_VER_STRING "1.6.52" #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 51 +#define PNG_LIBPNG_VER_RELEASE 52 /* 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 10651 /* 1.6.51 */ +#define PNG_LIBPNG_VER 10652 /* 1.6.52 */ /* 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_51; +typedef char* png_libpng_version_1_6_52; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * diff --git a/3rdparty/libpng/pngconf.h b/3rdparty/libpng/pngconf.h index 981df68d87..76b5c20bdf 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.51 + * libpng version 1.6.52 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson 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/pngread.c b/3rdparty/libpng/pngread.c index 79917daaaf..f8ca2b7e31 100644 --- a/3rdparty/libpng/pngread.c +++ b/3rdparty/libpng/pngread.c @@ -3207,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) @@ -3263,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 GitHub Issue #764.) + */ + 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; diff --git a/3rdparty/libpng/pngrtran.c b/3rdparty/libpng/pngrtran.c index 2f52022551..507d11381e 100644 --- a/3rdparty/libpng/pngrtran.c +++ b/3rdparty/libpng/pngrtran.c @@ -1843,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) */ diff --git a/3rdparty/libpng/riscv/filter_rvv_intrinsics.c b/3rdparty/libpng/riscv/filter_rvv_intrinsics.c index a71e561bba..8d277d14cd 100644 --- a/3rdparty/libpng/riscv/filter_rvv_intrinsics.c +++ b/3rdparty/libpng/riscv/filter_rvv_intrinsics.c @@ -3,7 +3,8 @@ * Copyright (c) 2023 Google LLC * Written by Manfred SCHLAEGL, 2022 * Dragoș Tiselice , May 2023. - * Filip Wasil , March 2025. + * Filip Wasil , March 2025. + * Liang Junzhao , November 2025. * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer @@ -140,11 +141,8 @@ png_read_filter_row_avg_rvv(size_t len, size_t bpp, unsigned char* row, /* x = *row */ x = __riscv_vle8_v_u8m1(row, vl); - /* tmp = a + b */ - vuint16m2_t tmp = __riscv_vwaddu_vv_u16m2(a, b, vl); - - /* a = tmp/2 */ - a = __riscv_vnsrl_wx_u8m1(tmp, 1, vl); + /* a = (a + b) / 2, round to zero with vxrm = 2 */ + a = __riscv_vaaddu_wx_u8m1(a, b, 2, vl); /* a += x */ a = __riscv_vadd_vv_u8m1(a, x, vl); @@ -265,27 +263,22 @@ png_read_filter_row_paeth_rvv(size_t len, size_t bpp, unsigned char* row, /* x = *row */ vuint8m1_t x = __riscv_vle8_v_u8m1(row, vl); - /* Calculate p = b - c and pc = a - c using widening subtraction */ - vuint16m2_t p_wide = __riscv_vwsubu_vv_u16m2(b, c, vl); - vuint16m2_t pc_wide = __riscv_vwsubu_vv_u16m2(a, c, vl); - - /* Convert to signed for easier manipulation */ - size_t vl16 = __riscv_vsetvl_e16m2(bpp); - vint16m2_t p = __riscv_vreinterpret_v_u16m2_i16m2(p_wide); - vint16m2_t pc = __riscv_vreinterpret_v_u16m2_i16m2(pc_wide); + /* 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| */ - vbool8_t p_neg_mask = __riscv_vmslt_vx_i16m2_b8(p, 0, vl16); - vint16m2_t pa = __riscv_vrsub_vx_i16m2_m(p_neg_mask, p, 0, vl16); + vuint16m2_t tmp = __riscv_vrsub_vx_u16m2(p, 0, vl); + vuint16m2_t pa = __riscv_vminu_vv_u16m2(p, tmp, vl); /* pb = |pc| */ - vbool8_t pc_neg_mask = __riscv_vmslt_vx_i16m2_b8(pc, 0, vl16); - vint16m2_t pb = __riscv_vrsub_vx_i16m2_m(pc_neg_mask, pc, 0, vl16); + tmp = __riscv_vrsub_vx_u16m2(pc, 0, vl); + vuint16m2_t pb = __riscv_vminu_vv_u16m2(pc, tmp, vl); /* pc = |p + pc| */ - vint16m2_t p_plus_pc = __riscv_vadd_vv_i16m2(p, pc, vl16); - vbool8_t p_plus_pc_neg_mask = __riscv_vmslt_vx_i16m2_b8(p_plus_pc, 0, vl16); - pc = __riscv_vrsub_vx_i16m2_m(p_plus_pc_neg_mask, p_plus_pc, 0, vl16); + 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. @@ -294,31 +287,17 @@ png_read_filter_row_paeth_rvv(size_t len, size_t bpp, unsigned char* row, * - Else use c */ - /* Find which predictor to use based on minimum absolute difference */ - vbool8_t pa_le_pb = __riscv_vmsle_vv_i16m2_b8(pa, pb, vl16); - vbool8_t pa_le_pc = __riscv_vmsle_vv_i16m2_b8(pa, pc, vl16); - vbool8_t pb_le_pc = __riscv_vmsle_vv_i16m2_b8(pb, pc, vl16); + /* 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); - /* use_a = pa <= pb && pa <= pc */ - vbool8_t use_a = __riscv_vmand_mm_b8(pa_le_pb, pa_le_pc, vl16); - - /* use_b = !use_a && pb <= pc */ - vbool8_t not_use_a = __riscv_vmnot_m_b8(use_a, vl16); - vbool8_t use_b = __riscv_vmand_mm_b8(not_use_a, pb_le_pc, vl16); - - /* Switch back to e8m1 for final operations */ - vl = __riscv_vsetvl_e8m1(bpp); - - /* Start with a, then conditionally replace with b or c */ - vuint8m1_t result = a; - result = __riscv_vmerge_vvm_u8m1(result, b, use_b, vl); - - /* use_c = !use_a && !use_b */ - vbool8_t use_c = __riscv_vmnand_mm_b8(__riscv_vmor_mm_b8(use_a, use_b, vl), __riscv_vmor_mm_b8(use_a, use_b, vl), vl); - result = __riscv_vmerge_vvm_u8m1(result, c, use_c, 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(result, x, vl); + a = __riscv_vadd_vv_u8m1(a, x, vl); /* *row = a */ __riscv_vse8_v_u8m1(row, a, vl); diff --git a/3rdparty/libpng/riscv/riscv_init.c b/3rdparty/libpng/riscv/riscv_init.c index f2796ebccb..36a41b54f7 100644 --- a/3rdparty/libpng/riscv/riscv_init.c +++ b/3rdparty/libpng/riscv/riscv_init.c @@ -30,13 +30,13 @@ png_init_filter_functions_rvv(png_structp pp, unsigned int bpp) 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_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_PAETH-1] = png_read_filter_row_paeth4_rvv; pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_rvv; } } From 98d065370c54ce9d101a05cc2be8a15114bbb882 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 5 Dec 2025 08:08:36 +0300 Subject: [PATCH 017/103] Build fix for RISC-V RVV. --- 3rdparty/libpng/patches/riscv_rvv_fix.patch | 13 +++++++++++++ 3rdparty/libpng/riscv/filter_rvv_intrinsics.c | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 3rdparty/libpng/patches/riscv_rvv_fix.patch 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/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); From 92ea2c324b9b033fd7329bacad1037ac3edeab31 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 5 Dec 2025 08:45:32 +0300 Subject: [PATCH 018/103] Fixed unicode tracing symbols with QT. --- modules/world/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) 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() From fd11f635c17804f503ccec88f78f769ad663e523 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:13:19 +0300 Subject: [PATCH 019/103] Merge pull request #28122 from asmorkalov:as/dynamic_openmp Dropped OPENCV_FOR_OPENMP_DYNAMIC_DISABLE environment varibale in favor of standard OMP_DYNAMIC #28122 Fixes: https://github.com/opencv/opencv/issues/25717 Replaces: https://github.com/opencv/opencv/pull/28084 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../introduction/env_reference/env_reference.markdown | 2 +- modules/core/src/parallel.cpp | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/doc/tutorials/introduction/env_reference/env_reference.markdown b/doc/tutorials/introduction/env_reference/env_reference.markdown index d4e2881f42..c370ab8d6e 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/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 09c86e94d2..3aa72f9f44 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(); From de660aedcad310f6be284309c87cd1e7ef6852f3 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 5 Dec 2025 16:00:14 +0300 Subject: [PATCH 020/103] Reverted CI changes. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f71fe9be11..81f82e9558 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,7 +196,7 @@ set(BUILD_LIST "" CACHE STRING "Build only listed modules (comma-separated, e.g. OCV_OPTION(OPENCV_ENABLE_NONFREE "Enable non-free algorithms" OFF) # 3rd party libs -OCV_OPTION(OPENCV_FORCE_3RDPARTY_BUILD "Force using 3rdparty code from source" ON) +OCV_OPTION(OPENCV_FORCE_3RDPARTY_BUILD "Force using 3rdparty code from source" OFF) OCV_OPTION(BUILD_ZLIB "Build zlib from source" (WIN32 OR APPLE OR OPENCV_FORCE_3RDPARTY_BUILD) ) OCV_OPTION(BUILD_TIFF "Build libtiff from source" (WIN32 OR ANDROID OR APPLE OR OPENCV_FORCE_3RDPARTY_BUILD) ) OCV_OPTION(BUILD_OPENJPEG "Build OpenJPEG from source" (WIN32 OR ANDROID OR APPLE OR OPENCV_FORCE_3RDPARTY_BUILD) ) From 29f44b45302b7e01c7cfe390ae2ef277ede16494 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Sat, 6 Dec 2025 08:33:27 +0900 Subject: [PATCH 021/103] chore: Synchronize 3rdparty libpng version to 1.6.52 in README/CHANGES --- 3rdparty/libpng/CHANGES | 86 +++++++++++++++++++++++++++++++++++++++++ 3rdparty/libpng/README | 4 +- 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/3rdparty/libpng/CHANGES b/3rdparty/libpng/CHANGES index 3f37f3f393..f8ad74bbdf 100644 --- a/3rdparty/libpng/CHANGES +++ b/3rdparty/libpng/CHANGES @@ -6229,6 +6229,92 @@ 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.) + 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/README b/3rdparty/libpng/README index 1f73eb58cf..87e5f8b177 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.52 ================================ 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 From 6fc0f38e1ca3593f55cc0f24c30f4069c62b35ce Mon Sep 17 00:00:00 2001 From: Kai Pastor Date: Sat, 6 Dec 2025 11:03:53 +0100 Subject: [PATCH 022/103] Fix CMake foreach loops --- cmake/OpenCVUtils.cmake | 2 +- modules/js/generator/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index 5886f4f3cb..5ca7fab808 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -1667,7 +1667,7 @@ function(ocv_install_used_external_targets) if(NOT BUILD_SHARED_LIBS AND NOT (CMAKE_VERSION VERSION_LESS "3.13.0") # upgrade CMake: https://gitlab.kitware.com/cmake/cmake/-/merge_requests/2152 ) - 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/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) From 5a0679b86241993fe16eca60539ef855fa4b0afe Mon Sep 17 00:00:00 2001 From: Ghazi-raad Date: Sat, 6 Dec 2025 12:45:48 +0000 Subject: [PATCH 023/103] Fix null pointer dereference in G-API stateful kernels Fixes #28095 Problem: - Stateful kernels dereference null state pointer on line 446 in gcpukernel.hpp - jinboson confirmed state_ptr is null on x86 Ubuntu (7 hours ago) - Causes crash with std::shared_ptr assertion on LoongArch64 and strict platforms Solution (addressing Copilot review feedback from PR #28096): 1. Added null pointer check using CV_Error instead of CV_Assert: - CV_Assert with && 'message' doesn't display the message correctly - CV_Error properly reports cv::Error::StsNullPtr with clear message 2. Fixed test kernels to properly initialize state using std::make_shared: - GOCVStInvalidResize: Initialize state in setup() - GOCVCountStateSetups: Initialize state before incrementing counter - Used std::make_shared() for modern C++ best practice Impact: - Prevents crashes on platforms with strict null pointer checking - Provides actionable error message for developers - Fixes StatefulKernel.StateInitOnceInRegularMode and InvalidReallocatingKernel tests --- modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp | 7 ++++++- modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp | 9 ++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp index eb5f784747..2763d17eaf 100644 --- a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp +++ b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp @@ -443,7 +443,12 @@ struct OCVStCallHelper, std::tuple> : template static void call_impl(GCPUContext &ctx, detail::Seq, detail::Seq) { - auto& st = *ctx.state().get>(); + auto state_ptr = ctx.state().get>(); + if (state_ptr == nullptr) { + CV_Error(cv::Error::StsNullPtr, "Stateful kernel's state is not initialized. " + "Make sure the setup() function properly initializes the state."); + } + auto& st = *state_ptr; call_and_postprocess::get(ctx, IIs))...> ::call(st, get_in::get(ctx, IIs)..., get_out::get(ctx, OIs)...); } diff --git a/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp b/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp index b9985e1377..1ee419aa2c 100644 --- a/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp +++ b/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp @@ -103,8 +103,10 @@ namespace GAPI_OCV_KERNEL_ST(GOCVStInvalidResize, GStInvalidResize, int) { static void setup(const cv::GMatDesc, cv::Size, double, double, int, - std::shared_ptr &/* state */) - { } + std::shared_ptr &state) + { + state = std::make_shared(); + } static void run(const cv::Mat& in, cv::Size sz, double fx, double fy, int interp, cv::Mat &out, int& /* state */) @@ -150,9 +152,10 @@ namespace GAPI_OCV_KERNEL_ST(GOCVCountStateSetups, GCountStateSetups, int) { - static void setup(const cv::GMatDesc &, std::shared_ptr &, + static void setup(const cv::GMatDesc &, std::shared_ptr &state, const cv::GCompileArgs &compileArgs) { + state = std::make_shared(); auto params = cv::gapi::getCompileArg(compileArgs) .value_or(CountStateSetupsParams { }); if (params.pSetupsCount != nullptr) { From 7971cbc7d0d1842263a9e7bed972154efb58b5c4 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Sun, 7 Dec 2025 00:33:02 +0900 Subject: [PATCH 024/103] doc: add supported src type info for each ColorConversionCodes --- modules/imgproc/include/opencv2/imgproc.hpp | 428 ++++++++++---------- 1 file changed, 217 insertions(+), 211 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 17d7d50b58..35420b6f53 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -533,149 +533,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 @@ -684,8 +688,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 @@ -693,35 +697,35 @@ 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_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 @@ -730,144 +734,144 @@ enum ColorConversionCodes { 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 @@ -3778,6 +3782,7 @@ floating-point. channels is derived automatically from src and code. @param hint Implementation modfication 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 ); @@ -3831,6 +3836,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); From 7a1425ca2c718faacd4311f4e9ac693ca9f56230 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Sun, 7 Dec 2025 11:38:29 +0300 Subject: [PATCH 025/103] Added Orbbec cameras support to CMake diagnostics. --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81f82e9558..167ce19578 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1721,6 +1721,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) From 201db16aa224507ecf58c47411c5c90e2c0f50e6 Mon Sep 17 00:00:00 2001 From: Parth Parekh Date: Sun, 7 Dec 2025 14:25:56 +0530 Subject: [PATCH 026/103] Document boolean return value of imwrite --- modules/imgcodecs/include/opencv2/imgcodecs.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 7bb14f9c7e..bad9f006b5 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -556,6 +556,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()); From 6460332780d15b2876940545537bb6667998806e Mon Sep 17 00:00:00 2001 From: ramukhsuya Date: Sun, 7 Dec 2025 19:35:25 +0530 Subject: [PATCH 027/103] Docs: Fix CAP_PROP_CONVERT_RGB description to BGR (Fixes #22697) --- modules/videoio/include/opencv2/videoio.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index 2457703f78..7688718c47 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -156,7 +156,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). From bd54424f03ac541be7ec54aed89b85a007838f6d Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Mon, 8 Dec 2025 08:59:51 +0300 Subject: [PATCH 028/103] Merge pull request #28140 from asmorkalov:as/openjpeg_2.5.4 OpenJPEG update to 2.5.4 #28140 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/openjpeg/openjp2/jp2.c | 6 ++++++ 3rdparty/openjpeg/openjp2/libopenjp2.pc.cmake.in | 2 +- 3rdparty/openjpeg/openjp2/openjpeg.h | 5 ++++- 3 files changed, 11 insertions(+), 2 deletions(-) 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 From 1288aace987652d6c0d002a9d6fa351b775e225c Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 8 Dec 2025 10:34:20 +0300 Subject: [PATCH 029/103] libpng update to version 1.6.53. --- 3rdparty/libpng/CHANGES | 6 +++ 3rdparty/libpng/README | 2 +- 3rdparty/libpng/png.c | 4 +- 3rdparty/libpng/png.h | 14 +++--- 3rdparty/libpng/pngconf.h | 2 +- 3rdparty/libpng/pngread.c | 2 +- 3rdparty/libpng/riscv/filter_rvv_intrinsics.c | 49 ++----------------- 7 files changed, 23 insertions(+), 56 deletions(-) diff --git a/3rdparty/libpng/CHANGES b/3rdparty/libpng/CHANGES index f8ad74bbdf..ea43101538 100644 --- a/3rdparty/libpng/CHANGES +++ b/3rdparty/libpng/CHANGES @@ -6315,6 +6315,12 @@ Version 1.6.52 [December 3, 2025] 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/README b/3rdparty/libpng/README index 87e5f8b177..4041ad86eb 100644 --- a/3rdparty/libpng/README +++ b/3rdparty/libpng/README @@ -1,4 +1,4 @@ -README for libpng version 1.6.52 +README for libpng version 1.6.53 ================================ See the note about version numbers near the top of `png.h`. diff --git a/3rdparty/libpng/png.c b/3rdparty/libpng/png.c index 11b65d1f13..85b4949652 100644 --- a/3rdparty/libpng/png.c +++ b/3rdparty/libpng/png.c @@ -13,7 +13,7 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_52 Your_png_h_is_not_version_1_6_52; +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 @@ -817,7 +817,7 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.52" 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 \ diff --git a/3rdparty/libpng/png.h b/3rdparty/libpng/png.h index bceb9aa45d..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.52 + * 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.52, December 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.52 16 10651 16.so.16.52[.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.52" +#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 52 +#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 10652 /* 1.6.52 */ +#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_52; +typedef char* png_libpng_version_1_6_53; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * diff --git a/3rdparty/libpng/pngconf.h b/3rdparty/libpng/pngconf.h index 76b5c20bdf..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.52 + * libpng version 1.6.53 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson diff --git a/3rdparty/libpng/pngread.c b/3rdparty/libpng/pngread.c index f8ca2b7e31..13a93deedc 100644 --- a/3rdparty/libpng/pngread.c +++ b/3rdparty/libpng/pngread.c @@ -3278,7 +3278,7 @@ png_image_read_composite(png_voidp argument) /* Clamp to the valid range to defend against * unforeseen cases where the data might be sRGB * instead of linear premultiplied. - * (Belt-and-suspenders for GitHub Issue #764.) + * (Belt-and-suspenders for CVE-2025-66293.) */ if (component > 255*65535) component = 255*65535; diff --git a/3rdparty/libpng/riscv/filter_rvv_intrinsics.c b/3rdparty/libpng/riscv/filter_rvv_intrinsics.c index 7e61fb89fb..b41cf9d400 100644 --- a/3rdparty/libpng/riscv/filter_rvv_intrinsics.c +++ b/3rdparty/libpng/riscv/filter_rvv_intrinsics.c @@ -2,9 +2,11 @@ * * Copyright (c) 2023 Google LLC * Written by Manfred SCHLAEGL, 2022 - * Dragoș Tiselice , May 2023. - * Filip Wasil , March 2025. - * Liang Junzhao , November 2025. + * 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 @@ -175,47 +177,6 @@ png_read_filter_row_avg4_rvv(png_row_infop row_info, png_bytep row, PNG_UNUSED(prev_row) } -#define MIN_CHUNK_LEN 256 -#define MAX_CHUNK_LEN 2048 - -static inline vuint8m1_t -prefix_sum(vuint8m1_t chunk, unsigned char* carry, size_t vl, - size_t max_chunk_len) -{ - size_t r; - - for (r = 1; r < MIN_CHUNK_LEN; r <<= 1) - { - vbool8_t shift_mask = __riscv_vmsgeu_vx_u8m1_b8(__riscv_vid_v_u8m1(vl), r, vl); - chunk = __riscv_vadd_vv_u8m1_mu(shift_mask, chunk, chunk, __riscv_vslideup_vx_u8m1(__riscv_vundefined_u8m1(), chunk, r, vl), vl); - } - - for (r = MIN_CHUNK_LEN; r < MAX_CHUNK_LEN && r < max_chunk_len; r <<= 1) - { - vbool8_t shift_mask = __riscv_vmsgeu_vx_u8m1_b8(__riscv_vid_v_u8m1(vl), r, vl); - chunk = __riscv_vadd_vv_u8m1_mu(shift_mask, chunk, chunk, __riscv_vslideup_vx_u8m1(__riscv_vundefined_u8m1(), chunk, r, vl), vl); - } - - chunk = __riscv_vadd_vx_u8m1(chunk, *carry, vl); - *carry = __riscv_vmv_x_s_u8m1_u8(__riscv_vslidedown_vx_u8m1(chunk, vl - 1, vl)); - - return chunk; -} - -static inline vint16m1_t -abs_diff(vuint16m1_t a, vuint16m1_t b, size_t vl) -{ - vint16m1_t diff = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vsub_vv_u16m1(a, b, vl)); - vbool16_t mask = __riscv_vmslt_vx_i16m1_b16(diff, 0, vl); - return __riscv_vrsub_vx_i16m1_m(mask, diff, 0, vl); -} - -static inline vint16m1_t -abs_sum(vint16m1_t a, vint16m1_t b, size_t vl) -{ - return __riscv_vadd_vv_i16m1(a, b, vl); -} - static inline void png_read_filter_row_paeth_rvv(size_t len, size_t bpp, unsigned char* row, const unsigned char* prev) From c6220c2b47dd463f1cfa90db8c77d0389e5fff9e Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Mon, 8 Dec 2025 08:53:54 +0100 Subject: [PATCH 030/103] Add missing combaseapi.h include. This is needed for CoCreateGuid. --- modules/core/src/system.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 98971d57eb..ef9facd4f1 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -178,6 +178,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 From 4d7ce375fc76a5ce51b244866e1f67b5621ef4e9 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Mon, 8 Dec 2025 09:44:02 +0100 Subject: [PATCH 031/103] Increase minAreaRect accuracy Just keep doubles all the way and avoid arithmetic with CV_PI/2 --- modules/imgproc/src/rotcalipers.cpp | 10 +++++----- modules/imgproc/test/test_convhull.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/imgproc/src/rotcalipers.cpp b/modules/imgproc/src/rotcalipers.cpp index d906258650..e28058b199 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/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp index 774bee7af6..24c1604d2a 100644 --- a/modules/imgproc/test/test_convhull.cpp +++ b/modules/imgproc/test/test_convhull.cpp @@ -1279,7 +1279,7 @@ 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) )); From 01f2f3c4b9aab357ffb86e641dd5bd2b8de2bd67 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Mon, 8 Dec 2025 21:53:53 +0100 Subject: [PATCH 032/103] Disable MSAn for lapack_LU One of the arguments is sent to Fortran so the whole function has to be disabled fro MSAN. Arguments cannot be unpoisoned, cf https://g3doc.corp.google.com/testing/msan/g3doc/index.md?cl=head#memorysanitizer-api --- modules/core/src/hal_internal.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/core/src/hal_internal.cpp b/modules/core/src/hal_internal.cpp index 377c688015..f3f23a3b3c 100644 --- a/modules/core/src/hal_internal.cpp +++ b/modules/core/src/hal_internal.cpp @@ -69,10 +69,12 @@ #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 //lapack stores matrices in column-major order so transposing is needed everywhere @@ -108,8 +110,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); From 498853d9965b20993909c64586e485bc6d7c043a Mon Sep 17 00:00:00 2001 From: shubham khatri Date: Tue, 9 Dec 2025 14:35:55 +0530 Subject: [PATCH 033/103] Merge pull request #28143 from Shubh3155:fix-js-ptr-factory-namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix JS bindings for namespaced Ptr factory return types #28143 This PR fixes an issue in the JS bindings generator for factory functions returning cv::Ptr where T belongs to a namespaced class (for example cv::ximgproc::EdgeDrawing). The generator previously produced unqualified C++ template arguments such as: .constructor(select_overload()>(&cv::ximgproc::createEdgeDrawing)) This results in invalid C++ because EdgeDrawing is not found in the global namespace. Fixes https://github.com/opencv/opencv/issues/28130 In modules/js/generator/embindgen.py, inside both: gen_function_binding_with_wrapper gen_function_binding a check is added: When factory == True, And the return type begins with Ptr<...>, And the inner type is missing a namespace (::), Ptr → Ptr This ensures the fully-qualified class name (e.g. cv::ximgproc::EdgeDrawing) is used in the generated bindings. .constructor(select_overload()>(&cv::ximgproc::createEdgeDrawing)) Configured OpenCV with: cmake .. -DBUILD_opencv_js=ON Ran: make -j gen_opencv_js_source JS generator completed successfully without errors. This change does not modify generated files directly — it modifies the generator logic so the correct namespace is applied automatically. --- modules/js/generator/embindgen.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) 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: From 02628f0188a9cc9e6a2fa0de5305d81f2bb653c6 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 9 Dec 2025 12:09:41 +0300 Subject: [PATCH 034/103] Enabled Python test for CUDA BufferPool. --- modules/python/test/test_cuda.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/python/test/test_cuda.py b/modules/python/test/test_cuda.py index c41bfc957d..ec8c03b230 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) From 2bf4f5c151eef7e786f5903eff4a010e800bf288 Mon Sep 17 00:00:00 2001 From: Skreg <85214856+shyama7004@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:01:53 +0530 Subject: [PATCH 035/103] Merge pull request #26366 from shyama7004:fix-orb.cpp Fix ORB inconsistency for masks with values 255 and 1. #26366 ### Pull Request Readiness Checklist The PR fixes : [25974](https://github.com/opencv/opencv/issues/25974) See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/features2d/src/orb.cpp | 8 ++++++-- modules/features2d/test/test_orb.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/modules/features2d/src/orb.cpp b/modules/features2d/src/orb.cpp index b0d32002a1..f970818377 100644 --- a/modules/features2d/src/orb.cpp +++ b/modules/features2d/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/features2d/test/test_orb.cpp b/modules/features2d/test/test_orb.cpp index 89e2f7d78b..92fe8e9838 100644 --- a/modules/features2d/test/test_orb.cpp +++ b/modules/features2d/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 From f704001eb7f4a382eb6ba6cf93459b2c3ed9e6cd Mon Sep 17 00:00:00 2001 From: Ghazi-raad Date: Tue, 9 Dec 2025 12:59:24 +0000 Subject: [PATCH 036/103] Merge pull request #28120 from Ghazi-raad:test/line-connectivity-26413 Test: Add regression test for LINE_4 vs LINE_8 connectivity #28120 Add test to verify correct behavior of LINE_4 (4-connected) and LINE_8 (8-connected) line drawing. This test ensures: - LINE_4 produces staircase pattern (more pixels) for diagonal lines - LINE_8 produces diagonal steps (fewer pixels) - LINE_4 pixels have only horizontal/vertical neighbors (no diagonal-only) Regression test for issue #26413 where LINE_4 and LINE_8 behaviors were swapped. --- modules/imgproc/test/test_drawing.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/modules/imgproc/test/test_drawing.cpp b/modules/imgproc/test/test_drawing.cpp index 50c146baad..da984058e3 100644 --- a/modules/imgproc/test/test_drawing.cpp +++ b/modules/imgproc/test/test_drawing.cpp @@ -1104,4 +1104,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 From 334b7146f5ebfc204557893a6757cec80146e907 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Wed, 10 Dec 2025 15:20:08 +0800 Subject: [PATCH 037/103] Merge pull request #28157 from dkurt:normalize_vec_qrdetect Remove floating point arithmetic from angle computation in QR codes #28157 ### Pull Request Readiness Checklist resolves https://github.com/opencv/opencv/issues/24646 See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/objdetect/src/qrcode.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index f79829eb7c..7160bbf720 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -83,8 +83,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)); } @@ -825,7 +827,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) @@ -863,7 +869,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) From dee619b628e86d187bbe7a55b5fe6c7bfbb8b64d Mon Sep 17 00:00:00 2001 From: Stefania Hergane Date: Wed, 10 Dec 2025 16:48:35 +0200 Subject: [PATCH 038/103] Merge pull request #28127 from StefaniaHergane:hs/deprecated_ov_element_undefined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update deprecated ov::element::undefined #28127 ### Summary Fixing OpenCV build error below. Relates to OpenVINO 2026.0 updates on `ov::element::undefined` (https://github.com/openvinotoolkit/openvino/pull/32573) - replaced by `ov::element::dynamic`. ``` /home/jenkins/agent/workspace/openVINO-builder/opencv_source/opencv-opencv-f627368/modules/gapi/src/backends/ov/govbackend.cpp [2025-12-03T21:25:54.540Z] /home/jenkins/agent/workspace/openVINO-builder/opencv_source/opencv-opencv-f627368/modules/gapi/src/backends/ov/govbackend.cpp: In function ���ov::element::Type toOV(int)���: [2025-12-03T21:25:54.540Z] /home/jenkins/agent/workspace/openVINO-builder/opencv_source/opencv-opencv-f627368/modules/gapi/src/backends/ov/govbackend.cpp:114:25: error: ���undefined��� is not a member of ���ov::element��� [2025-12-03T21:25:54.540Z] 114 | return ov::element::undefined; ... /home/jenkins/agent/workspace/openVINO-builder/opencv_source/opencv-opencv-f627368/modules/gapi/test/infer/gapi_infer_ov_tests.cpp [2025-12-03T21:26:19.642Z] /home/jenkins/agent/workspace/openVINO-builder/opencv_source/opencv-opencv-f627368/modules/gapi/test/infer/gapi_infer_ov_tests.cpp: In function ���ov::element::Type opencv_test::toOV(int)���: [2025-12-03T21:26:19.642Z] /home/jenkins/agent/workspace/openVINO-builder/opencv_source/opencv-opencv-f627368/modules/gapi/test/infer/gapi_infer_ov_tests.cpp:832:25: error: ���undefined��� is not a member of ���ov::element��� [2025-12-03T21:26:19.642Z] 832 | return ov::element::undefined; ``` ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/gapi/src/backends/ov/govbackend.cpp | 2 +- modules/gapi/test/infer/gapi_infer_ov_tests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gapi/src/backends/ov/govbackend.cpp b/modules/gapi/src/backends/ov/govbackend.cpp index 6a787c03e9..15c216d716 100644 --- a/modules/gapi/src/backends/ov/govbackend.cpp +++ b/modules/gapi/src/backends/ov/govbackend.cpp @@ -111,7 +111,7 @@ static ov::element::Type toOV(int depth) { case CV_16F: return ov::element::f16; default: GAPI_Error("OV Backend: Unsupported data type"); } - return ov::element::undefined; + return ov::element::dynamic; } static ov::preprocess::ResizeAlgorithm toOVInterp(int interpolation) { diff --git a/modules/gapi/test/infer/gapi_infer_ov_tests.cpp b/modules/gapi/test/infer/gapi_infer_ov_tests.cpp index 85df0b24da..31fb3dd65a 100644 --- a/modules/gapi/test/infer/gapi_infer_ov_tests.cpp +++ b/modules/gapi/test/infer/gapi_infer_ov_tests.cpp @@ -829,7 +829,7 @@ static ov::element::Type toOV(int depth) { case CV_16F: return ov::element::f16; default: GAPI_Error("OV Backend: Unsupported data type"); } - return ov::element::undefined; + return ov::element::dynamic; } struct TestMeanScaleOV : public ::testing::TestWithParam{ From 579dfb6e025c62acb16b7b3e1fe953030df68d40 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:06:39 +0300 Subject: [PATCH 039/103] Merge pull request #27640 from asmorkalov:as/kleidicv_mac Enable KleidiCV on Linux and Mac Mx by default #27640 OpenCV Extra: https://github.com/opencv/opencv_extra/pull/1296 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- CMakeLists.txt | 2 +- modules/dnn/test/test_torch_importer.cpp | 2 +- modules/video/perf/perf_ecc.cpp | 11 ++++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 167ce19578..caf42ebc0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,7 +227,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) diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index f1d7521e7b..d27cda17b8 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -426,7 +426,7 @@ static void normAssertSegmentation(const Mat& ref, const Mat& test) Mat refMask = getSegmMask(ref); Mat testMask = getSegmMask(test); - EXPECT_EQ(countNonZero(refMask != testMask), 0); + EXPECT_LE(countNonZero(refMask != testMask), 2); } TEST_P(Test_Torch_nets, ENet_accuracy) 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 From 3eb143cc2238f73c5fb3df35340f7d2b89fb5dd9 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Sat, 13 Dec 2025 12:44:01 +0300 Subject: [PATCH 040/103] Merge pull request #28139 from asmorkalov:as/webp_1.6.0 WebP update to version 1.6.0 #28139 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/libwebp/CMakeLists.txt | 7 + 3rdparty/libwebp/patches/7233156.patch | 21 + 3rdparty/libwebp/sharpyuv/sharpyuv.c | 11 +- 3rdparty/libwebp/sharpyuv/sharpyuv.h | 19 +- 3rdparty/libwebp/sharpyuv/sharpyuv_csp.c | 24 +- 3rdparty/libwebp/sharpyuv/sharpyuv_csp.h | 5 + 3rdparty/libwebp/sharpyuv/sharpyuv_dsp.c | 1 + 3rdparty/libwebp/sharpyuv/sharpyuv_gamma.c | 1 + 3rdparty/libwebp/sharpyuv/sharpyuv_sse2.c | 6 +- 3rdparty/libwebp/src/dec/alpha_dec.c | 124 +- 3rdparty/libwebp/src/dec/alphai_dec.h | 25 +- 3rdparty/libwebp/src/dec/buffer_dec.c | 8 +- 3rdparty/libwebp/src/dec/common_dec.h | 3 + 3rdparty/libwebp/src/dec/frame_dec.c | 437 +++---- 3rdparty/libwebp/src/dec/idec_dec.c | 555 ++++----- 3rdparty/libwebp/src/dec/io_dec.c | 48 +- 3rdparty/libwebp/src/dec/quant_dec.c | 35 +- 3rdparty/libwebp/src/dec/tree_dec.c | 61 +- 3rdparty/libwebp/src/dec/vp8_dec.c | 287 ++--- 3rdparty/libwebp/src/dec/vp8_dec.h | 2 + 3rdparty/libwebp/src/dec/vp8i_dec.h | 208 ++-- 3rdparty/libwebp/src/dec/vp8l_dec.c | 498 ++++---- 3rdparty/libwebp/src/dec/vp8li_dec.h | 85 +- 3rdparty/libwebp/src/dec/webp_dec.c | 82 +- 3rdparty/libwebp/src/dec/webpi_dec.h | 5 +- 3rdparty/libwebp/src/demux/anim_decode.c | 142 +-- 3rdparty/libwebp/src/demux/demux.c | 428 +++---- 3rdparty/libwebp/src/dsp/alpha_processing.c | 4 + .../libwebp/src/dsp/alpha_processing_sse2.c | 58 +- .../libwebp/src/dsp/alpha_processing_sse41.c | 4 +- 3rdparty/libwebp/src/dsp/cost.c | 11 +- 3rdparty/libwebp/src/dsp/cost_mips32.c | 4 +- 3rdparty/libwebp/src/dsp/cost_neon.c | 4 +- 3rdparty/libwebp/src/dsp/cost_sse2.c | 8 +- 3rdparty/libwebp/src/dsp/cpu.c | 4 + 3rdparty/libwebp/src/dsp/cpu.h | 25 + 3rdparty/libwebp/src/dsp/dec.c | 54 +- 3rdparty/libwebp/src/dsp/dec_clip_tables.c | 2 + 3rdparty/libwebp/src/dsp/dec_mips32.c | 22 +- 3rdparty/libwebp/src/dsp/dec_mips_dsp_r2.c | 28 +- 3rdparty/libwebp/src/dsp/dec_msa.c | 29 +- 3rdparty/libwebp/src/dsp/dec_neon.c | 80 +- 3rdparty/libwebp/src/dsp/dec_sse2.c | 25 +- 3rdparty/libwebp/src/dsp/dec_sse41.c | 5 +- 3rdparty/libwebp/src/dsp/dsp.h | 153 ++- 3rdparty/libwebp/src/dsp/enc.c | 157 ++- 3rdparty/libwebp/src/dsp/enc_mips32.c | 50 +- 3rdparty/libwebp/src/dsp/enc_mips_dsp_r2.c | 111 +- 3rdparty/libwebp/src/dsp/enc_msa.c | 135 ++- 3rdparty/libwebp/src/dsp/enc_neon.c | 379 +++++- 3rdparty/libwebp/src/dsp/enc_sse2.c | 231 ++-- 3rdparty/libwebp/src/dsp/enc_sse41.c | 45 +- 3rdparty/libwebp/src/dsp/filters.c | 145 +-- .../libwebp/src/dsp/filters_mips_dsp_r2.c | 134 +-- 3rdparty/libwebp/src/dsp/filters_msa.c | 27 +- 3rdparty/libwebp/src/dsp/filters_neon.c | 123 +- 3rdparty/libwebp/src/dsp/filters_sse2.c | 120 +- 3rdparty/libwebp/src/dsp/lossless.c | 113 +- 3rdparty/libwebp/src/dsp/lossless.h | 120 +- 3rdparty/libwebp/src/dsp/lossless_avx2.c | 443 +++++++ 3rdparty/libwebp/src/dsp/lossless_common.h | 66 +- 3rdparty/libwebp/src/dsp/lossless_enc.c | 562 ++++----- 3rdparty/libwebp/src/dsp/lossless_enc_avx2.c | 736 ++++++++++++ .../libwebp/src/dsp/lossless_enc_mips32.c | 122 +- .../src/dsp/lossless_enc_mips_dsp_r2.c | 37 +- 3rdparty/libwebp/src/dsp/lossless_enc_msa.c | 10 +- 3rdparty/libwebp/src/dsp/lossless_enc_neon.c | 17 +- 3rdparty/libwebp/src/dsp/lossless_enc_sse2.c | 229 ++-- 3rdparty/libwebp/src/dsp/lossless_enc_sse41.c | 47 +- .../libwebp/src/dsp/lossless_mips_dsp_r2.c | 6 +- 3rdparty/libwebp/src/dsp/lossless_msa.c | 6 +- 3rdparty/libwebp/src/dsp/lossless_neon.c | 59 +- 3rdparty/libwebp/src/dsp/lossless_sse2.c | 58 +- 3rdparty/libwebp/src/dsp/lossless_sse41.c | 24 +- 3rdparty/libwebp/src/dsp/rescaler.c | 14 +- 3rdparty/libwebp/src/dsp/rescaler_mips32.c | 8 +- 3rdparty/libwebp/src/dsp/rescaler_msa.c | 27 +- 3rdparty/libwebp/src/dsp/rescaler_neon.c | 4 +- 3rdparty/libwebp/src/dsp/rescaler_sse2.c | 22 +- 3rdparty/libwebp/src/dsp/ssim.c | 2 + 3rdparty/libwebp/src/dsp/ssim_sse2.c | 6 +- 3rdparty/libwebp/src/dsp/upsampling.c | 44 +- .../libwebp/src/dsp/upsampling_mips_dsp_r2.c | 18 +- 3rdparty/libwebp/src/dsp/upsampling_msa.c | 55 +- 3rdparty/libwebp/src/dsp/upsampling_neon.c | 111 +- 3rdparty/libwebp/src/dsp/upsampling_sse2.c | 35 +- 3rdparty/libwebp/src/dsp/upsampling_sse41.c | 37 +- 3rdparty/libwebp/src/dsp/yuv.c | 56 +- 3rdparty/libwebp/src/dsp/yuv.h | 68 +- 3rdparty/libwebp/src/dsp/yuv_mips32.c | 7 +- 3rdparty/libwebp/src/dsp/yuv_mips_dsp_r2.c | 7 +- 3rdparty/libwebp/src/dsp/yuv_neon.c | 19 +- 3rdparty/libwebp/src/dsp/yuv_sse2.c | 138 ++- 3rdparty/libwebp/src/dsp/yuv_sse41.c | 74 +- 3rdparty/libwebp/src/enc/alpha_enc.c | 60 +- 3rdparty/libwebp/src/enc/analysis_enc.c | 129 +- .../src/enc/backward_references_cost_enc.c | 397 +++---- .../libwebp/src/enc/backward_references_enc.c | 181 ++- .../libwebp/src/enc/backward_references_enc.h | 29 +- 3rdparty/libwebp/src/enc/config_enc.c | 15 +- 3rdparty/libwebp/src/enc/cost_enc.c | 46 +- 3rdparty/libwebp/src/enc/cost_enc.h | 5 +- 3rdparty/libwebp/src/enc/filter_enc.c | 73 +- 3rdparty/libwebp/src/enc/frame_enc.c | 331 +++--- 3rdparty/libwebp/src/enc/histogram_enc.c | 1039 ++++++++--------- 3rdparty/libwebp/src/enc/histogram_enc.h | 59 +- 3rdparty/libwebp/src/enc/iterator_enc.c | 256 ++-- 3rdparty/libwebp/src/enc/near_lossless_enc.c | 5 +- 3rdparty/libwebp/src/enc/picture_csp_enc.c | 13 +- 3rdparty/libwebp/src/enc/picture_enc.c | 7 +- 3rdparty/libwebp/src/enc/picture_psnr_enc.c | 1 + .../libwebp/src/enc/picture_rescale_enc.c | 2 + 3rdparty/libwebp/src/enc/picture_tools_enc.c | 7 +- 3rdparty/libwebp/src/enc/predictor_enc.c | 667 ++++++++--- 3rdparty/libwebp/src/enc/quant_enc.c | 444 +++---- 3rdparty/libwebp/src/enc/syntax_enc.c | 152 +-- 3rdparty/libwebp/src/enc/token_enc.c | 77 +- 3rdparty/libwebp/src/enc/tree_enc.c | 44 +- 3rdparty/libwebp/src/enc/vp8i_enc.h | 274 ++--- 3rdparty/libwebp/src/enc/vp8l_enc.c | 709 +++++------ 3rdparty/libwebp/src/enc/vp8li_enc.h | 74 +- 3rdparty/libwebp/src/enc/webp_enc.c | 136 +-- 3rdparty/libwebp/src/mux/anim_encode.c | 753 ++++++------ 3rdparty/libwebp/src/mux/muxedit.c | 184 +-- 3rdparty/libwebp/src/mux/muxi.h | 56 +- 3rdparty/libwebp/src/mux/muxinternal.c | 144 +-- 3rdparty/libwebp/src/mux/muxread.c | 165 +-- .../libwebp/src/utils/bit_reader_inl_utils.h | 71 +- 3rdparty/libwebp/src/utils/bit_reader_utils.c | 121 +- 3rdparty/libwebp/src/utils/bit_reader_utils.h | 66 +- 3rdparty/libwebp/src/utils/bit_writer_utils.c | 213 ++-- 3rdparty/libwebp/src/utils/bit_writer_utils.h | 50 +- .../libwebp/src/utils/color_cache_utils.c | 22 +- .../libwebp/src/utils/color_cache_utils.h | 25 +- 3rdparty/libwebp/src/utils/filters_utils.c | 5 +- 3rdparty/libwebp/src/utils/filters_utils.h | 2 +- .../libwebp/src/utils/huffman_encode_utils.c | 42 +- .../libwebp/src/utils/huffman_encode_utils.h | 8 +- 3rdparty/libwebp/src/utils/huffman_utils.c | 2 + 3rdparty/libwebp/src/utils/huffman_utils.h | 1 + 3rdparty/libwebp/src/utils/palette.c | 17 +- 3rdparty/libwebp/src/utils/palette.h | 2 + .../src/utils/quant_levels_dec_utils.c | 151 +-- .../libwebp/src/utils/quant_levels_utils.c | 3 +- 3rdparty/libwebp/src/utils/random_utils.c | 15 +- 3rdparty/libwebp/src/utils/random_utils.h | 17 +- 3rdparty/libwebp/src/utils/rescaler_utils.c | 2 + 3rdparty/libwebp/src/utils/thread_utils.c | 135 +-- 3rdparty/libwebp/src/utils/thread_utils.h | 4 +- 3rdparty/libwebp/src/utils/utils.c | 24 +- 3rdparty/libwebp/src/webp/decode.h | 13 +- 3rdparty/libwebp/src/webp/demux.h | 2 + 3rdparty/libwebp/src/webp/encode.h | 11 +- 3rdparty/libwebp/src/webp/format_constants.h | 7 +- 3rdparty/libwebp/src/webp/mux_types.h | 1 + 3rdparty/libwebp/src/webp/types.h | 8 +- 156 files changed, 9416 insertions(+), 6870 deletions(-) create mode 100644 3rdparty/libwebp/patches/7233156.patch create mode 100644 3rdparty/libwebp/src/dsp/lossless_avx2.c create mode 100644 3rdparty/libwebp/src/dsp/lossless_enc_avx2.c diff --git a/3rdparty/libwebp/CMakeLists.txt b/3rdparty/libwebp/CMakeLists.txt index f3b6ebd0d6..1787466f8d 100644 --- a/3rdparty/libwebp/CMakeLists.txt +++ b/3rdparty/libwebp/CMakeLists.txt @@ -21,6 +21,13 @@ if(ANDROID AND ARMEABI_V7A AND NOT NEON) endforeach() endif() +if(WIN32) + foreach(file ${lib_srcs}) + if("${file}" MATCHES "_avx2.c") + set_source_files_properties("${file}" COMPILE_FLAGS "/arch:AVX") + 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)) From c5d70a7f229ed3d9eae75fe2258d3b5ece507fb5 Mon Sep 17 00:00:00 2001 From: zdenyhraz Date: Sat, 13 Dec 2025 10:45:05 +0100 Subject: [PATCH 041/103] Merge pull request #28146 from zdenyhraz:iterative-phase-correlation Iterative Phase Correlation #28146 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- doc/opencv.bib | 10 + modules/imgproc/include/opencv2/imgproc.hpp | 16 ++ modules/imgproc/src/phasecorr_iterative.cpp | 191 ++++++++++++++++++++ modules/imgproc/test/test_ipc.cpp | 117 ++++++++++++ 4 files changed, 334 insertions(+) create mode 100644 modules/imgproc/src/phasecorr_iterative.cpp create mode 100644 modules/imgproc/test/test_ipc.cpp diff --git a/doc/opencv.bib b/doc/opencv.bib index 97ad726dbd..5c8898f120 100644 --- a/doc/opencv.bib +++ b/doc/opencv.bib @@ -1561,3 +1561,13 @@ volume = {7}, url = {https://doi.org/10.1007/BF01898354} } +@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/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 35420b6f53..e02d57875e 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3051,6 +3051,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) 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/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 From 748d6545352a70feb83054c4699807ee3c45e160 Mon Sep 17 00:00:00 2001 From: satyam yadav Date: Sun, 14 Dec 2025 17:55:52 +0530 Subject: [PATCH 042/103] Merge pull request #28173 from satyam102006:fix/issue-28165-videoio-ios-crash videoio(ios): fix NSInvalidArgumentException in VideoWriter release #28173 Summary Fixes a crash on iOS when calling `VideoWriter::release()` in OpenCV 4.12.0. Issue Fixes #28165 Detailed Description This PR resolves a regression where an `NSInvalidArgumentException` was thrown with the message ` -[NSAutoreleasePool retain]: Cannot retain an autorelease pool`. The crash was caused by the manual usage of `NSAutoreleasePool` in the `~CvVideoWriter_AVFoundation` destructor. The local pool variable was being captured by the completion handler block passed to `[mMovieWriter finishWritingWithCompletionHandler:]`. Since `NSAutoreleasePool` instances cannot be retained, capturing them in a block causes a crash. Changes: - Replaced manual `NSAutoreleasePool` allocation and draining with modern `@autoreleasepool` blocks in `CvVideoWriter_AVFoundation`. - applied this modernization to the Constructor, Destructor, and `write` methods. - Specifically in the destructor, an inner `@autoreleasepool` is now used inside the completion block, ensuring no pool object needs to be captured from the outer scope. --- modules/videoio/src/cap_avfoundation.mm | 372 +++++++++++------------- 1 file changed, 171 insertions(+), 201 deletions(-) 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 From f5d6fe5392eb6b5b2c42129ef3416937aec2d90b Mon Sep 17 00:00:00 2001 From: Karnav Shah Date: Mon, 15 Dec 2025 11:54:58 +0530 Subject: [PATCH 043/103] Merge pull request #28176 from shahkarnav115-beep:docs-animation-frame-duration Docs(imgcodecs): clarify animation frame duration units #28176 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake Docs-only change. No code or tests affected. --- doc/tutorials/app/animations.markdown | 3 +++ 1 file changed, 3 insertions(+) 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 From 912d27a7b76313530db296fcfb38e1b11471c466 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Mon, 15 Dec 2025 15:51:48 +0800 Subject: [PATCH 044/103] Merge pull request #28180 from fengyuentau:rvv_hal/flip rvv_hal: fix flip inplace #28180 Fixes https://github.com/opencv/opencv/issues/28124 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- hal/riscv-rvv/src/core/flip.cpp | 48 +++++++++++++++++++++++++------ modules/core/test/test_arithm.cpp | 39 +++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/hal/riscv-rvv/src/core/flip.cpp b/hal/riscv-rvv/src/core/flip.cpp index 0af8458ccf..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); \ @@ -322,10 +354,8 @@ int flip(int src_type, const uchar* src_data, size_t src_step, int src_width, in if (src_width < 0 || src_height < 0 || esz > 32) return CV_HAL_ERROR_NOT_IMPLEMENTED; - // BUG: https://github.com/opencv/opencv/issues/28124 if (src_data == dst_data) { - return CV_HAL_ERROR_NOT_IMPLEMENTED; - //return flip_inplace(esz, dst_data, dst_step, src_width, src_height, flip_mode); + return flip_inplace(esz, dst_data, dst_step, src_width, src_height, flip_mode); } if (flip_mode == 0) diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index d2ce1f03fd..3a7a54a050 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -878,6 +878,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; @@ -944,6 +952,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; } @@ -1622,6 +1660,7 @@ INSTANTIATE_TEST_CASE_P(Core_InRangeS, ElemWiseTest, ::testing::Values(ElemWiseO INSTANTIATE_TEST_CASE_P(Core_InRange, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new InRangeOp))); 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))); From 9a7e88eb3ee06010c3788386d27e73e6aa21d1ca Mon Sep 17 00:00:00 2001 From: Karnav Shah Date: Mon, 15 Dec 2025 15:04:10 +0530 Subject: [PATCH 045/103] samples(python): guard against invalid STFT time step --- samples/python/audio_spectrogram.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 From 1eb1157490dd4a473cd518226c33519afebe4035 Mon Sep 17 00:00:00 2001 From: Karnav Shah Date: Mon, 15 Dec 2025 15:53:04 +0530 Subject: [PATCH 046/103] samples(python): fix argparse boolean flag for --try_cuda --- samples/python/stitching_detailed.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/samples/python/stitching_detailed.py b/samples/python/stitching_detailed.py index 1daef4d314..b1ce15b9c4 100644 --- a/samples/python/stitching_detailed.py +++ b/samples/python/stitching_detailed.py @@ -95,10 +95,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, From ddf2863aaa44b75105fe08f73d8e7e5789eb45cd Mon Sep 17 00:00:00 2001 From: pratham-mcw Date: Mon, 15 Dec 2025 20:51:46 +0530 Subject: [PATCH 047/103] NEON: fix incorrect accumulation in v_dotprod_expand_fast --- modules/core/include/opencv2/core/hal/intrin_neon.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_neon.hpp b/modules/core/include/opencv2/core/hal/intrin_neon.hpp index 3c1630148b..074e96da8a 100644 --- a/modules/core/include/opencv2/core/hal/intrin_neon.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_neon.hpp @@ -888,9 +888,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) { From 4e2c39b1a0b74660dff14a3c9ca4360cdba8a614 Mon Sep 17 00:00:00 2001 From: Karnav Shah Date: Tue, 16 Dec 2025 11:30:55 +0530 Subject: [PATCH 048/103] Fix crash in sample launcher when no item is selected --- samples/python/demo.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/samples/python/demo.py b/samples/python/demo.py index a56614ce4d..9f4a94f297 100755 --- a/samples/python/demo.py +++ b/samples/python/demo.py @@ -112,7 +112,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 = "" From 9c48c69c8a1877242738816482b8cedd3f455c21 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Tue, 16 Dec 2025 16:41:43 +0900 Subject: [PATCH 049/103] Merge pull request #28179 from Kumataro:fix28178 js: add C++17 requirement check for Emscripten 4.0.20+ #28179 Close https://github.com/opencv/opencv/issues/28178 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- .../js_setup/js_setup/js_setup.markdown | 7 ++++++- modules/js/CMakeLists.txt | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) 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/modules/js/CMakeLists.txt b/modules/js/CMakeLists.txt index ca27e3df60..526cb3d585 100644 --- a/modules/js/CMakeLists.txt +++ b/modules/js/CMakeLists.txt @@ -74,13 +74,23 @@ 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) ocv_module_include_directories(${EMSCRIPTEN_INCLUDE_DIR}) -add_definitions("-std=c++11") - set(deps ${OPENCV_MODULE_${the_module}_DEPS}) list(REMOVE_ITEM deps opencv_js_bindings_generator) # don't add dummy module link_libraries(${deps}) From 33ebceddb21f1169395c6a9046566cf4a4312173 Mon Sep 17 00:00:00 2001 From: Abhishek Gola Date: Tue, 16 Dec 2025 13:15:19 +0530 Subject: [PATCH 050/103] Merge pull request #28168 from abhishek-gola:mergeDebevec_fix Added 16U and 32F support in merge functions in photo module #28168 closes: https://github.com/opencv/opencv/issues/27873 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/photo/src/hdr_common.cpp | 47 ++++++++++++++------ modules/photo/src/hdr_common.hpp | 6 +-- modules/photo/src/merge.cpp | 73 ++++++++++++++++++++++++++------ modules/photo/test/test_hdr.cpp | 62 +++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 29 deletions(-) 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/"; From 7ca9d9ce038b7a19aba6defd4d29a065d28a9e6b Mon Sep 17 00:00:00 2001 From: Alex <117711477+0lekW@users.noreply.github.com> Date: Tue, 16 Dec 2025 21:38:51 +1300 Subject: [PATCH 051/103] Merge pull request #27878 from 0lekW:ffmpeg-negative-dts-seeking-fix Fix frame seeking with negative DTS values in FFMPEG backend #27878 **Merge with extra**: https://github.com/opencv/opencv_extra/pull/1289 Fixes https://github.com/opencv/opencv/issues/27819 Fixes https://github.com/opencv/opencv/issues/23472 Accompanied by PR on https://github.com/opencv/opencv_extra/pull/1289 The FFmpeg backend fails to correctly seek in H.264 videos that contain negative DTS values in their initial frames. This is a valid encoding practice used by modern video encoders (such as DaVinci Resolve's current export) where B-frame reordering causes the first few frames to have negative DTS values. When picture_pts is unavailable (AV_NOPTS_VALUE), the code falls back to using pkt_dts: `picture_pts = packet_raw.pts != AV_NOPTS_VALUE_ ? packet_raw.pts : packet_raw.dts;` If this DTS value is negative (which is legal per H.264 spec), it propagates through the frame number calculation: `frame_number = dts_to_frame_number(picture_pts) - first_frame_number;` This results in negative frame numbers, messing up seeking operations. Solution implemented in this branch is a timestamp normalization similar to FFmpegs -avoid_negative_ts_make_zero flag: - Calculate a global offset once on the first decoded frame by getting the minimum timestamp in either: - Container start_time - Stream start_time - First observed timestamp (PTS, then DTS). - Apply the offset consistently to all timestamps, shifting negative values to begin at 0 while keeping relative timing. - Simplify timestamp converters to remove `start_time` subtractions since timestamps are pre-normalized. This also includes a new test `videoio_ffmpeg.seek_with_negative_dts` This test verifies that seeking behavior performs as expected on a file which has negative DTS values in the first frames. A PR on opencv_extra accompanies this one with that testing file: https://github.com/opencv/opencv_extra/pull/1279 ``` opencv_extra=ffmpeg-videoio-negative-dts-test-data ``` ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/videoio/src/cap_ffmpeg_impl.hpp | 115 +++++++++++++++++++++--- modules/videoio/test/test_ffmpeg.cpp | 57 ++++++++++++ 2 files changed, 160 insertions(+), 12 deletions(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 69a355fb4a..26509a1a81 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 @@ -2120,8 +2196,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 +2255,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/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 3021bdfa4e..6ed908ceaa 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -1055,6 +1055,63 @@ TEST(ffmpeg_cap_properties, set_pos_get_msec) EXPECT_EQ(cap.get(CAP_PROP_POS_MSEC), 0.0); } +// 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 From c03734475f2cfab3ca2b7dc19b2f8eb15f5a1a93 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:21:34 +0300 Subject: [PATCH 052/103] Merge pull request #28159 from asmorkalov:as/java_cleaners Introduce option to generate Java code with finalize() or Cleaners interface #28159 Closes https://github.com/opencv/opencv/issues/22260 Replaces https://github.com/opencv/opencv/pull/23467 The PR introduce configuration option to generate Java code with Cleaner interface for Java 9+ and old-fashion finalize() method for old Java and Android. Mat class and derivatives are manually written. The PR introduce 2 base classes for it depending on the generator configuration. Pros: 1. No need to implement complex and error prone cleaner on library side. 2. No new CMake templates, easier to modify code in IDE. Cons: 1. More generator branches and different code for modern desktop and Android. TODO: - [x] Add Java version check to cmake - [x] Use Cleaners for ANDROID API 33+ ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- CMakeLists.txt | 14 +++--- cmake/android/OpenCVDetectAndroidSDK.cmake | 2 + modules/core/misc/java/src/java/core+Mat.java | 43 +++++++------------ modules/java/generator/CMakeLists.txt | 21 ++++++++- modules/java/generator/gen_java.py | 29 +++++++++++-- .../java/generator/src/cpp/CleanableMat.cpp | 28 ++++++++++++ modules/java/generator/src/cpp/Mat.cpp | 16 ------- .../generator/src/java9/CleanableMat.java | 23 ++++++++++ .../src/java_classic/CleanableMat.java | 21 +++++++++ .../generator/templates/java_class.prolog | 5 ++- 10 files changed, 145 insertions(+), 57 deletions(-) create mode 100644 modules/java/generator/src/cpp/CleanableMat.cpp create mode 100644 modules/java/generator/src/java9/CleanableMat.java create mode 100644 modules/java/generator/src/java_classic/CleanableMat.java diff --git a/CMakeLists.txt b/CMakeLists.txt index caf42ebc0e..fc99f04129 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -834,12 +834,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() @@ -1997,7 +1997,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/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/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/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 0885969277..591eb38ea3 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) @@ -366,6 +367,7 @@ class ClassInfo(GeneralInfo): module = 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 "", @@ -948,6 +950,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 + "(" @@ -1214,8 +1217,9 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${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 { @@ -1225,7 +1229,7 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${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); """ ) @@ -1233,7 +1237,7 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${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); @@ -1454,6 +1458,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) @@ -1543,6 +1553,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 5662534bc5..e3a2822277 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; } From 5c6c584c01ab0c8fd612dc1c49e93464a4fae283 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Tue, 16 Dec 2025 12:51:19 +0100 Subject: [PATCH 053/103] Prevent integer overflow in dimension checks. This fixes https://g-issues.oss-fuzz.com/issues/446726230 --- modules/imgcodecs/src/grfmt_png.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index a505266a8b..eb3f22796b 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); From 9f1a2d37365b6929b6fef406e9d843e72f9e8616 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 16 Dec 2025 18:34:20 +0300 Subject: [PATCH 054/103] Fixes for issues found by PVS Studio. --- modules/gapi/include/opencv2/gapi/util/any.hpp | 3 ++- modules/gapi/src/streaming/queue_source.cpp | 2 +- modules/gapi/test/gapi_async_test.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/util/any.hpp b/modules/gapi/include/opencv2/gapi/util/any.hpp index 94451c7717..9f0a5618ac 100644 --- a/modules/gapi/include/opencv2/gapi/util/any.hpp +++ b/modules/gapi/include/opencv2/gapi/util/any.hpp @@ -17,6 +17,7 @@ #if defined(_MSC_VER) // disable MSVC warning on "multiple copy constructors specified" + #pragma warning(push) # pragma warning(disable: 4521) #endif @@ -184,7 +185,7 @@ namespace util #if defined(_MSC_VER) // Enable "multiple copy constructors specified" back -# pragma warning(default: 4521) +# pragma warning(pop) #endif #endif // OPENCV_GAPI_UTIL_ANY_HPP diff --git a/modules/gapi/src/streaming/queue_source.cpp b/modules/gapi/src/streaming/queue_source.cpp index 59fde09c44..d81aaf43b4 100644 --- a/modules/gapi/src/streaming/queue_source.cpp +++ b/modules/gapi/src/streaming/queue_source.cpp @@ -67,7 +67,7 @@ cv::GMetaArg QueueSourceBase::descr_of() const { QueueInput::QueueInput(const cv::GMetaArgs &args) { for (auto &&m : args) { - m_sources.emplace_back(new cv::gapi::wip::QueueSourceBase(m)); + m_sources.emplace_back(std::make_shared(m)); } } diff --git a/modules/gapi/test/gapi_async_test.cpp b/modules/gapi/test/gapi_async_test.cpp index 7086f47c5c..e510526def 100644 --- a/modules/gapi/test/gapi_async_test.cpp +++ b/modules/gapi/test/gapi_async_test.cpp @@ -359,7 +359,7 @@ TYPED_TEST_CASE_P(cancel); TYPED_TEST_P(cancel, basic) { -#if defined(__GNUC__) && __GNUC__ >= 11 +#if defined(__GNUC__) && __GNUC__ == 11 // std::vector requests can't handle type with ctor parameter (SelfCanceling) FAIL() << "Test code is not available due to compilation error with GCC 11"; #else From dbe11a4d1cb700f45af1f08959b10e6743a202cf Mon Sep 17 00:00:00 2001 From: satyam yadav Date: Tue, 16 Dec 2025 23:46:29 +0530 Subject: [PATCH 055/103] Update cap_dshow.cpp --- modules/videoio/src/cap_dshow.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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: From 8ea50a77fd14752aba5d1e706fdd24866e382c2d Mon Sep 17 00:00:00 2001 From: happy-capybara-man Date: Thu, 18 Dec 2025 01:39:27 +0800 Subject: [PATCH 056/103] js: restore deep copy behavior for Mat.clone() Emscripten 3.1.71+ introduced ClassHandle.clone() which performs a shallow copy. This shadowed the original OpenCV clone() method which was intended for deep copying. This patch: 1. Hooks into onRuntimeInitialized in helpers.js 2. Overrides cv.Mat.prototype.clone to point to mat_clone (deep copy) 3. Updates JS tests to use clone() to verify the fix --- modules/js/src/helpers.js | 17 +++++++++++++++++ modules/js/test/test_mat.js | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/js/src/helpers.js b/modules/js/src/helpers.js index ab88d18eec..67dc8639fa 100644 --- a/modules/js/src/helpers.js +++ b/modules/js/src/helpers.js @@ -410,3 +410,20 @@ 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; + } +}; + 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); From a858fb7cff0f50b469fc9da42a1e2b7104abcc3f Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 17 Dec 2025 12:57:50 +0300 Subject: [PATCH 057/103] Stateless HAL for filters and morphology. --- modules/imgproc/src/filter.dispatch.cpp | 35 +++++++- modules/imgproc/src/hal_replacement.hpp | 111 ++++++++++++++++++++++++ modules/imgproc/src/morph.dispatch.cpp | 16 +++- 3 files changed, 159 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/src/filter.dispatch.cpp b/modules/imgproc/src/filter.dispatch.cpp index a1568d249c..172ec34d8e 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..f73094c494 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(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, + 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,44 @@ 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(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, + uchar * kernelx_data, int kernelx_len, 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 +244,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 dst_type destination image type + @param src_data source image data + @param src_step source image step + @param dst_data destination image data + @param dst_step destination image step + @param src_type source 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, 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, + 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 +343,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/morph.dispatch.cpp b/modules/imgproc/src/morph.dispatch.cpp index 98adbf10c1..ee473886ad 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, From 6aa1228ec1918e2ca65dc57c7c2f5cad4c7d940b Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 18 Dec 2025 09:48:43 +0300 Subject: [PATCH 058/103] KleidiCV update to version 0.7. --- hal/kleidicv/kleidicv.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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" From 721bb7289df1d1134a4eb04beb49cc1cc819c104 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Thu, 18 Dec 2025 11:32:19 +0300 Subject: [PATCH 059/103] Merge pull request #28185 from asmorkalov:as/static_analysys_fix Fixed issues identified by PVS Studio #28185 Partially fixes https://github.com/opencv/opencv/issues/28167 Paper: https://pvs-studio.com/en/blog/posts/cpp/1321/ Closed items: N2, N4, N5, N6, N7, N8, N10, N11, N13, N14. To be continued... ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/calib3d/src/chessboard.cpp | 2 +- modules/calib3d/src/usac/utils.cpp | 6 +- modules/calib3d/test/test_fundam.cpp | 4 +- modules/imgcodecs/src/grfmt_exr.cpp | 3 - modules/imgproc/src/approx.cpp | 23 +- modules/imgproc/test/test_filter.cpp | 2 +- .../imgproc/test/test_goodfeaturetotrack.cpp | 3 +- .../aruco/apriltag/apriltag_quad_thresh.cpp | 42 +--- .../aruco/apriltag/apriltag_quad_thresh.hpp | 1 - .../src/aruco/apriltag/unionfind.hpp | 214 +++++++++--------- .../objdetect/src/aruco/apriltag/zmaxheap.cpp | 1 - modules/objdetect/src/precomp.hpp | 1 + modules/ts/src/ocl_test.cpp | 2 - 13 files changed, 133 insertions(+), 171 deletions(-) diff --git a/modules/calib3d/src/chessboard.cpp b/modules/calib3d/src/chessboard.cpp index b4a8d9dd50..5855c3f255 100644 --- a/modules/calib3d/src/chessboard.cpp +++ b/modules/calib3d/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/calib3d/src/usac/utils.cpp b/modules/calib3d/src/usac/utils.cpp index 52bc456588..47e78cb559 100644 --- a/modules/calib3d/src/usac/utils.cpp +++ b/modules/calib3d/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]; } @@ -789,4 +789,4 @@ Ptr GridNeighborhoodGraph::create(const Mat &points, return makePtr(points, points_size, cell_size_x_img1_, cell_size_y_img1_, cell_size_x_img2_, cell_size_y_img2_, max_neighbors); } -}} \ No newline at end of file +}} diff --git a/modules/calib3d/test/test_fundam.cpp b/modules/calib3d/test/test_fundam.cpp index 2a7af54a12..ab987ffcea 100644 --- a/modules/calib3d/test/test_fundam.cpp +++ b/modules/calib3d/test/test_fundam.cpp @@ -621,9 +621,9 @@ void CV_RodriguesTest::get_test_array_types_and_sizes( } -double CV_RodriguesTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int j ) +double CV_RodriguesTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ ) { - return j == 4 ? 1e-2 : 1e-2; + return 1e-2; } diff --git a/modules/imgcodecs/src/grfmt_exr.cpp b/modules/imgcodecs/src/grfmt_exr.cpp index f1d4126e16..1a5d7397d9 100644 --- a/modules/imgcodecs/src/grfmt_exr.cpp +++ b/modules/imgcodecs/src/grfmt_exr.cpp @@ -151,9 +151,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/imgproc/src/approx.cpp b/modules/imgproc/src/approx.cpp index 433ca669d5..14a6c743e9 100644 --- a/modules/imgproc/src/approx.cpp +++ b/modules/imgproc/src/approx.cpp @@ -793,16 +793,13 @@ cvApproxPoly( const void* array, int header_size, { CvSeq *contour = 0; - switch (method) + if( parameter < 0 ) + CV_Error( cv::Error::StsOutOfRange, "Accuracy must be non-negative" ); + + CV_Assert( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 || + CV_SEQ_ELTYPE(src_seq) == CV_32FC2 ); + { - case CV_POLY_APPROX_DP: - if( parameter < 0 ) - CV_Error( cv::Error::StsOutOfRange, "Accuracy must be non-negative" ); - - CV_Assert( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 || - CV_SEQ_ELTYPE(src_seq) == CV_32FC2 ); - - { int npoints = src_seq->total, nout = 0; _buf.allocate(npoints*2); cv::Point *src = _buf.data(), *dst = src + npoints; @@ -817,17 +814,13 @@ cvApproxPoly( const void* array, int header_size, nout = cv::approxPolyDP_(src, npoints, dst, closed, parameter, stack); else if( CV_SEQ_ELTYPE(src_seq) == CV_32FC2 ) nout = cv::approxPolyDP_((cv::Point2f*)src, npoints, - (cv::Point2f*)dst, closed, parameter, stack); + (cv::Point2f*)dst, closed, parameter, stack); else CV_Error( cv::Error::StsUnsupportedFormat, "" ); contour = cvCreateSeq( src_seq->flags, header_size, - src_seq->elem_size, storage ); + src_seq->elem_size, storage ); cvSeqPushMulti(contour, dst, nout); - } - break; - default: - CV_Error( cv::Error::StsBadArg, "Invalid approximation method" ); } CV_Assert( contour ); diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index 46164aed21..2920b71e8a 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -1374,7 +1374,7 @@ test_cornerEigenValsVecs( const Mat& src, Mat& eigenv, Mat& ocv_eigenv, int block_size, int _aperture_size, int mode ) { int i, j; - int aperture_size = _aperture_size < 0 ? 3 : _aperture_size; + int aperture_size = _aperture_size <= 0 ? 3 : _aperture_size; Point anchor( aperture_size/2, aperture_size/2 ); CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 ); diff --git a/modules/imgproc/test/test_goodfeaturetotrack.cpp b/modules/imgproc/test/test_goodfeaturetotrack.cpp index d6204c0404..38e9a4bd5f 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/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/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/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_; From 91a745adc5fa312e2235f944d30dcaee87ccd2aa Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Thu, 18 Dec 2025 10:26:04 +0100 Subject: [PATCH 060/103] Fix potential pointer overflow. It is undefined behavior in C++ to compute an adress out of the allocated zone (even when not dereferencing). https://en.cppreference.com/w/cpp/language/operator_arithmetic.html#Pointer_arithmetic --- modules/video/src/optflowgf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; From dc52acc7d1310f5fbd2a0f60db06f767a4698bcf Mon Sep 17 00:00:00 2001 From: Karnav Shah Date: Thu, 18 Dec 2025 15:17:35 +0530 Subject: [PATCH 061/103] Merge pull request #28200 from shahkarnav115-beep:fix-avif-null-checks imgcodecs(avif): add safety checks for AVIF decoder and encoder #28200 Add defensive checks to prevent potential null dereference in AVIF decoder and encoder paths when handling malformed input or allocation failures. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgcodecs/src/grfmt_avif.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) 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++) From 87f4a9782cda94da4f98b7753fa2127a7d61f847 Mon Sep 17 00:00:00 2001 From: AdwaithBatchu Date: Thu, 18 Dec 2025 16:23:56 +0530 Subject: [PATCH 062/103] Merge pull request #28203 from AdwaithBatchu:fix-macos-lapack-warnings core: suppress LAPACK deprecation warnings on macOS #28203 ### Description On macOS (Apple Silicon) with newer Xcode/Clang, the CLAPACK interface is deprecated. Compiling `hal_internal.cpp` triggers multiple warnings like: `warning: 'sgesv_' is deprecated: first deprecated in macOS 13.3 - The CLAPACK interface is deprecated. [-Wdeprecated-declarations]` Since migrating to the new Accelerate interface (Apple's recommended fix) requires significant changes to the HAL implementation and breaks the build if headers are mismatched, this PR takes the pragmatic approach. It suppresses the `-Wdeprecated-declarations` warning for this specific file using Clang pragmas to clean up the build output. ### Verification - [x] Verified build is silent on macOS (M3). - [x] Verified pragmas are correctly scoped within `HAVE_LAPACK` to prevent scope mismatch on non-LAPACK builds. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/calib3d/src/usac/dls_solver.cpp | 8 ++++++++ modules/calib3d/src/usac/essential_solver.cpp | 8 ++++++++ modules/calib3d/src/usac/pnp_solver.cpp | 8 ++++++++ modules/core/src/hal_internal.cpp | 11 ++++++++++- 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/modules/calib3d/src/usac/dls_solver.cpp b/modules/calib3d/src/usac/dls_solver.cpp index 76e7e5d645..1e1cf6c34d 100644 --- a/modules/calib3d/src/usac/dls_solver.cpp +++ b/modules/calib3d/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/calib3d/src/usac/essential_solver.cpp b/modules/calib3d/src/usac/essential_solver.cpp index 8f9b5b9d33..dfff4b970b 100644 --- a/modules/calib3d/src/usac/essential_solver.cpp +++ b/modules/calib3d/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/calib3d/src/usac/pnp_solver.cpp b/modules/calib3d/src/usac/pnp_solver.cpp index 24c06ac22b..044a7da9ff 100644 --- a/modules/calib3d/src/usac/pnp_solver.cpp +++ b/modules/calib3d/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 { @@ -412,3 +416,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/core/src/hal_internal.cpp b/modules/core/src/hal_internal.cpp index f3f23a3b3c..d15cbb7ad3 100644 --- a/modules/core/src/hal_internal.cpp +++ b/modules/core/src/hal_internal.cpp @@ -77,6 +77,11 @@ __msan_unpoison(address, size) #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 template static inline void transpose_square_inplace(fptype *src, size_t src_ld, size_t m) @@ -701,4 +706,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 From e3e87055bd263aa152556900d40d98ffd847f41a Mon Sep 17 00:00:00 2001 From: HARSH SUMAN Date: Thu, 18 Dec 2025 17:22:20 +0530 Subject: [PATCH 063/103] Merge pull request #28195 from harsh-suman5:fix-coverage-regex Fix regex escape warning in _coverage.py by using raw string #2819 while i am running '_coverage.py' I noticed there is a syntax warning:"\." It is an invalid escape sequence The regex was written as a normal string with '\.' which is not valid escape in string literals and give problems and error in a new python versions. To fix this switch to raw string(r'cv2?\.\w+') which can passes the backslashes directly to the regex engine. This removes the warning and keeps the code and pattern working properly. This is a small fix but important for future compatibilities. --- samples/python/_coverage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 44b31dd82c5c95c75778d48bb13e961e0587fd87 Mon Sep 17 00:00:00 2001 From: ramukhsuya <163312977+ramukhsuya@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:25:04 +0530 Subject: [PATCH 064/103] Merge pull request #28171 from ramukhsuya:tflite-maximum-support dnn(tflite): add support for MAXIMUM layer #28171 Fixes #26433 This PR adds support for the `MAXIMUM` layer in the TFLite importer. It maps the TFLite `MAXIMUM` opcode to the existing OpenCV Element-wise `Max` operation. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/dnn/src/tflite/tflite_importer.cpp | 8 +++++-- modules/dnn/test/test_tflite_importer.cpp | 28 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/modules/dnn/src/tflite/tflite_importer.cpp b/modules/dnn/src/tflite/tflite_importer.cpp index 7bc4f55988..edcc3804e9 100644 --- a/modules/dnn/src/tflite/tflite_importer.cpp +++ b/modules/dnn/src/tflite/tflite_importer.cpp @@ -287,7 +287,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"] = &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; @@ -577,7 +578,10 @@ 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 { 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_tflite_importer.cpp b/modules/dnn/test/test_tflite_importer.cpp index 186b0ff154..3cee776611 100644 --- a/modules/dnn/test/test_tflite_importer.cpp +++ b/modules/dnn/test/test_tflite_importer.cpp @@ -283,6 +283,34 @@ 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); +} + INSTANTIATE_TEST_CASE_P(/**/, Test_TFLite, dnnBackendsAndTargets()); }} // namespace From 4946b97c3d1ef2a3806fc3fc624666ab5db2ee14 Mon Sep 17 00:00:00 2001 From: happy-capybara-man Date: Fri, 19 Dec 2025 02:56:25 +0800 Subject: [PATCH 065/103] js: remove empty line before EOF in helpers.js --- modules/js/src/helpers.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/js/src/helpers.js b/modules/js/src/helpers.js index 67dc8639fa..315f802eb1 100644 --- a/modules/js/src/helpers.js +++ b/modules/js/src/helpers.js @@ -425,5 +425,4 @@ Module['onRuntimeInitialized'] = function() { typeof cv.Mat.prototype.mat_clone === 'function') { cv.Mat.prototype.clone = cv.Mat.prototype.mat_clone; } -}; - +}; \ No newline at end of file From 5f0bd387dbd066da43922af6dc0cd45465a3d5c9 Mon Sep 17 00:00:00 2001 From: AdwaithBatchu Date: Fri, 19 Dec 2025 11:56:20 +0530 Subject: [PATCH 066/103] optimize: replace push_back with emplace_back in core loops Replaces wasteful temporary object construction with in-place construction using emplace_back. Covers hot paths in objdetect, imgproc, and stitching modules. --- modules/imgproc/src/hough.cpp | 18 ++++++------- modules/imgproc/src/subdivision2d.cpp | 14 +++++----- modules/objdetect/src/cascadedetect.cpp | 24 ++++++++--------- modules/objdetect/src/qrcode.cpp | 34 ++++++++++++------------- modules/stitching/src/seam_finders.cpp | 10 ++++---- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/modules/imgproc/src/hough.cpp b/modules/imgproc/src/hough.cpp index b418b046bc..8e9e3b3e48 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/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/objdetect/src/cascadedetect.cpp b/modules/objdetect/src/cascadedetect.cpp index 97b9d70fef..276d563a5b 100644 --- a/modules/objdetect/src/cascadedetect.cpp +++ b/modules/objdetect/src/cascadedetect.cpp @@ -1063,9 +1063,9 @@ public: if( classifier->data.stages.size() + result == 0 ) { mtx->lock(); - rectangles->push_back(Rect(cvRound(x*scalingFactor), - cvRound(y*scalingFactor), - winSize.width, winSize.height)); + rectangles->emplace_back(cvRound(x*scalingFactor), + cvRound(y*scalingFactor), + winSize.width, winSize.height); rejectLevels->push_back(-result); levelWeights->push_back(gypWeight); mtx->unlock(); @@ -1074,9 +1074,9 @@ public: else if( result > 0 ) { mtx->lock(); - rectangles->push_back(Rect(cvRound(x*scalingFactor), - cvRound(y*scalingFactor), - winSize.width, winSize.height)); + rectangles->emplace_back(cvRound(x*scalingFactor), + cvRound(y*scalingFactor), + winSize.width, winSize.height); mtx->unlock(); } if( result == 0 ) @@ -1222,10 +1222,10 @@ bool CascadeClassifierImpl::ocl_detectMultiScaleNoGrouping( const std::vectorgetScaleData(fptr[i*3 + 1]); - candidates.push_back(Rect(cvRound(fptr[i*3 + 2]*s.scale), - cvRound(fptr[i*3 + 3]*s.scale), - cvRound(data.origWinSize.width*s.scale), - cvRound(data.origWinSize.height*s.scale))); + candidates.emplace_back(cvRound(fptr[i*3 + 2]*s.scale), + cvRound(fptr[i*3 + 3]*s.scale), + cvRound(data.origWinSize.width*s.scale), + cvRound(data.origWinSize.height*s.scale)); } } return ok; @@ -1570,8 +1570,8 @@ bool CascadeClassifierImpl::Data::read(const FileNode &root) for( int i = 0; i < ntrees; i++, nodeOfs++, leafOfs+= 2 ) { const DTreeNode& node = nodes[nodeOfs]; - stumps.push_back(Stump(node.featureIdx, node.threshold, - leaves[leafOfs], leaves[leafOfs+1])); + stumps.emplace_back(node.featureIdx, node.threshold, + leaves[leafOfs], leaves[leafOfs+1]); } } } diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index 7160bbf720..d52b818287 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -412,9 +412,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++) { @@ -757,7 +757,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; @@ -2217,13 +2217,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.0, 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.0, 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) { @@ -2294,13 +2294,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()) @@ -3319,9 +3319,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/stitching/src/seam_finders.cpp b/modules/stitching/src/seam_finders.cpp index 0e0c7d1967..e24ba09367 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); } } From 93f1413db9efe3f100895b3267dace76cc232d84 Mon Sep 17 00:00:00 2001 From: Brian Ferri Date: Fri, 19 Dec 2025 10:49:36 +0100 Subject: [PATCH 067/103] fix: Invalid operands to binary expression --- modules/core/src/opencl/runtime/opencl_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 43074571af37ec25889255d453d39a28a8bb3587 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Fri, 19 Dec 2025 19:41:21 +0800 Subject: [PATCH 068/103] Merge pull request #28163 from dkurt:d.kurtaev/convexHull_repeats Keep convexHull output indices monotone if possible #28163 ### Pull Request Readiness Checklist resolves https://github.com/opencv/opencv/issues/24907 ? resolves https://github.com/opencv/opencv/issues/4954 See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/src/convhull.cpp | 42 ++++++++++++++++++-- modules/imgproc/test/test_convhull.cpp | 54 ++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/modules/imgproc/src/convhull.cpp b/modules/imgproc/src/convhull.cpp index ed7b5a4a52..0340870397 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/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp index 24c1604d2a..2d9dab5a57 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(), @@ -1283,6 +1287,56 @@ INSTANTIATE_TEST_CASE_P(Imgproc, minAreaRect_of_line, 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. */ From a3380cc128d8c8d880d20f88cb3a8d6ec6971831 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 19 Dec 2025 14:59:45 +0300 Subject: [PATCH 069/103] Code review fixes. --- modules/imgproc/src/hal_replacement.hpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/imgproc/src/hal_replacement.hpp b/modules/imgproc/src/hal_replacement.hpp index f73094c494..6df3b77c9b 100644 --- a/modules/imgproc/src/hal_replacement.hpp +++ b/modules/imgproc/src/hal_replacement.hpp @@ -113,10 +113,10 @@ struct cvhalFilter2D {}; @param allowInplace indicates whether the inplace operation will be possible @sa cv::filter2D, cv::hal::Filter2D */ -inline int hal_ni_filter_stateless(uchar * src_data, size_t src_step, int src_type, +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, - uchar * kernel_data, size_t kernel_step, int kernel_type, int kernel_width, int kernel_height, + 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; } @@ -196,11 +196,12 @@ inline int hal_ni_filterFree(cvhalFilter2D *context) { return CV_HAL_ERROR_NOT_I @param borderType border processing mode (CV_HAL_BORDER_REFLECT, ...) @sa cv::sepFilter2D, cv::hal::SepFilter2D */ -inline int hal_ni_sepFilter_stateless(uchar * src_data, size_t src_step, int src_type, +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, - uchar * kernelx_data, int kernelx_len, uchar * kernely_data, int kernely_len, int kernel_type, - int anchor_x, int anchor_y, double delta, int borderType) + 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; } /** @@ -253,12 +254,12 @@ inline int hal_ni_sepFilterFree(cvhalFilter2D *context) { return CV_HAL_ERROR_NO /** @brief morphology in a stateless manner @param operation morphology operation CV_HAL_MORPH_ERODE or CV_HAL_MORPH_DILATE - @param dst_type destination image type @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 src_type source image type + @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) @@ -283,11 +284,11 @@ inline int hal_ni_sepFilterFree(cvhalFilter2D *context) { return CV_HAL_ERROR_NO @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, uchar * src_data, size_t src_step, int src_type, +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, - uchar * kernel_data, size_t kernel_step, int kernel_type, int kernel_width, int kernel_height, + 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; } From 43053327ffabbd0dc205f65497f0086dd981b198 Mon Sep 17 00:00:00 2001 From: ramukhsuya Date: Fri, 19 Dec 2025 20:41:38 +0530 Subject: [PATCH 070/103] DNN: Add TFLite Minimum layer support --- modules/dnn/src/tflite/tflite_importer.cpp | 5 +++- modules/dnn/test/test_tflite_importer.cpp | 28 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/modules/dnn/src/tflite/tflite_importer.cpp b/modules/dnn/src/tflite/tflite_importer.cpp index edcc3804e9..4b443ed797 100644 --- a/modules/dnn/src/tflite/tflite_importer.cpp +++ b/modules/dnn/src/tflite/tflite_importer.cpp @@ -288,7 +288,7 @@ TFLiteImporter::DispatchMap TFLiteImporter::buildDispatchMap() dispatch["ADD"] = dispatch["MUL"] = dispatch["SUB"] = dispatch["SQRT"] = dispatch["DIV"] = dispatch["NEG"] = dispatch["RSQRT"] = dispatch["SQUARED_DIFFERENCE"] = - dispatch["MAXIMUM"] = &TFLiteImporter::parseEltwise; + 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; @@ -581,6 +581,9 @@ void TFLiteImporter::parseEltwise(const Operator& op, const std::string& opcode, } 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_tflite_importer.cpp b/modules/dnn/test/test_tflite_importer.cpp index 3cee776611..f7689405dc 100644 --- a/modules/dnn/test/test_tflite_importer.cpp +++ b/modules/dnn/test/test_tflite_importer.cpp @@ -311,6 +311,34 @@ TEST_P(Test_TFLite, maximum) 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 From 5d8ab389f8a475e486080d698325d434e14aec2f Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Fri, 19 Dec 2025 16:59:18 +0100 Subject: [PATCH 071/103] Fix potential overflow Fixes https://g-issues.oss-fuzz.com/issues/470321412 --- modules/imgcodecs/src/grfmt_png.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index eb3f22796b..e1e5d9a710 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -509,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; } From 78c27ba4a60e32caf6906675a1b50062ae27bc79 Mon Sep 17 00:00:00 2001 From: happy-capybara-man Date: Sat, 20 Dec 2025 15:23:15 +0800 Subject: [PATCH 072/103] Revert "Merge pull request #27985 from happy-capybara-man:docs/fix-mat-clone-js" This reverts commit db207c88b0d3949354f0125cc718962e69b7a637. --- .../js_assets/js_image_arithmetics_bitwise.html | 2 +- doc/js_tutorials/js_assets/js_imgproc_camera.html | 2 +- doc/js_tutorials/js_assets/js_intelligent_scissors.html | 2 +- doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown | 6 ++---- samples/dnn/js_face_recognition.html | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) 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/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); From 2b427888037c7c96b78ddd9c874a97acc3ad30fd Mon Sep 17 00:00:00 2001 From: satyam yadav Date: Sat, 20 Dec 2025 15:58:32 +0530 Subject: [PATCH 073/103] Merge pull request #28217 from satyam102006:feature/dis-coarsest-scale [video] Add setCoarsestScale to DISOpticalFlow (Fixes #25068) #28217 ### Description This PR addresses issue #25068 regarding the number of scales in `DISOpticalFlow`. Currently, `DISOpticalFlow` automatically computes the `coarsest_scale` based on the image size. While generally effective, this behavior can cause errors in specific use cases (e.g., mechanics/speckle pattern analysis) where high pyramid levels degrade quality. This change introduces a manual override: - Added `setCoarsestScale(int val)` and `getCoarsestScale()` to the public API. - Updated `DISOpticalFlowImpl::calc` and `ocl_calc` to use the user-defined scale if set. - Preserved the existing "automatic" behavior as the default (when set to -1). ### Changes - **`modules/video/include/opencv2/video/tracking.hpp`**: Added virtual method declarations. - **`modules/video/src/dis_flow.cpp`**: Implemented `set/getCoarsestScale` in `DISOpticalFlowImpl` and updated calculation logic. - **`modules/video/test/test_OF_accuracy.cpp`**: Added `DenseOpticalFlow_DIS.ManualCoarsestScale` regression test. Fixes #25068 --- .../video/include/opencv2/video/tracking.hpp | 10 +++++++ modules/video/src/dis_flow.cpp | 23 +++++++++++++--- modules/video/test/test_OF_accuracy.cpp | 27 ++++++++++++++++++- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index 7cd04061de..2ed27f505d 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -678,6 +678,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/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/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 From 66bb0a801765a411b1e801e06f4e02c9b93bace8 Mon Sep 17 00:00:00 2001 From: Abhishek Gola Date: Sun, 21 Dec 2025 22:48:28 +0530 Subject: [PATCH 074/103] Merge pull request #28164 from abhishek-gola:randomNormalLike_layer_4x Added randomNormalLike layer to 4.x branch #28164 OpenCV Extra: https://github.com/opencv/opencv_extra/pull/1297 Backport of https://github.com/opencv/opencv/pull/28110 to 4.x ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/dnn/src/layers/layers_common.hpp | 68 ++++++++++ .../dnn/src/layers/randomnormallike_layer.cpp | 122 ++++++++++++++++++ modules/dnn/src/onnx/onnx_importer.cpp | 10 ++ modules/dnn/test/test_onnx_importer.cpp | 43 ++++++ 4 files changed, 243 insertions(+) create mode 100644 modules/dnn/src/layers/randomnormallike_layer.cpp diff --git a/modules/dnn/src/layers/layers_common.hpp b/modules/dnn/src/layers/layers_common.hpp index 4510f6b106..5d36004669 100644 --- a/modules/dnn/src/layers/layers_common.hpp +++ b/modules/dnn/src/layers/layers_common.hpp @@ -59,6 +59,74 @@ namespace cv { namespace dnn { + +enum OnnxDataType +{ + ONNX_UNDEFINED = 0, + ONNX_FLOAT = 1, // float + ONNX_UINT8 = 2, // uint8_t + ONNX_INT8 = 3, // int8_t + ONNX_UINT16 = 4, // uint16_t + ONNX_INT16 = 5, // int16_t + ONNX_INT32 = 6, // int32_t + ONNX_INT64 = 7, // int64_t + ONNX_STRING = 8, // string + ONNX_BOOL = 9, // bool + ONNX_FLOAT16 = 10, + ONNX_DOUBLE = 11, + ONNX_UINT32 = 12, + ONNX_UINT64 = 13, + ONNX_BFLOAT16 = 14 +}; + +inline int onnxDataTypeToCV(OnnxDataType dt) +{ + switch (dt) + { + case ONNX_UINT8: return CV_8U; + case ONNX_INT8: return CV_8S; + case ONNX_UINT16: return CV_16U; + case ONNX_INT16: return CV_16S; + case ONNX_UINT32: +#ifdef CV_32U + return CV_32U; +#else + return CV_32S; +#endif + case ONNX_INT32: return CV_32S; + case ONNX_UINT64: +#ifdef CV_64U + return CV_64U; +#else + return CV_32S; +#endif + case ONNX_INT64: +#ifdef CV_64S + return CV_64S; +#else + return CV_32S; +#endif + case ONNX_FLOAT: return CV_32F; + case ONNX_DOUBLE: return CV_64F; + case ONNX_FLOAT16: return CV_16F; + case ONNX_BFLOAT16: +#ifdef CV_16BF + return CV_16BF; +#else + return CV_16F; +#endif + case ONNX_BOOL: +#ifdef CV_Bool + return CV_Bool; +#else + return CV_8U; +#endif + default: + // Fallback to default ONNX FLOAT if value is unknown. + return CV_32F; + } +} + void getConvolutionKernelParams(const LayerParams ¶ms, std::vector& kernel, std::vector& pads_begin, std::vector& pads_end, std::vector& strides, std::vector& dilations, cv::String &padMode, std::vector& adjust_pads, bool& useWinograd); diff --git a/modules/dnn/src/layers/randomnormallike_layer.cpp b/modules/dnn/src/layers/randomnormallike_layer.cpp new file mode 100644 index 0000000000..30713390c9 --- /dev/null +++ b/modules/dnn/src/layers/randomnormallike_layer.cpp @@ -0,0 +1,122 @@ +// 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. +// Copyright (C) 2025, BigVision LLC, all rights reserved. +// Third party copyrights are property of their respective owners. + +#include "../precomp.hpp" +#include "layers_common.hpp" +#include +#include + +namespace cv { namespace dnn { + +class RandomNormalLikeLayerImpl CV_FINAL : public Layer +{ +public: + RandomNormalLikeLayerImpl(const LayerParams& params) + { + setParamsFrom(params); + + mean = params.get("mean", 0.0); + scale = params.get("scale", 1.0); + + hasSeed = params.has("seed"); + if (hasSeed) + { + seed = params.get("seed"); + } + + depth = params.get("depth", CV_32F); + if (params.has("dtype")) + { + depth = onnxDataTypeToCV(static_cast(params.get("dtype"))); + } + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE + { + return backendId == DNN_BACKEND_OPENCV; + } + + virtual bool getMemoryShapes(const std::vector& inputs, + const int requiredOutputs, + std::vector& outputs, + std::vector& internals) const CV_OVERRIDE + { + CV_UNUSED(requiredOutputs); + CV_UNUSED(internals); + CV_CheckEQ(inputs.size(), 1ull, "RandomNormalLike: one input is expected"); + + outputs.assign(1, inputs[0]); + return false; + } + + virtual void forward(InputArrayOfArrays inputs_arr, + OutputArrayOfArrays outputs_arr, + OutputArrayOfArrays internals_arr) CV_OVERRIDE + { + CV_UNUSED(internals_arr); + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + CV_Assert(!inputs.empty()); + CV_Assert(!outputs.empty()); + + Mat out = outputs[0]; + const int desiredDepth = depth; + const bool needRecreate = out.depth() != desiredDepth; + Mat outBlob; + if (needRecreate) + { + const int dims = out.dims; + const int* sizes = out.size.p; + outBlob = Mat(dims, sizes, CV_MAKETYPE(desiredDepth, out.channels())); + } + else + { + outBlob = out; + } + + RNG seededRng; + RNG* rng = &theRNG(); + if (hasSeed) + { + Cv64suf u; + u.f = seed; + seededRng = RNG(u.u ? u.u : 1); + rng = &seededRng; + } + + if (outBlob.depth() == CV_32F || outBlob.depth() == CV_64F || outBlob.depth() == CV_16F) + { + rng->fill(outBlob, RNG::NORMAL, mean, scale); + } + else + { + Mat tmp(outBlob.size.dims(), outBlob.size.p, CV_32F); + rng->fill(tmp, RNG::NORMAL, mean, scale); + tmp.convertTo(outBlob, outBlob.type()); + } + + if (needRecreate) + { + outputs_arr.assign(std::vector{outBlob}); + } + } + +private: + double mean; + double scale; + bool hasSeed = false; + double seed = 0.0; + int depth; +}; + +CV_DNN_REGISTER_LAYER_CLASS_STATIC(RandomNormalLike, RandomNormalLikeLayerImpl); + +}} // namespace cv::dnn diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 93198d66d1..713867287e 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -191,6 +191,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); @@ -2952,6 +2953,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."); @@ -3958,6 +3967,7 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version) 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; std::vector simpleLayers{"Acos", "Acosh", "Asin", "Asinh", "Atan", "Atanh", "Ceil", "Celu", "Cos", diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 70859092a7..c1b50f3fcf 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -3233,6 +3233,49 @@ TEST_P(Test_ONNX_layers, TopK) { test("top_k_smallest"); } +TEST_P(Test_ONNX_layers, 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); +} + +TEST_P(Test_ONNX_layers, 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 From a714934c86a0061bfc591b214b26cc42535e41a1 Mon Sep 17 00:00:00 2001 From: Karnav Shah Date: Mon, 22 Dec 2025 01:25:48 +0530 Subject: [PATCH 075/103] dnn: add error message for unimplemented UMat NCHW blob conversion --- modules/dnn/src/dnn_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 74fbfc2343bab73d93d363cf5eeb019601475be7 Mon Sep 17 00:00:00 2001 From: satyam yadav Date: Mon, 22 Dec 2025 13:31:51 +0530 Subject: [PATCH 076/103] Merge pull request #28117 from satyam102006:fix/solveCubic-numerical-instability core: fix solveCubic numerical instability via coefficient normalization (fixes #27748) #28117 Summary This PR fixes numerical instability in `cv::solveCubic` when the leading coefficient `a` is non-zero but extremely small relative to other coefficients (Issue #27748). It introduces a **normalization step** that scales all coefficients by their maximum magnitude before solving. This ensures robust detection of when the equation should degenerate to a quadratic solver, without breaking valid cubic equations that happen to have small coefficients (e.g., scaled by 1e-9). The Problem (Issue #27748) The previous implementation checked `if (a == 0)` to decide whether to use the cubic or quadratic formula. - When `a` is extremely small (e.g., 1e-17) but not exactly zero, and other coefficients are normal (e.g., 5.0), the standard cubic formula suffers from catastrophic cancellation and overflow, producing incorrect roots (e.g., 1e14). The Fix 1. Normalization: The solver now finds `max_coeff = max(|a|, |b|, |c|, |d|)` and scales all coefficients by `1.0 / max_coeff`. 2. Relative Threshold: It then checks `if (abs(a) < epsilon)` on the *normalized* coefficients. Why this is better than previous attempts In a previous attempt (PR #28057), a simple absolute check `abs(a) < epsilon` was proposed. That approach was rejected because it failed for scaled equations. Fixes #27748 --- modules/core/src/mathfuncs.cpp | 29 ++++++++++++++++++++++++----- modules/core/test/test_math.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/modules/core/src/mathfuncs.cpp b/modules/core/src/mathfuncs.cpp index 90713fb27d..7108f2e81d 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 @@ -1586,12 +1588,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 @@ -1608,7 +1619,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; @@ -1625,6 +1636,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; @@ -1633,6 +1651,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/test/test_math.cpp b/modules/core/test/test_math.cpp index c6686cf1c4..c7c5dceb31 100644 --- a/modules/core/test/test_math.cpp +++ b/modules/core/test/test_math.cpp @@ -2618,6 +2618,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 From 15fe69b5af177d7bcd089d4d4cad7d0851c7d7ca Mon Sep 17 00:00:00 2001 From: satyam yadav Date: Mon, 22 Dec 2025 13:52:12 +0530 Subject: [PATCH 077/103] Merge pull request #28250 from satyam102006:fix/stackblur-overflow-28233 imgproc: fix heap-buffer-overflow in stackBlur #28233 #28250 ### Summary Fixes a heap-buffer-overflow in `cv::stackBlur` when the kernel size is larger than the image dimensions ### Changes * Added input validation to clamp the kernel size to the image dimensions. * Added a regression test (`regression_28233`) covering 1x1 and small image cases. Fixes #28233 --- modules/imgproc/src/stackblur.cpp | 12 +++++++++--- modules/imgproc/test/test_stackblur.cpp | 13 +++++++++++++ modules/js/test/test_imgproc.js | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) 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/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/js/test/test_imgproc.js b/modules/js/test/test_imgproc.js index 786a7ba4d2..47228e712c 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(); From f86bdfcff461bba3463a0cc906564df859a63227 Mon Sep 17 00:00:00 2001 From: Abhishek Gola Date: Mon, 22 Dec 2025 17:19:32 +0530 Subject: [PATCH 078/103] fixed heap buffer overflow issue --- modules/dnn/src/layers/nary_eltwise_layers.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/dnn/src/layers/nary_eltwise_layers.cpp b/modules/dnn/src/layers/nary_eltwise_layers.cpp index 348252d6fd..68c76906c6 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -466,8 +466,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]; @@ -481,7 +481,7 @@ public: ptr[i] = op(ptr1[i], ptr2[i]); } for (int j = 2; j < ninputs; j++) { - int dpj = steps[j + 1].back(); + size_t dpj = steps[j + 1].back() / sizeof(T); const T* ptrj = (const T*)(ptrs[j + 1]); if (dpj == 1) { for (int i = r.start; i < r.end; i++) { @@ -500,7 +500,7 @@ public: } ptr = tmp; for (int j = 2; j < ninputs; j++) { - int dpj = steps[j + 1].back(); + size_t dpj = steps[j + 1].back() / sizeof(T); const T* ptr_j = (const T*)(ptrs[j + 1]); for (int i = r.start; i < r.end; i++, ptr += dp, ptr_j += dpj) { *ptr = saturate_cast(op(*ptr, *ptr_j) * scale); @@ -512,8 +512,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]; @@ -535,7 +535,7 @@ public: ptr[i] = saturate_cast(op(ptr1[i], ptr2[i]) * scale); } for (int j = 2; j < ninputs; j++) { - int dpj = steps[j + 1].back(); + size_t dpj = steps[j + 1].back() / sizeof(T); const T* ptrj = (const T*)(ptrs[j + 1]); if (dpj == 1) { for (int i = 0; i < plane_size; i++) { @@ -554,7 +554,7 @@ public: } ptr = tmp; for (int j = 2; j < ninputs; j++) { - int dpj = steps[j + 1].back(); + size_t dpj = steps[j + 1].back() / sizeof(T); const T* ptrj = (const T*)(ptrs[j + 1]); for (int i = 0; i < plane_size; i++, ptr += dp, ptrj += dpj) { *ptr = op(*ptr, saturate_cast(*ptrj * scale)); From 55293d39c354cc7a4ffa65f67b149d8f1cd787b0 Mon Sep 17 00:00:00 2001 From: Jahnvi Date: Mon, 22 Dec 2025 22:26:39 +0530 Subject: [PATCH 079/103] Fix wording in Adding Images Tutorial --- doc/tutorials/core/adding_images/adding_images.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorials/core/adding_images/adding_images.markdown b/doc/tutorials/core/adding_images/adding_images.markdown index 7f3de47b80..90679a1b39 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?) From ea9b183d9ba2df3f656a5222694e322933b904eb Mon Sep 17 00:00:00 2001 From: Abhishek Shinde <139879930+falloficarus22@users.noreply.github.com> Date: Mon, 22 Dec 2025 17:41:58 +0000 Subject: [PATCH 080/103] Merge pull request #28259 from falloficarus22:fix/bilateral-filter-oob Fix the out-of-bounds read in cv::bilateralFilter for 32f images #28259 ### Root Cause Analysis The issue was caused by a discrepancy between the image range used to allocate the color weight look-up table (LUT) and the actual range of pixel values encountered during filtering, especially near the image borders. - Range Computation: `cv::bilateralFilter` computes the min/max values of the source image and allocates a `LUT (expLUT)` of size `kExpNumBins + 2` based on this range. - Border Padding: If `cv::BORDER_CONSTANT` is used (defaulting to 0), and 0 is outside the image's original range (e.g., an image with values between 100 and 200), the padded image will contain values (0) that create differences larger than those accounted for in the `LUT`. - Out-of-Bounds Access: When calculating the color weight, the code computes an index `idx` from the absolute difference. If this difference exceeds the expected range, `idx` can reach or exceed `kExpNumBins + 1`. Since the code performs linear interpolation using `expLUT[idx]` and `expLUT[idx + 1]`, an `idx` of `kExpNumBins + 1` causes an access to `expLUT[kExpNumBins + 2]`, which is out of bounds. ### Fix I implemented a robust clamping mechanism in both the SIMD (AVX/SSE) and scalar paths of the bilateral filter invoker: - Signature Update: Updated `bilateralFilterInvoker_32f` to accept `kExpNumBins` (the maximum valid `LUT` index). - Clamping: Clamped the computed color difference (alpha) to `kExpNumBins` before calculating the `LUT` index. This ensures that any difference exceeding the planned range is safely treated as the maximum difference in the `LUT` (which usually corresponds to a weight of 0), avoiding any out-of-bounds memory access. ### Modified Files Modified Files: `modules/imgproc/src/bilateral_filter.simd.hpp`: Updated the invoker class and SIMD/scalar loops to clamp the LUT index. `modules/imgproc/src/bilateral_filter.dispatch.cpp`: Updated the dispatch call site to pass the correct LUT size. Closes #28254 ### Pull Request Readiness Checklist - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- .../imgproc/src/bilateral_filter.dispatch.cpp | 2 +- modules/imgproc/src/bilateral_filter.simd.hpp | 29 ++++++++++--------- .../imgproc/test/test_bilateral_filter.cpp | 29 +++++++++++++++++++ 3 files changed, 45 insertions(+), 15 deletions(-) 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/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 From 2bca09a19156c0e4cd7f78326a752485f4989fb6 Mon Sep 17 00:00:00 2001 From: Jonas Perolini <74473718+JonasPerolini@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:54:40 +0100 Subject: [PATCH 081/103] Merge pull request #23190 from JonasPerolini:pr-output-marker-score Include pixel-based confidence in ArUco marker detection #23190 The aim of this pull request is to compute a **pixel-based confidence** of the marker detection. The confidence [0;1] is defined as the percentage of correctly detected pixels, with 1 describing a pixel perfect detection. Currently it is possible to get the normalized Hamming distance between the detected marker and the dictionary ground truth [Dictionary::getDistanceToId()](https://github.com/opencv/opencv/blob/4.x/modules/objdetect/src/aruco/aruco_dictionary.cpp#L114) However, this distance is based on the extracted bits and we lose information in the [majority count step](https://github.com/opencv/opencv/blob/4.x/modules/objdetect/src/aruco/aruco_detector.cpp#L487). For example, even if each cell has 49% incorrect pixels, we still obtain a perfect Hamming distance. **Implementation tests**: Generate 36 synthetic images containing 4 markers each (with different ids) so a total of 144 markers. Invert a given percentage of pixels in each cell of the marker to simulate uncertain detection. Assuming a perfect detection, define the ground truth uncertainty as the percentage of inverted pixels. The test is passed if `abs(computedConfidece - groundTruthConfidence) < 0.05` where `0.05` accounts for minor detection inaccuracies. - Performed for both regular and inverted markers - Included perspective-distorted markers - Markers in all 4 possible rotations [0, 90, 180, 270] - Different set of detection params: - `perspectiveRemovePixelPerCell` - `perspectiveRemoveIgnoredMarginPerCell` - `markerBorderBits` ![TestCases](https://github.com/user-attachments/assets/1113abd3-ff7a-45c8-8b4b-a9d2182eda82) The code properly builds locally and `opencv_test_objdetect` and `opencv_test_core` passed. Please let me know if there are any further modifications needed. Thanks! I've also pushed minor unrelated improvement (let me know if you want a separate PR) in the [bit extraction method](https://github.com/opencv/opencv/blob/4.x/modules/objdetect/src/aruco/aruco_detector.cpp#L435). `CV_Assert(perspectiveRemoveIgnoredMarginPerCell <=1)` should be `< 0.5`. Since there are margins on both sides of the cell, the margins must be smaller than half of the cell. When setting `perspectiveRemoveIgnoredMarginPerCell >= 0.5`, `opencv_test_objdetect` fails. Note: 0.499 is ok because `int()` will floor the result, thus `cellMarginPixels = int(cellMarginRate * cellSize)` will be smaller than `cellSize / 2` ### Pull Request Readiness Checklist - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The PR is proposed to the proper branch - [x] The feature is well documented and sample code can be built with the project CMake --- .../opencv2/objdetect/aruco_detector.hpp | 27 ++ .../opencv2/objdetect/aruco_dictionary.hpp | 3 +- .../objdetect/src/aruco/aruco_detector.cpp | 123 ++++- .../objdetect/src/aruco/aruco_dictionary.cpp | 15 +- .../objdetect/test/test_arucodetection.cpp | 429 ++++++++++++++++++ 5 files changed, 573 insertions(+), 24 deletions(-) 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/src/aruco/aruco_detector.cpp b/modules/objdetect/src/aruco/aruco_detector.cpp index a0f5c64390..fefaecbfef 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 @@ -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/test/test_arucodetection.cpp b/modules/objdetect/test/test_arucodetection.cpp index 399aa36102..d40bef6582 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)); From ad9387340acd189376db68e3b63487bed91b665b Mon Sep 17 00:00:00 2001 From: fcmiron Date: Mon, 22 Dec 2025 20:04:53 +0200 Subject: [PATCH 082/103] Merge pull request #27460 from fcmiron:gapi_set_workloadtype_dynamically G-API: Add support to set workload type dynamically in both OpenVINO and ONNX OVEP #27460 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../gapi/include/opencv2/gapi/infer/onnx.hpp | 9 +++ .../gapi/include/opencv2/gapi/infer/ov.hpp | 8 +++ .../opencv2/gapi/infer/workload_type.hpp | 58 +++++++++++++++++++ .../gapi/src/backends/onnx/gonnxbackend.cpp | 38 ++++++++++-- .../gapi/src/backends/onnx/gonnxbackend.hpp | 3 +- modules/gapi/src/backends/ov/govbackend.cpp | 28 +++++++++ modules/gapi/src/backends/ov/govbackend.hpp | 10 +++- 7 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 modules/gapi/include/opencv2/gapi/infer/workload_type.hpp diff --git a/modules/gapi/include/opencv2/gapi/infer/onnx.hpp b/modules/gapi/include/opencv2/gapi/infer/onnx.hpp index eb6316b446..f2e2cfac54 100644 --- a/modules/gapi/include/opencv2/gapi/infer/onnx.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/onnx.hpp @@ -20,6 +20,7 @@ #include // GAPI_EXPORTS #include // GKernelPackage #include // Generic +#include namespace cv { namespace gapi { @@ -752,8 +753,16 @@ protected: std::string m_tag; }; +class WorkloadTypeONNX : public WorkloadType {}; +using WorkloadTypeONNXPtr = std::shared_ptr; + } // namespace onnx } // namespace gapi +namespace detail { +template<> struct CompileArgTag { + static const char* tag() { return "gapi.onnx.workload_type"; } +}; +} // namespace detail } // namespace cv #endif // OPENCV_GAPI_INFER_HPP diff --git a/modules/gapi/include/opencv2/gapi/infer/ov.hpp b/modules/gapi/include/opencv2/gapi/infer/ov.hpp index 3673ff53a2..d228879fd1 100644 --- a/modules/gapi/include/opencv2/gapi/infer/ov.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/ov.hpp @@ -13,6 +13,7 @@ #include // GAPI_EXPORTS #include // GKernelType[M], GBackend #include // Generic +#include #include @@ -745,6 +746,9 @@ namespace wip { namespace ov { */ struct benchmark_mode { }; +class WorkloadTypeOV : public WorkloadType {}; +using WorkloadTypeOVPtr = std::shared_ptr; + } // namespace ov } // namespace wip @@ -756,6 +760,10 @@ namespace detail { static const char* tag() { return "gapi.wip.ov.benchmark_mode"; } }; + template<> struct CompileArgTag + { + static const char* tag() { return "gapi.wip.ov.workload_type"; } + }; } } // namespace cv diff --git a/modules/gapi/include/opencv2/gapi/infer/workload_type.hpp b/modules/gapi/include/opencv2/gapi/infer/workload_type.hpp new file mode 100644 index 0000000000..76482451f7 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/infer/workload_type.hpp @@ -0,0 +1,58 @@ +// 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. +// +// Copyright (C) 2025 Intel Corporation + +#ifndef OPENCV_WORKLOADTYPE_HPP +#define OPENCV_WORKLOADTYPE_HPP + +#include +#include +#include +#include + +using Callback = std::function; + +class WorkloadListener { + Callback callback; +public: + uint64_t id; + WorkloadListener(const Callback &cb, uint64_t listener_id) : callback(cb), id(listener_id) {} + + void operator()(const std::string &type) const { + if (callback) { + callback(type); + } + } + + bool operator==(const WorkloadListener& other) const { + return id == other.id; + } +}; +class WorkloadType { + std::vector listeners; + uint64_t nextId = 1; +public: + uint64_t addListener(const Callback &cb) { + uint64_t id = nextId++; + listeners.emplace_back(cb, id); + return id; + } + + void removeListener(uint64_t id) { + auto it = std::remove_if(listeners.begin(), listeners.end(), + [id](const WorkloadListener& entry) { return entry.id == id; }); + if (it != listeners.end()) { + listeners.erase(it, listeners.end()); + } + } + + void notify(const std::string &type) { + for (const auto &listener : listeners) { + listener(type); + } + } +}; + +#endif // OPENCV_WORKLOADTYPE_HPP diff --git a/modules/gapi/src/backends/onnx/gonnxbackend.cpp b/modules/gapi/src/backends/onnx/gonnxbackend.cpp index 546e5e9088..cbdd615c20 100644 --- a/modules/gapi/src/backends/onnx/gonnxbackend.cpp +++ b/modules/gapi/src/backends/onnx/gonnxbackend.cpp @@ -126,8 +126,14 @@ class ONNXCompiled { std::vector& outs); std::vector in_names_without_const; + + cv::gapi::onnx::WorkloadTypeONNXPtr m_workload_type; + uint64_t m_workload_listener_id = 0; + void setWorkloadType(const std::string &type); public: explicit ONNXCompiled(const gapi::onnx::detail::ParamDesc &pp); + ~ONNXCompiled(); + void listenToWorkloadType(cv::gapi::onnx::WorkloadTypeONNXPtr workload); // Extract the information about output layer #i cv::GMatDesc outMeta(int i) const; @@ -577,8 +583,10 @@ using GConstGONNXModel = ade::ConstTypedGraph } // anonymous namespace // GCPUExcecutable implementation ////////////////////////////////////////////// -cv::gimpl::onnx::GONNXExecutable::GONNXExecutable(const ade::Graph &g, - const std::vector &nodes) +cv::gimpl::onnx::GONNXExecutable::GONNXExecutable(const ade::Graph &g, + const std::vector &nodes, + const cv::GCompileArgs &compileArgs) + : m_g(g), m_gm(m_g) { // FIXME: Currently this backend is capable to run a single inference node only. // Need to extend our island fusion with merge/not-to-merge decision making parametrization @@ -589,6 +597,11 @@ cv::gimpl::onnx::GONNXExecutable::GONNXExecutable(const ade::Graph &g, case NodeType::OP: if (this_nh == nullptr) { this_nh = nh; + auto workload_arg = cv::gapi::getCompileArg(compileArgs); + if(workload_arg.has_value()) { + const auto &onnx_unit = iem.metadata(nh).get(); + onnx_unit.oc->listenToWorkloadType(workload_arg.value()); + } } else { util::throw_error(std::logic_error("Multi-node inference is not supported!")); @@ -834,6 +847,23 @@ ONNXCompiled::ONNXCompiled(const gapi::onnx::detail::ParamDesc &pp) out_data.resize(params.num_out); } +ONNXCompiled::~ONNXCompiled() { + if (m_workload_type) { + m_workload_type->removeListener(m_workload_listener_id); + } +} + +void ONNXCompiled::setWorkloadType(const std::string &type) { + const char* keys[] = {"ep.dynamic.workload_type"}; + const char* values[] = {type.c_str()}; + this_session.SetEpDynamicOptions(keys, values, 1); +} + +void ONNXCompiled::listenToWorkloadType(cv::gapi::onnx::WorkloadTypeONNXPtr workload) { + m_workload_type = workload; + m_workload_listener_id = m_workload_type->addListener(std::bind(&ONNXCompiled::setWorkloadType, this, std::placeholders::_1)); +} + std::vector ONNXCompiled::getTensorInfo(TensorPosition pos) { GAPI_Assert(pos == INPUT || pos == OUTPUT); @@ -1348,9 +1378,9 @@ namespace { } virtual EPtr compile(const ade::Graph &graph, - const cv::GCompileArgs &, + const cv::GCompileArgs &compileArgs, const std::vector &nodes) const override { - return EPtr{new cv::gimpl::onnx::GONNXExecutable(graph, nodes)}; + return EPtr{new cv::gimpl::onnx::GONNXExecutable(graph, nodes, compileArgs)}; } virtual cv::GKernelPackage auxiliaryKernels() const override { diff --git a/modules/gapi/src/backends/onnx/gonnxbackend.hpp b/modules/gapi/src/backends/onnx/gonnxbackend.hpp index 8c67df1e1e..28f5feb3aa 100644 --- a/modules/gapi/src/backends/onnx/gonnxbackend.hpp +++ b/modules/gapi/src/backends/onnx/gonnxbackend.hpp @@ -39,7 +39,8 @@ class GONNXExecutable final: public GIslandExecutable public: GONNXExecutable(const ade::Graph &graph, - const std::vector &nodes); + const std::vector &nodes, + const cv::GCompileArgs &compileArgs); virtual inline bool canReshape() const override { return false; } virtual inline void reshape(ade::Graph&, const GCompileArgs&) override { diff --git a/modules/gapi/src/backends/ov/govbackend.cpp b/modules/gapi/src/backends/ov/govbackend.cpp index 15c216d716..99574195e2 100644 --- a/modules/gapi/src/backends/ov/govbackend.cpp +++ b/modules/gapi/src/backends/ov/govbackend.cpp @@ -1599,7 +1599,16 @@ cv::gimpl::ov::GOVExecutable::GOVExecutable(const ade::Graph &g, const cv::GCompileArgs &compileArgs, const std::vector &nodes) : m_g(g), m_gm(m_g) { + auto workload_arg = cv::gapi::getCompileArg(compileArgs); + if(workload_arg.has_value()) { +#if INF_ENGINE_RELEASE >= 2024030000 + m_workload_type = workload_arg.value(); + m_workload_listener_id = m_workload_type->addListener(std::bind(&GOVExecutable::setWorkloadType, this, std::placeholders::_1)); +#else + util::throw_error(std::logic_error("Workload type not supported in this version of OpenVINO, use >= 2024.3.0")); +#endif + } m_options.inference_only = cv::gapi::getCompileArg(compileArgs).has_value(); // FIXME: Currently this backend is capable to run a single inference node only. @@ -1636,6 +1645,25 @@ cv::gimpl::ov::GOVExecutable::GOVExecutable(const ade::Graph &g, } } +#if INF_ENGINE_RELEASE >= 2024030000 +cv::gimpl::ov::GOVExecutable::~GOVExecutable() { + if (m_workload_type) + m_workload_type->removeListener(m_workload_listener_id); +} + +void cv::gimpl::ov::GOVExecutable::setWorkloadType(const std::string &type) { + if (type == "Default") { + compiled.compiled_model.set_property({{"WORKLOAD_TYPE", ::ov::WorkloadType::DEFAULT}}); + } + else if (type == "Efficient") { + compiled.compiled_model.set_property({{"WORKLOAD_TYPE", ::ov::WorkloadType::EFFICIENT}}); + } + else { + GAPI_LOG_WARNING(NULL, "Unknown value for WORKLOAD_TYPE"); + } +} +#endif + void cv::gimpl::ov::GOVExecutable::run(cv::gimpl::GIslandExecutable::IInput &in, cv::gimpl::GIslandExecutable::IOutput &out) { std::vector input_objs; diff --git a/modules/gapi/src/backends/ov/govbackend.hpp b/modules/gapi/src/backends/ov/govbackend.hpp index ff9793afad..4c7d627a08 100644 --- a/modules/gapi/src/backends/ov/govbackend.hpp +++ b/modules/gapi/src/backends/ov/govbackend.hpp @@ -50,12 +50,18 @@ class GOVExecutable final: public GIslandExecutable // To manage additional execution options Options m_options; - +#if INF_ENGINE_RELEASE >= 2024030000 + cv::gapi::wip::ov::WorkloadTypeOVPtr m_workload_type; + uint64_t m_workload_listener_id = 0; + void setWorkloadType(const std::string &type); +#endif public: GOVExecutable(const ade::Graph &graph, const cv::GCompileArgs &compileArgs, const std::vector &nodes); - +#if INF_ENGINE_RELEASE >= 2024030000 + ~GOVExecutable(); +#endif virtual inline bool canReshape() const override { return false; } virtual inline void reshape(ade::Graph&, const GCompileArgs&) override { GAPI_Error("InternalError"); // Not implemented yet From 56d76f95ed96ca8c965b6c1519856aff5f595762 Mon Sep 17 00:00:00 2001 From: Josua Rieder <> Date: Mon, 22 Dec 2025 21:33:34 +0100 Subject: [PATCH 083/103] Remove stray STX in stitcher tutorial --- doc/tutorials/others/stitcher.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorials/others/stitcher.markdown b/doc/tutorials/others/stitcher.markdown index 5f22beacd7..8e2b29f541 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 From 2bce8e6a469eeb100b49280bae6d68bc8e62a2de Mon Sep 17 00:00:00 2001 From: Abhishek Gola Date: Tue, 23 Dec 2025 13:49:07 +0530 Subject: [PATCH 084/103] Added conv kernel size --- modules/dnn/src/onnx/onnx_importer.cpp | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 93198d66d1..8f3a85aa8e 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2014,6 +2014,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); @@ -2030,6 +2049,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."); From eaccbe24b2ccea4d17b489302b3aa231e5df514a Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 23 Dec 2025 14:25:07 +0300 Subject: [PATCH 085/103] Merge pull request #28242 from vpisarev:fix_input_array_std_vector modified Input/OutputArray methods to handle 'std::vector' or 'std::vector>' properly #28242 This is port of #26408 with some further improvements (all switch-by-vector-type statements are consolidated in a single macro) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/core/include/opencv2/core/mat.hpp | 1 + modules/core/src/matrix_wrap.cpp | 349 +++++++++++----------- modules/core/test/test_mat.cpp | 60 ++++ 3 files changed, 235 insertions(+), 175 deletions(-) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 7a260abe04..dc9d526b97 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -246,6 +246,7 @@ public: bool isContinuous(int i=-1) const; bool isSubmatrix(int i=-1) const; bool empty() const; + bool empty(int i) const; void copyTo(const _OutputArray& arr) const; void copyTo(const _OutputArray& arr, const _InputArray & mask) const; size_t offset(int i=-1) const; diff --git a/modules/core/src/matrix_wrap.cpp b/modules/core/src/matrix_wrap.cpp index a8366a5f6c..8c4a09c3ac 100644 --- a/modules/core/src/matrix_wrap.cpp +++ b/modules/core/src/matrix_wrap.cpp @@ -7,6 +7,51 @@ namespace cv { +typedef Vec Vec5i; +typedef Vec Vec7i; +typedef Vec Vec9i; +typedef Vec Vec10i; +typedef Vec Vec11i; +typedef Vec Vec12i; +typedef Vec Vec13i; +typedef Vec Vec14i; +typedef Vec Vec15i; +typedef Vec Vec16i; +typedef Vec Vec32i; +typedef Vec Vec64i; +typedef Vec Vec128i; + +#undef STD_VECTOR_SWITCH +#define STD_VECTOR_SWITCH(esz, func) \ + switch( esz ) \ + { \ + case 1: func(uchar); \ + case 2: func(Vec2b); \ + case 3: func(Vec3b); \ + case 4: func(int); \ + case 6: func(Vec3s); \ + case 8: func(Vec2i); \ + case 12: func(Vec3i); \ + case 16: func(Vec4i); \ + case 20: func(Vec5i); \ + case 24: func(Vec6i); \ + case 28: func(Vec7i); \ + case 32: func(Vec8i); \ + case 36: func(Vec9i); \ + case 40: func(Vec10i); \ + case 44: func(Vec11i); \ + case 48: func(Vec12i); \ + case 52: func(Vec13i); \ + case 56: func(Vec14i); \ + case 60: func(Vec15i); \ + case 64: func(Vec16i); \ + case 128: func(Vec32i); \ + case 256: func(Vec64i); \ + case 512: func(Vec128i); \ + default: \ + CV_Error_(cv::Error::StsBadArg, ("Vectors of vectors with element size %d are not supported. Please, modify STD_VECTOR_SWITCH() macro\n", esz)); \ + } + /*************************************************************************************************\ Input/Output Array \*************************************************************************************************/ @@ -38,15 +83,6 @@ Mat _InputArray::getMat_(int i) const return Mat(sz, flags, obj); } - if( k == STD_VECTOR ) - { - CV_Assert( i < 0 ); - int t = CV_MAT_TYPE(flags); - const std::vector& v = *(const std::vector*)obj; - - return !v.empty() ? Mat(size(), t, (void*)&v[0]) : Mat(); - } - if( k == STD_BOOL_VECTOR ) { CV_Assert( i < 0 ); @@ -65,14 +101,27 @@ Mat _InputArray::getMat_(int i) const if( k == NONE ) return Mat(); - if( k == STD_VECTOR_VECTOR ) + if( k == STD_VECTOR || k == STD_VECTOR_VECTOR ) { - int t = type(i); - const std::vector >& vv = *(const std::vector >*)obj; - CV_Assert( 0 <= i && i < (int)vv.size() ); - const std::vector& v = vv[i]; + int t = CV_MAT_TYPE(flags); + int esz = CV_ELEM_SIZE(t); - return !v.empty() ? Mat(size(i), t, (void*)&v[0]) : Mat(); + #undef GET_MAT + #define GET_MAT(T) \ + { \ + std::vector* v; \ + if (k == STD_VECTOR_VECTOR) { \ + std::vector >* vv = \ + reinterpret_cast >*>(obj); \ + CV_Assert(size_t(i) < vv->size()); \ + v = &vv->at(i); \ + } else { \ + v = reinterpret_cast*>(obj); \ + } \ + return v->empty() ? Mat() : Mat(1, int(v->size()), t, v->data()); \ + } + + STD_VECTOR_SWITCH(esz, GET_MAT) } if( k == STD_VECTOR_MAT ) @@ -192,7 +241,7 @@ void _InputArray::getMatVector(std::vector& mv) const { const std::vector& v = *(const std::vector*)obj; - size_t n = size().width, esz = CV_ELEM_SIZE(flags); + size_t n = total(-1), esz = CV_ELEM_SIZE(flags); int t = CV_MAT_DEPTH(flags), cn = CV_MAT_CN(flags); mv.resize(n); @@ -209,16 +258,25 @@ void _InputArray::getMatVector(std::vector& mv) const if( k == STD_VECTOR_VECTOR ) { - const std::vector >& vv = *(const std::vector >*)obj; - int n = (int)vv.size(); - int t = CV_MAT_TYPE(flags); - mv.resize(n); + int typ = CV_MAT_TYPE(flags); + int esz = CV_ELEM_SIZE(typ); - for( int i = 0; i < n; i++ ) - { - const std::vector& v = vv[i]; - mv[i] = Mat(size(i), t, (void*)&v[0]); - } + #undef GET_VEC_VEC_MAT_VEC + #define GET_VEC_VEC_MAT_VEC(T) \ + { \ + const std::vector >* vv = (const std::vector >*)obj; \ + size_t n = vv->size(); \ + mv.resize(n); \ + for (size_t i = 0; i < n; i++) { \ + const std::vector& vi = vv->at(i); \ + CV_Assert(vi.size() <= (size_t)INT_MAX); \ + int ni = (int)vi.size(); \ + mv[i] = ni > 0 ? Mat(1, ni, typ, (void*)&vi[0]) : Mat(); \ + } \ + } \ + break + + STD_VECTOR_SWITCH(esz, GET_VEC_VEC_MAT_VEC) return; } @@ -449,35 +507,14 @@ Size _InputArray::size(int i) const return sz; } - if( k == STD_VECTOR ) - { - CV_Assert( i < 0 ); - const std::vector& v = *(const std::vector*)obj; - const std::vector& iv = *(const std::vector*)obj; - size_t szb = v.size(), szi = iv.size(); - return szb == szi ? Size((int)szb, 1) : Size((int)(szb/CV_ELEM_SIZE(flags)), 1); - } - - if( k == STD_BOOL_VECTOR ) - { - CV_Assert( i < 0 ); - const std::vector& v = *(const std::vector*)obj; - return Size((int)v.size(), 1); - } - if( k == NONE ) return Size(); - if( k == STD_VECTOR_VECTOR ) + if( k == STD_VECTOR || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR ) { - const std::vector >& vv = *(const std::vector >*)obj; - if( i < 0 ) - return vv.empty() ? Size() : Size((int)vv.size(), 1); - CV_Assert( i < (int)vv.size() ); - const std::vector >& ivv = *(const std::vector >*)obj; - - size_t szb = vv[i].size(), szi = ivv[i].size(); - return szb == szi ? Size((int)szb, 1) : Size((int)(szb/CV_ELEM_SIZE(flags)), 1); + size_t n = total(i); + CV_Assert(n <= (size_t)INT_MAX); + return Size((int)n, 1); } if( k == STD_VECTOR_MAT ) @@ -617,6 +654,30 @@ int _InputArray::sizend(int* arrsz, int i) const return d; } +bool _InputArray::empty(int i) const +{ + _InputArray::KindFlag k = kind(); + if (i >= 0) { + if (k == STD_VECTOR_MAT) { + auto mv = reinterpret_cast*>(obj); + CV_Assert((size_t)i < mv->size()); + return mv->at(i).empty(); + } + else if (k == STD_VECTOR_UMAT) { + auto umv = reinterpret_cast*>(obj); + CV_Assert((size_t)i < umv->size()); + return umv->at(i).empty(); + } + else if (k == STD_VECTOR || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR) { + size_t n = total(i); + return n == 0; + } else { + CV_Error(Error::StsNotImplemented, ""); + } + } + return empty(); +} + bool _InputArray::sameSize(const _InputArray& arr) const { _InputArray::KindFlag k1 = kind(), k2 = arr.kind(); @@ -688,13 +749,7 @@ int _InputArray::dims(int i) const return 0; if( k == STD_VECTOR_VECTOR ) - { - const std::vector >& vv = *(const std::vector >*)obj; - if( i < 0 ) - return 1; - CV_Assert( i < (int)vv.size() ); - return 2; - } + return 1; if( k == STD_VECTOR_MAT ) { @@ -814,6 +869,32 @@ size_t _InputArray::total(int i) const } + if (k == STD_VECTOR || k == STD_VECTOR_VECTOR) + { + CV_Assert(i < 0 || k == STD_VECTOR_VECTOR); + int esz = CV_ELEM_SIZE(flags); + + #undef GET_VEC_SIZE + #define GET_VEC_SIZE(T) \ + if (k == STD_VECTOR_VECTOR) { \ + const std::vector >* vv = (const std::vector >*)obj; \ + size_t n = vv->size(); \ + if (i < 0) \ + return n; \ + CV_Assert((size_t)i < n); \ + return vv->at(i).size(); \ + } else \ + return ((const std::vector*)obj)->size() + + STD_VECTOR_SWITCH(esz, GET_VEC_SIZE) + } + + if (k == STD_BOOL_VECTOR) + { + CV_Assert(i < 0); + return ((const std::vector*)obj)->size(); + } + return size(i).area(); } @@ -923,27 +1004,15 @@ bool _InputArray::empty() const if (k == MATX) return false; - if( k == STD_VECTOR ) + if( k == STD_VECTOR || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR) { - const std::vector& v = *(const std::vector*)obj; - return v.empty(); - } - - if( k == STD_BOOL_VECTOR ) - { - const std::vector& v = *(const std::vector*)obj; - return v.empty(); + size_t n = total(-1); + return n == 0; } if( k == NONE ) return true; - if( k == STD_VECTOR_VECTOR ) - { - const std::vector >& vv = *(const std::vector >*)obj; - return vv.empty(); - } - if( k == STD_VECTOR_MAT ) { const std::vector& vv = *(const std::vector*)obj; @@ -952,7 +1021,7 @@ bool _InputArray::empty() const if( k == STD_ARRAY_MAT ) { - return sz.height == 0; + return sz.area() == 0; } if( k == STD_VECTOR_UMAT ) @@ -1471,8 +1540,8 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i, else { CV_Check(requested_size, - (requested_size == sz || (requested_size.height == sz.width && requested_size.width == sz.height)), - ""); + (requested_size == sz || (requested_size.height == sz.width && requested_size.width == sz.height)), + ""); } } return; @@ -1480,104 +1549,40 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i, if( k == STD_VECTOR || k == STD_VECTOR_VECTOR ) { - CV_Assert( d == 2 && (sizes[0] == 1 || sizes[1] == 1 || sizes[0]*sizes[1] == 0) ); + CV_Assert(!fixedSize()); + CV_Assert(k == STD_VECTOR_VECTOR || i < 0); + CV_Assert(d == 2 && (sizes[0] == 1 || sizes[1] == 1 || sizes[0]*sizes[1] == 0)); size_t len = sizes[0]*sizes[1] > 0 ? sizes[0] + sizes[1] - 1 : 0; - std::vector* v = (std::vector*)obj; + int esz = CV_ELEM_SIZE(flags); - if( k == STD_VECTOR_VECTOR ) + if( k == STD_VECTOR || (k == STD_VECTOR_VECTOR && i >= 0) ) { - std::vector >& vv = *(std::vector >*)obj; - if( i < 0 ) - { - CV_Assert(!fixedSize() || len == vv.size()); - vv.resize(len); - return; - } - CV_Assert( i < (int)vv.size() ); - v = &vv[i]; + int type0 = CV_MAT_TYPE(flags); + CV_Assert( mtype == type0 || (CV_MAT_CN(mtype) == CV_MAT_CN(type0) && ((1 << type0) & fixedDepthMask) != 0) ); } - else - CV_Assert( i < 0 ); - int type0 = CV_MAT_TYPE(flags); - CV_Assert( mtype == type0 || (CV_MAT_CN(mtype) == CV_MAT_CN(type0) && ((1 << type0) & fixedDepthMask) != 0) ); + #undef RESIZE_VEC + #define RESIZE_VEC(T) \ + { \ + std::vector* v; \ + if (k == STD_VECTOR_VECTOR) { \ + std::vector >* vv = reinterpret_cast >*>(obj); \ + if (i < 0) { \ + CV_Assert(!fixedSize() || len == vv->size()); \ + vv->resize(len); \ + return; \ + } \ + CV_Assert(size_t(i) < vv->size()); \ + v = &vv->at(i); \ + } else { \ + v = reinterpret_cast*>(obj); \ + } \ + CV_Assert(!fixedSize() || len == v->size()); \ + v->resize(len); \ + } \ + break - int esz = CV_ELEM_SIZE(type0); - CV_Assert(!fixedSize() || len == ((std::vector*)v)->size() / esz); - switch( esz ) - { - case 1: - ((std::vector*)v)->resize(len); - break; - case 2: - ((std::vector*)v)->resize(len); - break; - case 3: - ((std::vector*)v)->resize(len); - break; - case 4: - ((std::vector*)v)->resize(len); - break; - case 6: - ((std::vector*)v)->resize(len); - break; - case 8: - ((std::vector*)v)->resize(len); - break; - case 12: - ((std::vector*)v)->resize(len); - break; - case 16: - ((std::vector*)v)->resize(len); - break; - case 20: - ((std::vector >*)v)->resize(len); - break; - case 24: - ((std::vector*)v)->resize(len); - break; - case 28: - ((std::vector >*)v)->resize(len); - break; - case 32: - ((std::vector*)v)->resize(len); - break; - case 36: - ((std::vector >*)v)->resize(len); - break; - case 40: - ((std::vector >*)v)->resize(len); - break; - case 44: - ((std::vector >*)v)->resize(len); - break; - case 48: - ((std::vector >*)v)->resize(len); - break; - case 52: - ((std::vector >*)v)->resize(len); - break; - case 56: - ((std::vector >*)v)->resize(len); - break; - case 60: - ((std::vector >*)v)->resize(len); - break; - case 64: - ((std::vector >*)v)->resize(len); - break; - case 128: - ((std::vector >*)v)->resize(len); - break; - case 256: - ((std::vector >*)v)->resize(len); - break; - case 512: - ((std::vector >*)v)->resize(len); - break; - default: - CV_Error_(cv::Error::StsBadArg, ("Vectors with element size %d are not supported. Please, modify OutputArray::create()\n", esz)); - } + STD_VECTOR_SWITCH(esz, RESIZE_VEC) return; } @@ -1851,15 +1856,9 @@ void _OutputArray::release() const if( k == NONE ) return; - if( k == STD_VECTOR ) + if( k == STD_VECTOR || k == STD_VECTOR_VECTOR ) { - create(Size(), CV_MAT_TYPE(flags)); - return; - } - - if( k == STD_VECTOR_VECTOR ) - { - ((std::vector >*)obj)->clear(); + create(Size(), CV_MAT_TYPE(flags), -1); return; } diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 20f3ce6809..b0fcea60c6 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2778,4 +2778,64 @@ TEST(Mat, copyAt_regression27298) } } +template static void make_vector(std::vector >& v, int n) +{ + v.clear(); + v.resize(n); + _Tp* data = &v[0][0]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < cn; j++) { + int k = j % 4; + int val = (k == 0 ? 1 : k == 1 ? -1 : k == 2 ? (i+1) : -(i+1))*(i+1); + data[i*cn + j] = (_Tp)val; + } + } +} + +TEST(Core_InputOutputArray, std_vector_vector) +{ + std::vector vv0_s, vv1_s; + std::vector > cn_s; + make_vector(vv0_s, 100); + + split(vv0_s, cn_s); + merge(cn_s, vv1_s); + + double err0 = cvtest::norm(vv0_s, vv1_s, NORM_INF); + EXPECT_EQ(0, err0); + + _InputArray iarr_s(cn_s); + _OutputArray oarr_s(cn_s); + EXPECT_EQ(3u, iarr_s.total(-1)); + size_t newsize_s = vv0_s.size()*2; + oarr_s.create(Size((int)newsize_s, 1), CV_16S, 2); + EXPECT_EQ(newsize_s, cn_s[2].size()); + cn_s[1].clear(); + EXPECT_EQ(true, oarr_s.empty(1)); + + std::vector vv0_d, vv1_d; + std::vector > cn_d; + make_vector(vv0_d, 1000); + + split(vv0_d, cn_d); + merge(cn_d, vv1_d); + + double err1 = cvtest::norm(vv0_d, vv1_d, NORM_INF); + EXPECT_EQ(0., err1); + + _InputArray iarr_d(cn_d); + _OutputArray oarr_d(cn_d); + EXPECT_EQ(4u, iarr_d.total(-1)); + size_t newsize_d = vv0_d.size()*3; + oarr_d.create(Size((int)newsize_d, 1), CV_64F, 3); + EXPECT_EQ(newsize_d, cn_d[3].size()); + cn_d[1].clear(); + EXPECT_EQ(true, oarr_d.empty(1)); + Mat m2 = oarr_d.getMat(2); + + double err2 = cvtest::norm(m2, Mat(cn_d[2]).t(), NORM_INF); + EXPECT_EQ(m2.ptr(), &cn_d[2][0]); + EXPECT_EQ(0., err2); +} + }} // namespace From 7e66c0504a35bd4fe5483011b52a5545610489b4 Mon Sep 17 00:00:00 2001 From: Shruti Arsode <131191265+Shruti192903@users.noreply.github.com> Date: Tue, 23 Dec 2025 17:33:57 +0530 Subject: [PATCH 086/103] Merge pull request #28275 from Shruti192903:docs/simpleblobdetector-api docs(features2d): document getBlobContours and collectContours in SimpleBlobDetector #28275 Description: This PR adds missing Doxygen documentation for the getBlobContours() method and the collectContours parameter in SimpleBlobDetector. These features were previously undocumented, making the contour collection functionality difficult for users to discover and use correctly. Changes: Added a @brief and detailed note for SimpleBlobDetector::Params::collectContours. Added documentation for SimpleBlobDetector::getBlobContours(), including a @note regarding the required parameter setup. Testing: Verified the documentation build locally on macOS using ninja opencv_docs. Confirmed the generated HTML displays the descriptions and cross-references accurately. Partially fixes: #25904 Related PR that adds method: https://github.com/opencv/opencv/pull/21942 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work. - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/features2d/include/opencv2/features2d.hpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index b4c4dde712..b09fc3ac04 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -769,6 +769,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 ); @@ -782,10 +787,14 @@ 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; }; - /** @brief Class implementing the KAZE keypoint detector and descriptor extractor, described in @cite ABD12 . @note AKAZE descriptor can only be used with KAZE or AKAZE keypoints .. [ABD12] KAZE Features. Pablo From 54fc9dce0ba6cfb3c1588e180487c26bf5bbd7c9 Mon Sep 17 00:00:00 2001 From: Adrian Kretz Date: Tue, 23 Dec 2025 15:54:37 +0100 Subject: [PATCH 087/103] Guarantee temporary object lifetime extension --- modules/objdetect/src/aruco/charuco_detector.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/objdetect/src/aruco/charuco_detector.cpp b/modules/objdetect/src/aruco/charuco_detector.cpp index 37aca01b36..87cab866e0 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); } From a1b682254e2641cb8d2237d881b7cc70d7758322 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 23 Dec 2025 18:25:26 +0300 Subject: [PATCH 088/103] Warning fix on Windows. --- modules/objdetect/src/qrcode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index d52b818287..9db0e7e7fd 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -2217,11 +2217,11 @@ bool QRDecode::straightenQRCodeInParts() vector perspective_points; - perspective_points.emplace_back(0.0, start_cut); + perspective_points.emplace_back(0.0f, start_cut); perspective_points.emplace_back(perspective_curved_size, start_cut); perspective_points.emplace_back(perspective_curved_size, start_cut + dist); - perspective_points.emplace_back(0.0, start_cut+dist); + perspective_points.emplace_back(0.0f, start_cut+dist); perspective_points.emplace_back(perspective_curved_size * 0.5f, start_cut + dist * 0.5f); From e63d2a12f00c84cf9fc94969950c64543a759ac0 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 23 Dec 2025 18:31:50 +0300 Subject: [PATCH 089/103] pre: OpenCV 4.13.0 (version++). --- .../cross_referencing/tutorial_cross_referencing.markdown | 4 ++-- modules/core/include/opencv2/core/version.hpp | 2 +- modules/dnn/include/opencv2/dnn/version.hpp | 2 +- modules/python/package/setup.py | 3 ++- platforms/maven/opencv-it/pom.xml | 2 +- platforms/maven/opencv/pom.xml | 2 +- platforms/maven/pom.xml | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown b/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown index 009571c895..1f799532eb 100644 --- a/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown +++ b/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown @@ -46,14 +46,14 @@ Open your Doxyfile using your favorite text editor and search for the key `TAGFILES`. Change it as follows: @code -TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.12.0 +TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.13.0 @endcode If you had other definitions already, you can append the line using a `\`: @code TAGFILES = ./docs/doxygen-tags/libstdc++.tag=https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen \ - ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.12.0 + ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.13.0 @endcode Doxygen can now use the information from the tag file to link to the OpenCV diff --git a/modules/core/include/opencv2/core/version.hpp b/modules/core/include/opencv2/core/version.hpp index 690a61e337..4c897b0608 100644 --- a/modules/core/include/opencv2/core/version.hpp +++ b/modules/core/include/opencv2/core/version.hpp @@ -8,7 +8,7 @@ #define CV_VERSION_MAJOR 4 #define CV_VERSION_MINOR 13 #define CV_VERSION_REVISION 0 -#define CV_VERSION_STATUS "-dev" +#define CV_VERSION_STATUS "-pre" #define CVAUX_STR_EXP(__A) #__A #define CVAUX_STR(__A) CVAUX_STR_EXP(__A) diff --git a/modules/dnn/include/opencv2/dnn/version.hpp b/modules/dnn/include/opencv2/dnn/version.hpp index 8aa3177deb..95bb2f03d1 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(dnn4_v, OPENCV_DNN_API_VERSION) diff --git a/modules/python/package/setup.py b/modules/python/package/setup.py index 72559bde96..f693208126 100644 --- a/modules/python/package/setup.py +++ b/modules/python/package/setup.py @@ -19,7 +19,7 @@ def main(): os.chdir(SCRIPT_DIR) package_name = 'opencv' - package_version = os.environ.get('OPENCV_VERSION', '4.12.0') # TODO + package_version = os.environ.get('OPENCV_VERSION', '4.13.0') # TODO long_description = 'Open Source Computer Vision Library Python bindings' # TODO @@ -68,6 +68,7 @@ def main(): "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/platforms/maven/opencv-it/pom.xml b/platforms/maven/opencv-it/pom.xml index eb44e021d6..4682c95440 100644 --- a/platforms/maven/opencv-it/pom.xml +++ b/platforms/maven/opencv-it/pom.xml @@ -4,7 +4,7 @@ org.opencv opencv-parent - 4.12.0 + 4.13.0 org.opencv opencv-it diff --git a/platforms/maven/opencv/pom.xml b/platforms/maven/opencv/pom.xml index 5961e4d69f..63069e52a1 100644 --- a/platforms/maven/opencv/pom.xml +++ b/platforms/maven/opencv/pom.xml @@ -4,7 +4,7 @@ org.opencv opencv-parent - 4.12.0 + 4.13.0 org.opencv opencv diff --git a/platforms/maven/pom.xml b/platforms/maven/pom.xml index 4853b32bf0..a49466f866 100644 --- a/platforms/maven/pom.xml +++ b/platforms/maven/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.opencv opencv-parent - 4.12.0 + 4.13.0 pom OpenCV Parent POM From d3c539bf7173b4d92a8b2869b11bbbdd63e644a6 Mon Sep 17 00:00:00 2001 From: Dheeraj Alamuri Date: Tue, 23 Dec 2025 23:23:43 +0530 Subject: [PATCH 090/103] Merge pull request #28227 from dheeraj25406:docs-moments-degenerate docs(imgproc): clarify cv::moments behavior for degenerate contours #28227 relates to https://github.com/opencv/opencv/issues/28222 Clarifies that for degenerate contours (single point or collinear points), cv::moments() returns m00 == 0 and centroid is undefined. Documents common workarounds such as boundingRect center or point averaging. --- modules/imgproc/include/opencv2/imgproc.hpp | 12 ++++++++++++ modules/imgproc/test/test_contours.cpp | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index e02d57875e..375c0ab9bb 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3876,6 +3876,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 ); diff --git a/modules/imgproc/test/test_contours.cpp b/modules/imgproc/test/test_contours.cpp index 5fb88dce32..9e76cfabb2 100644 --- a/modules/imgproc/test/test_contours.cpp +++ b/modules/imgproc/test/test_contours.cpp @@ -593,5 +593,20 @@ TEST(Imgproc_PointPolygonTest, regression_10222) EXPECT_GT(result, 0) << "Desired result: point is inside polygon - actual result: point is not inside polygon"; } +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. */ From 2fb95415dd46adeab7666df345d7da91b5ab3fd5 Mon Sep 17 00:00:00 2001 From: raimbekovm Date: Wed, 24 Dec 2025 22:11:28 +0600 Subject: [PATCH 091/103] Fix typos in documentation: 'algorighm' -> 'algorithm', 'necesary' -> 'necessary' --- modules/imgproc/include/opencv2/imgproc.hpp | 2 +- .../objdetect/include/opencv2/objdetect/charuco_detector.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 375c0ab9bb..4250625bb4 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -4290,7 +4290,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/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. From 17a01d687c6557ba5c3cb47805edf816871835f7 Mon Sep 17 00:00:00 2001 From: raimbekovm Date: Wed, 24 Dec 2025 22:37:09 +0600 Subject: [PATCH 092/103] docs: fix spelling errors in documentation - Fixed 'reinitalized' -> 'reinitialized' in background_segm.hpp - Fixed 'dimentions/dimentional/dimentinal' -> 'dimensions/dimensional' in mat.hpp, imgproc.hpp, gmat.hpp, recurrent_layers.cpp - Fixed 'tresholded' -> 'thresholded' in aruco_detector.cpp --- modules/core/include/opencv2/core/mat.hpp | 6 +++--- modules/dnn/src/layers/recurrent_layers.cpp | 2 +- modules/gapi/include/opencv2/gapi/gmat.hpp | 2 +- modules/gapi/include/opencv2/gapi/imgproc.hpp | 10 +++++----- modules/objdetect/src/aruco/aruco_detector.cpp | 2 +- .../video/include/opencv2/video/background_segm.hpp | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index dc9d526b97..78f7487e51 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -1366,15 +1366,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/dnn/src/layers/recurrent_layers.cpp b/modules/dnn/src/layers/recurrent_layers.cpp index 3d960012e7..020bf6b1bd 100644 --- a/modules/dnn/src/layers/recurrent_layers.cpp +++ b/modules/dnn/src/layers/recurrent_layers.cpp @@ -416,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 !!! diff --git a/modules/gapi/include/opencv2/gapi/gmat.hpp b/modules/gapi/include/opencv2/gapi/gmat.hpp index 6d6f74ff7f..69cbd094ea 100644 --- a/modules/gapi/include/opencv2/gapi/gmat.hpp +++ b/modules/gapi/include/opencv2/gapi/gmat.hpp @@ -245,7 +245,7 @@ struct GAPI_EXPORTS_W_SIMPLE GMatDesc static inline GMatDesc empty_gmat_desc() { return GMatDesc{-1,-1,{-1,-1}}; } namespace gapi { namespace detail { -/** Checks GMatDesc fields if the passed matrix is a set of n-dimentional points. +/** Checks GMatDesc fields if the passed matrix is a set of n-dimensional points. @param in GMatDesc to check. @param n expected dimensionality. @return the amount of points. In case input matrix can't be described as vector of points diff --git a/modules/gapi/include/opencv2/gapi/imgproc.hpp b/modules/gapi/include/opencv2/gapi/imgproc.hpp index 96aaa5a447..689c42c82b 100644 --- a/modules/gapi/include/opencv2/gapi/imgproc.hpp +++ b/modules/gapi/include/opencv2/gapi/imgproc.hpp @@ -217,7 +217,7 @@ namespace imgproc { GAPI_Assert (in.depth == CV_32S || in.depth == CV_32F); int amount = detail::checkVector(in, 2u); GAPI_Assert(amount != -1 && - "Input Mat can't be described as vector of 2-dimentional points"); + "Input Mat can't be described as vector of 2-dimensional points"); } return empty_gopaque_desc(); } @@ -242,7 +242,7 @@ namespace imgproc { static GOpaqueDesc outMeta(GMatDesc in,DistanceTypes,double,double,double) { int amount = detail::checkVector(in, 2u); GAPI_Assert(amount != -1 && - "Input Mat can't be described as vector of 2-dimentional points"); + "Input Mat can't be described as vector of 2-dimensional points"); return empty_gopaque_desc(); } }; @@ -276,7 +276,7 @@ namespace imgproc { static GOpaqueDesc outMeta(GMatDesc in,int,double,double,double) { int amount = detail::checkVector(in, 3u); GAPI_Assert(amount != -1 && - "Input Mat can't be described as vector of 3-dimentional points"); + "Input Mat can't be described as vector of 3-dimensional points"); return empty_gopaque_desc(); } }; @@ -1235,7 +1235,7 @@ weights \f$w_i\f$ are adjusted to be inversely proportional to \f$\rho(r_i)\f$ . @note - Function textual ID is "org.opencv.imgproc.shape.fitLine2DMat" - - In case of an N-dimentional points' set given, Mat should be 2-dimensional, have a single row + - In case of an N-dimensional points' set given, Mat should be 2-dimensional, have a single row or column if there are N channels, or have N columns if there is a single channel. @param src Input set of 2D points stored in one of possible containers: Mat, @@ -1307,7 +1307,7 @@ weights \f$w_i\f$ are adjusted to be inversely proportional to \f$\rho(r_i)\f$ . @note - Function textual ID is "org.opencv.imgproc.shape.fitLine3DMat" - - In case of an N-dimentional points' set given, Mat should be 2-dimensional, have a single row + - In case of an N-dimensional points' set given, Mat should be 2-dimensional, have a single row or column if there are N channels, or have N columns if there is a single channel. @param src Input set of 3D points stored in one of possible containers: Mat, diff --git a/modules/objdetect/src/aruco/aruco_detector.cpp b/modules/objdetect/src/aruco/aruco_detector.cpp index fefaecbfef..19343bf1c2 100644 --- a/modules/objdetect/src/aruco/aruco_detector.cpp +++ b/modules/objdetect/src/aruco/aruco_detector.cpp @@ -125,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, 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! From e16fbb392c414261771b28fa55b55a0f6723fd24 Mon Sep 17 00:00:00 2001 From: Anastasiya Pronina Date: Wed, 24 Dec 2025 22:44:54 +0000 Subject: [PATCH 093/103] [G-API] Renamed `WorkloadType::notify()` -> `WorkloadType::set()` --- modules/gapi/include/opencv2/gapi/infer/workload_type.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/gapi/include/opencv2/gapi/infer/workload_type.hpp b/modules/gapi/include/opencv2/gapi/infer/workload_type.hpp index 76482451f7..9f5c5bb512 100644 --- a/modules/gapi/include/opencv2/gapi/infer/workload_type.hpp +++ b/modules/gapi/include/opencv2/gapi/infer/workload_type.hpp @@ -30,6 +30,7 @@ public: return id == other.id; } }; + class WorkloadType { std::vector listeners; uint64_t nextId = 1; @@ -48,7 +49,7 @@ public: } } - void notify(const std::string &type) { + void set(const std::string &type) { for (const auto &listener : listeners) { listener(type); } From 229941f6a2bfe7577257b33a3b92766ba709d099 Mon Sep 17 00:00:00 2001 From: raimbekovm Date: Thu, 25 Dec 2025 11:48:31 +0600 Subject: [PATCH 094/103] docs: fix spelling errors in documentation and comments - Fixed 'arrray' -> 'array' in calib3d.hpp - Fixed 'varaible' -> 'variable' in matmul_layer.cpp - Fixed 'PreprocesingEngine' -> 'PreprocessingEngine' in onevpl sample - Fixed 'convertion/convertions' -> 'conversion/conversions' in quaternion.hpp, grfmt_tiff.cpp, nary_eltwise_layers.cpp, instance_norm_layer.cpp --- modules/calib3d/include/opencv2/calib3d.hpp | 2 +- modules/core/include/opencv2/core/quaternion.hpp | 6 +++--- modules/dnn/src/layers/instance_norm_layer.cpp | 2 +- modules/dnn/src/layers/matmul_layer.cpp | 2 +- modules/dnn/src/layers/nary_eltwise_layers.cpp | 2 +- .../onevpl_infer_with_advanced_device_selection.cpp | 8 ++++---- modules/imgcodecs/src/grfmt_tiff.cpp | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 3d79ec5b00..b66fec1e04 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -1411,7 +1411,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/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/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/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 68c76906c6..1cb32e9170 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -1036,7 +1036,7 @@ public: else if (op == OPERATION::LESS_EQUAL) node = std::make_shared(inp0, inp1); // 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/gapi/samples/onevpl_infer_with_advanced_device_selection.cpp b/modules/gapi/samples/onevpl_infer_with_advanced_device_selection.cpp index de1d233ae5..8aac8fbaab 100644 --- a/modules/gapi/samples/onevpl_infer_with_advanced_device_selection.cpp +++ b/modules/gapi/samples/onevpl_infer_with_advanced_device_selection.cpp @@ -434,7 +434,7 @@ int main(int argc, char *argv[]) { // // - you should pass such wrappers as constructor arguments for each component in pipeline: // a) use extended constructor for `onevpl::GSource` for activating predefined device & context - // b) use `cfgContextParams` method of `cv::gapi::ie::Params` to enable `PreprocesingEngine` + // b) use `cfgContextParams` method of `cv::gapi::ie::Params` to enable `PreprocessingEngine` // for predefined device & context // c) use `InferenceEngine::ParamMap` to activate remote ctx in Inference Engine for given // device & context @@ -577,14 +577,14 @@ int main(int argc, char *argv[]) { } #endif // HAVE_INF_ENGINE - // turn on VPP PreprocesingEngine if available & requested + // turn on VPP PreprocessingEngine if available & requested if (flow_settings->vpl_preproc_enable) { if (is_gpu(preproc_device)) { - // activate VPP PreprocesingEngine on GPU + // activate VPP PreprocessingEngine on GPU face_net.cfgPreprocessingParams(gpu_accel_device.value(), gpu_accel_ctx.value()); } else { - // activate VPP PreprocesingEngine on CPU + // activate VPP PreprocessingEngine on CPU face_net.cfgPreprocessingParams(cpu_accel_device, cpu_accel_ctx); } diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index 2061f14900..902e061e31 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -988,7 +988,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; From a41857f3c29ac00b28c68b42d1bedf3dd657d295 Mon Sep 17 00:00:00 2001 From: raimbekovm Date: Thu, 25 Dec 2025 15:45:08 +0600 Subject: [PATCH 095/103] docs: fix spelling errors - 'tirangle' -> 'triangle' - 'cirlce' -> 'circle' - 'gradiantSize' -> 'gradientSize' - 'unnotied' -> 'unnoticed' - 'consistensy' -> 'consistency' - 'implemention' -> 'implementation' - 'suppported/Unsuppported/suppport' -> 'supported/Unsupported/support' --- modules/calib3d/src/chessboard.hpp | 2 +- modules/core/test/test_ds.cpp | 2 +- modules/dnn/src/layers/const_layer.cpp | 4 ++-- modules/features2d/include/opencv2/features2d.hpp | 2 +- modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp | 2 +- modules/imgcodecs/test/test_exr.impl.hpp | 2 +- modules/imgproc/test/test_convhull.cpp | 2 +- modules/videoio/src/cap_v4l.cpp | 2 +- modules/videoio/test/test_video_io.cpp | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/calib3d/src/chessboard.hpp b/modules/calib3d/src/chessboard.hpp index 80519d15a5..7c9ce96802 100644 --- a/modules/calib3d/src/chessboard.hpp +++ b/modules/calib3d/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/test/test_ds.cpp b/modules/core/test/test_ds.cpp index 3dc9e81d4f..685f60f96a 100644 --- a/modules/core/test/test_ds.cpp +++ b/modules/core/test/test_ds.cpp @@ -1641,7 +1641,7 @@ int Core_GraphTest::test_graph_ops( int iters ) edge = (CvGraphEdge*)&elem_buf[0]; // assign some default weight that is easy to check for - // consistensy, 'cause an edge weight is not stored + // consistency, 'cause an edge weight is not stored // in the simple graph edge->weight = (float)(v_idx[0] + v_idx[1]); pass_data = cvtest::randInt(rng) % 2; diff --git a/modules/dnn/src/layers/const_layer.cpp b/modules/dnn/src/layers/const_layer.cpp index 2246c16320..11dd429686 100644 --- a/modules/dnn/src/layers/const_layer.cpp +++ b/modules/dnn/src/layers/const_layer.cpp @@ -102,14 +102,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/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index b09fc3ac04..567aeeefce 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -687,7 +687,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; diff --git a/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp b/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp index 1ee419aa2c..b8c7baa8f3 100644 --- a/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp +++ b/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp @@ -575,7 +575,7 @@ TEST(StatefulKernel, StateIsResetOnceOnReshapeInStreaming) run("cv/video/768x576.avi", 1); // FIXME: it should be 2, not 3 for expectedSetupsCount here. - // With current implemention both GCPUExecutable reshape() and + // With current implementation both GCPUExecutable reshape() and // handleNewStream() call setupKernelStates() run("cv/video/1920x1080.avi", 3); } diff --git a/modules/imgcodecs/test/test_exr.impl.hpp b/modules/imgcodecs/test/test_exr.impl.hpp index d439b7da44..c8aef61653 100644 --- a/modules/imgcodecs/test/test_exr.impl.hpp +++ b/modules/imgcodecs/test/test_exr.impl.hpp @@ -210,7 +210,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/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp index 2d9dab5a57..714ed75ba0 100644 --- a/modules/imgproc/test/test_convhull.cpp +++ b/modules/imgproc/test/test_convhull.cpp @@ -1053,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); 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_video_io.cpp b/modules/videoio/test/test_video_io.cpp index 0a631d8a04..9c1b5cb209 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -787,7 +787,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)"); } @@ -809,7 +809,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)"); } From 459a60927135d9aaa3d4a8bc2b46e36dbba6f7f2 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Thu, 25 Dec 2025 15:43:38 +0300 Subject: [PATCH 096/103] Merge pull request #28283 from asmorkalov:as/ffmpeg_picture_leak Fixed picture_sw object leak in ffmpeg backend with hardware codecs. #28283 Replacement for https://github.com/opencv/opencv/pull/28221 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/videoio/src/cap_ffmpeg_impl.hpp | 37 ++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 26509a1a81..afd8fd4098 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -1845,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); @@ -1905,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); @@ -1946,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); @@ -1956,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 From 2b1cf03a9dbd62141e32b7aa8a53e193b4c83d27 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 26 Dec 2025 09:19:28 +0300 Subject: [PATCH 097/103] Disable IPP with AVX512 in cv::compare because of performance regression. --- modules/core/src/arithm_ipp.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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)); } From 497e9037b45d593307f53a41458c249d2e48ff99 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 25 Dec 2025 17:43:02 +0300 Subject: [PATCH 098/103] FFmpeg binaries update for Windows. --- 3rdparty/ffmpeg/ffmpeg.cmake | 10 +++++----- modules/videoio/test/test_ffmpeg.cpp | 7 ------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/3rdparty/ffmpeg/ffmpeg.cmake b/3rdparty/ffmpeg/ffmpeg.cmake index 091197a010..a061e9d56f 100644 --- a/3rdparty/ffmpeg/ffmpeg.cmake +++ b/3rdparty/ffmpeg/ffmpeg.cmake @@ -1,8 +1,8 @@ -# Binaries branch name: ffmpeg/4.x_20250625 -# Binaries were created for OpenCV: e9f1da7e8e977a65b8bf8fe7ea8b92eef9171f19 -ocv_update(FFMPEG_BINARIES_COMMIT "ea9240e39bc0d6a69d2b1f0ba4513bdc7612a41e") -ocv_update(FFMPEG_FILE_HASH_BIN32 "2821ea672a11147a70974d760a54e9bc") -ocv_update(FFMPEG_FILE_HASH_BIN64 "e5c6936240201064b15bcecf1816e8f4") +# Binaries branch name: ffmpeg/4.x_20251226 +# Binaries were created for OpenCV: cff7581175d2abfc6aef2e4f04f482e258b5c864 +ocv_update(FFMPEG_BINARIES_COMMIT "d82ad9a54a7b42a1648a9cae8fed5c2f20ea396c") +ocv_update(FFMPEG_FILE_HASH_BIN32 "47730de2286110b0d1250ff9cf50ce56") +ocv_update(FFMPEG_FILE_HASH_BIN64 "3248b4663ffef770cdb54ec8b9d16a28") ocv_update(FFMPEG_FILE_HASH_CMAKE "8862c87496e2e8c375965e1277dee1c7") function(download_win_ffmpeg script_var) diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 6ed908ceaa..a8fdc39f8c 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -1030,10 +1030,6 @@ inline static std::string videoio_ffmpeg_mismatch_name_printer(const testing::Te INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_channel_mismatch, testing::ValuesIn(mismatch_cases), videoio_ffmpeg_mismatch_name_printer); -// PR: https://github.com/opencv/opencv/pull/27523 -// TODO: Enable the tests back on Windows after FFmpeg plugin rebuild -#ifndef _WIN32 - // related issue: https://github.com/opencv/opencv/issues/23088 TEST(ffmpeg_cap_properties, set_pos_get_msec) { @@ -1111,7 +1107,4 @@ TEST(videoio_ffmpeg, seek_with_negative_dts) } } -#endif // WIN32 - - }} // namespace From 5d5b5c14a9f432624b66a3dcf5789a5a8ed26372 Mon Sep 17 00:00:00 2001 From: 0AnshuAditya0 Date: Fri, 26 Dec 2025 13:36:17 +0530 Subject: [PATCH 099/103] Fix Qt HighGUI lifecycle issue when external QApplication exists Respect QApplication::quitOnLastWindowClosed() before calling qApp->quit(). This prevents OpenCV from unintentionally terminating externally managed Qt applications when the last HighGUI window is closed. Fixes #28291 --- modules/highgui/src/window_QT.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/highgui/src/window_QT.cpp b/modules/highgui/src/window_QT.cpp index 3746acb171..d327ad6603 100644 --- a/modules/highgui/src/window_QT.cpp +++ b/modules/highgui/src/window_QT.cpp @@ -854,14 +854,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(); + } } } } From 364f21fd245c39be12bdf07fc6dfb52ae9563c71 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Sat, 27 Dec 2025 10:54:06 +0900 Subject: [PATCH 100/103] docs(core): update Universal Intrinsics for VLA (RVV/SVE) and OpenCV 4.11+ API changes --- .../core/univ_intrin/univ_intrin.markdown | 45 +++++++++---------- .../include/opencv2/core/hal/intrin_cpp.hpp | 23 ++++++++-- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/doc/tutorials/core/univ_intrin/univ_intrin.markdown b/doc/tutorials/core/univ_intrin/univ_intrin.markdown index a80b6d4bd3..2f4c116ac7 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/modules/core/include/opencv2/core/hal/intrin_cpp.hpp b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp index 9c7922445f..756602c710 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. From 207ac36ca2901acf88dd1bfc27540c8863654bbc Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 28 Dec 2025 00:05:54 +0100 Subject: [PATCH 101/103] opencv/3rdparty/libwebp/CMakeLists.txt: "/arch:AVX" should be only for Visual Studio, "-mavx" for MinGW and possibly other compilers --- 3rdparty/libwebp/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/3rdparty/libwebp/CMakeLists.txt b/3rdparty/libwebp/CMakeLists.txt index 1787466f8d..13593526cd 100644 --- a/3rdparty/libwebp/CMakeLists.txt +++ b/3rdparty/libwebp/CMakeLists.txt @@ -24,7 +24,11 @@ endif() if(WIN32) foreach(file ${lib_srcs}) if("${file}" MATCHES "_avx2.c") - set_source_files_properties("${file}" COMPILE_FLAGS "/arch:AVX") + if(MSVC) + set_source_files_properties("${file}" COMPILE_FLAGS "/arch:AVX") + else() + set_source_files_properties("${file}" COMPILE_FLAGS "-mavx") + endif() endif() endforeach() endif() From 2addae071e28f1e11c99d273ead294facdce9650 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 29 Dec 2025 13:45:19 +0300 Subject: [PATCH 102/103] Fixed DLPack detection in Python standalone builds --- modules/python/standalone.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/python/standalone.cmake b/modules/python/standalone.cmake index 1188a02eb9..f138f7b061 100644 --- a/modules/python/standalone.cmake +++ b/modules/python/standalone.cmake @@ -29,6 +29,8 @@ if(NOT PYTHON_NUMPY_INCLUDE_DIRS) message(FATAL_ERROR "Can't find Python 'numpy' development files") endif() +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectDLPack.cmake") + status("-----------------------------------------------------------------") status(" Python:") status(" Interpreter:" "${PYTHON_EXECUTABLE} (ver ${PYTHON_VERSION_STRING})") From f9c29614117047f45012bd06c5e4c510f4ef792f Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 29 Dec 2025 15:01:07 +0300 Subject: [PATCH 103/103] Define installation layout for Python standalone builds. --- modules/python/standalone.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/python/standalone.cmake b/modules/python/standalone.cmake index f138f7b061..25a669803e 100644 --- a/modules/python/standalone.cmake +++ b/modules/python/standalone.cmake @@ -29,6 +29,7 @@ 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("-----------------------------------------------------------------")